Boost Library API Reference

Introduction to Boost Libraries
Containers
Math
Smart Pointers
Alphabetical Index

© Nokia 2006-2007

Back to top


Introduction to Boost Libraries

Overview
Libraries listed by category
Limitations

© Nokia 2006-2007

Back to top


$title

C++ Standard Template Library
Introduction to Boost C++ Libraries

Introduction to Boost C++ Libraries

Table of Contents

Overview

Overview

The Boost C++ Libraries are a collection of free peer-reviewed portable C++ source libraries, that extend the functionality of C++. It works on almost any modern operating system, including UNIX and Windows variants. Most of the libraries are licensed under the Boost Software License. The Boost license policies encourage both commercial and non-commercial use, and it is not related to the GPL or other licenses. Boost libraries rely on modern C++ features such as templates and the C++ Standard Library which is already supported in S60 (Series 60 platform).

The libraries are aimed at a wide range of C++ users and application domains. They range from general-purpose libraries like SmartPtr, to OS Abstractions like FileSystem, to libraries primarily aimed at other library developers and advanced C++ users, like MPL.

Boost makes extensive use of templates to ensure efficiency and flexibility. Boost is the source of extensive work and research into generic programming and metaprogramming in C++. The Boost C++ libraries:

  • Speed initial development
  • Result in fewer bugs
  • Cut long-term maintenance costs

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Introduction to Boost C++ Libraries

Introduction to Boost C++ Libraries

Table of Contents

Libraries listed by category

Libraries listed by category

This section lists by category the Boost C++ libraries.
Library | Description
Containers | Containers are classes that store other objects which are called elements.
Math | The math library provides the math functions such as common factor, octonions, quaternions and special functions.
Smart Pointers | Smart pointers are objects which store pointers to dynamically allocated (heap) objects.

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Introduction to Boost C++ Libraries

Introduction to Boost C++ Libraries

Table of Contents

Limitations

Limitations

No known limitations.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


Containers

Overview
API Description
Limitations
Capabilities

© Nokia 2006-2007

Back to top


$title

C++ Standard Template Library
Containers API Specification

Containers API Specification

Table of Contents

Overview

Overview

Containers are classes that store other objects which are called elements. Containers provide methods to access the elements and also manages the storage space for the elements. The lifetime of an element stored in the container cannot exceed that of the container. Containers are implemented as class templates and provide flexibility to the types supported as elements.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Containers API Specification

Containers API Specification

Table of Contents

API Description

API Description

The containers library provides the following:

Library | Description
array | STL compliant container wrapper for arrays of constant size.
dynamic_bitset | A runtime sized version of std::bitset.
graph | Generic graph components and algorithms.
multi_array | Multidimensional containers and adaptors for arrays of contiguous data.
multi_index | Containers with multiple STL-compatible access interfaces.
pointer_container | Containers for storing heap-allocated polymorphic objects to ease OO-programming.
property map | Concepts defining interfaces which map key objects to value objects.
variant | Safe, generic, stack-based discriminated union container.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Containers API Specification

Containers API Specification

Table of Contents

Limitations

Limitations

No known limitations.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Containers API Specification

Containers API Specification

Table of Contents

Required Capabilities

Required Capabilities

None

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


Math

Overview
API Description
Limitations
Capabilities

© Nokia 2006-2007

Back to top


$title

C++ Standard Template Library
Math API Specification

Math API Specification

Table of Contents

Overview

Overview

The math library provides the following functions:
  • Common Factors
  • Octonions
  • Quaternion
  • Special Functions

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Math API Specification

Math API Specification

Table of Contents

API Description

API Description

The Math library provides the following:

Math | Description
Common Factor | The class and function templates in <boost/math/common_factor.hpp>provide run-time and compile-time evaluation of the greatest common divisor (GCD) or least common multiple (LCM) of two integers.
Octonion | Octonions, like quaternions, are a relative of complex numbers.
Quaternion | Quaternions are a relative of complex numbers.
Special Functions | The Special Functions library currently provides eight templated special functions, in namespace boost. Two of these (sinc_pi and sinhc_pi) are required for implementations of quaternions and octonions.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Math API Specification

Math API Specification

Table of Contents

Limitations

Limitations

No known limitations.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Math API Specification

Math API Specification

Table of Contents

Required Capabilities

Required Capabilities

None

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


Smart Pointers

Overview
API Description
Limitations
Capabilities

© Nokia 2006-2007

Back to top


$title

C++ Standard Template Library
Smart Pointers API Specification

Smart Pointers API Specification

Table of Contents

Overview

Overview

Smart pointers are objects which store pointers to dynamically allocated (heap) objects. They behave much like built-in C++ pointers except that they automatically delete the object pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects. They can also be used to keep track of dynamically allocated objects shared by multiple owners. Conceptually, smart pointers are seen as owning the object pointed to, and thus responsible for deletion of the object when it is no longer needed.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Smart Pointers API Specification

Smart Pointers API Specification

Table of Contents

API Description

API Description

The smart pointer library provides five smart pointer class templates:

Smart Pointer | Description
scoped_ptr | Simple sole ownership of single objects. Non-copyable.
scoped_array | Simple sole ownership of arrays. Non-copyable.
shared_ptr | Object ownership shared among multiple pointers
shared_array | Array ownership shared among multiple pointers.
weak_ptr | Non-owning observers of an object owned by shared_ptr.
intrusive_ptr | Shared ownership of objects with an embedded reference count.

These templates are designed to complement the std::auto_ptr template.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Smart Pointers API Specification

Smart Pointers API Specification

Table of Contents

Limitations

Limitations

No known limitations.

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


$title

C++ Standard Template Library
Smart Pointers API Specification

Smart Pointers API Specification

Table of Contents

Required Capabilities

Required Capabilities

None

Give feedback of this article

©Nokia 2007

Back to top

This material, including documentation and any related computer programs, is protected by copyright controlled by Nokia. All rights are reserved. Copying, including reproducing, storing, adapting or translating, any or all of this material requires the prior written consent of Nokia. This material also contains confidential information, which may not be disclosed to others without the prior written consent of Nokia.

Nokia is a registered trademark of Nokia Corporation. S60 and logo is a trademark of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other company and product names mentioned herein may be trademarks or tradenames of their respective owners.


Alphabetical Index

Containers
Math
Smart Pointers

© Nokia 2006-2007

Back to top


Alphabetical Index for Containers

Index - Containers

A

array

AdjacencyGraph

associative_ptr_container

B

BidirectionalGraph

C

const_multi_array_ref

D

dynamic_bitset

E

EdgeListGraph

EgdeMutableGraph

G

graph

I

IncidenceGraph

L

LvaluePropertyMap

M

multi_array

multi_array_ref

multi_index_container

MutableGraph

MutablePropertyGraph

P

PropertyGraph

PropertyMap

pointer_container

ptr_array

ptr_deque

ptr_list

ptr_map

ptr_map_adapter

ptr_multimap_adapter

ptr_multiset

ptr_multimap

ptr_multiset_adapter

ptr_sequence_adapter

ptr_set

ptr_set_adapter

ptr_vector

R

ReadablePropertyMap

ReadWritePropertyMap

reversible_ptr_container

V

variant

VertexAndEdgeListGraph

VertexListGraph

W

WritablePropertyMap

© 2006-2007 Nokia

Top


Alphabetical Index for Math

Index - Math

G

greatest common divisor

L

least common multiple

O

octonions

Q

quarternions

S

special_functions

© 2006-2007 Nokia


Alphabetical Index for Smart Pointers

Index - Smart Pointers

I

intrusive_ptr

S

scoped_array

scoped_ptr

shared_array

shared_ptr

W

weak_ptr

© 2006-2007 Nokia


Class bad_function_call

boost::bad_function_call — An exception type thrown when an instance of a function object is empty when invoked.

Synopsis

class bad_function_call : public std::runtime_error {
public:
  // construct/copy/destruct
  bad_function_call();
};

Description

bad_function_call construct/copy/destruct

  1. bad_function_call();
    Effects: Constructs a bad_function_call exception object.
Copyright © 2001-2004 Douglas Gregor


Boost: Base-from-Member Idiom Documentation

Base-from-Member Idiom

The class template boost::base_from_member provides a workaround for a class that needs to initialize a base class with a member. The class template is in boost/utility/base_from_member.hpp which is included in boost/utility.hpp.

There is test/example code in base_from_member_test.cpp.

Rationale

Synopsis

Usage

Example

Rationale

When developing a class, sometimes a base class needs to be initialized with a member of the current class. As a naïve example:

#include <streambuf>  // for std::streambuf
#include <ostream>    // for std::ostream

class fdoutbuf
    : public std::streambuf
{
public:
    explicit fdoutbuf( int fd );
    //...
};

class fdostream
    : public std::ostream
{
protected:
    fdoutbuf buf;
public:
    explicit fdostream( int fd )
        : buf( fd ), std::ostream( &buf )
        {}
    //...
};

This is undefined because C++'s initialization order mandates that the base class is initialized before the member it uses. R. Samuel Klatchko developed a way around this by using the initialization order. Base classes are initialized in order of declaration, so moving the desired member to another base class, that is initialized before the desired base class, can ensure proper initialization.

A custom base class can be made for this idiom:

#include <streambuf>  // for std::streambuf
#include <ostream>    // for std::ostream

class fdoutbuf
    : public std::streambuf
{
public:
    explicit fdoutbuf( int fd );
    //...
};

struct fdostream_pbase
{
    fdoutbuf sbuffer;

    explicit fdostream_pbase( int fd )
        : sbuffer( fd )
        {}
};

class fdostream
    : private fdostream_pbase
    , public std::ostream
{
    typedef fdostream_pbase  pbase_type;
    typedef std::ostream     base_type;

public:
    explicit fdostream( int fd )
        : pbase_type( fd ), base_type( &sbuffer )
        {}
    //...
};

Other projects can use similar custom base classes. The technique is basic enough to make a template, with a sample template class in this library. The main template parameter is the type of the enclosed member. The template class has several (explicit) constructor member templates, which implicitly type the constructor arguments and pass them to the member. The template class uses implicit copy construction and assignment, cancelling them if the enclosed member is non-copyable.

Manually coding a base class may be better if the construction and/or copying needs are too complex for the supplied template class, or if the compiler is not advanced enough to use it.

Since base classes are unnamed, a class cannot have multiple (direct) base classes of the same type. The supplied template class has an extra template parameter, an integer, that exists solely to provide type differentiation. This parameter has a default value so a single use of a particular member type does not need to concern itself with the integer.

Synopsis

#ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY
#define BOOST_BASE_FROM_MEMBER_MAX_ARITY  10
#endif

template < typename MemberType, int UniqueID = 0 >
class boost::base_from_member
{
protected:
    MemberType  member;

    base_from_member();

    template< typename T1 >
    explicit  base_from_member( T1 x1 );

    template< typename T1, typename T2 >
    base_from_member( T1 x1, T2 x2 );

    //...

    template< typename T1, typename T2, typename T3, typename T4,
     typename T5, typename T6, typename T7, typename T8, typename T9,
     typename T10 >
    base_from_member( T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7,
     T8 x8, T9 x9, T10 x10 );
};

The class template has a first template parameter MemberType representing the type of the based-member. It has a last template parameter UniqueID, that is an int, to differentiate between multiple base classes that use the same based-member type. The last template parameter has a default value of zero if it is omitted. The class template has a protected data member called member that the derived class can use for later base classes (or itself).

There is a default constructor and several constructor member templates. These constructor templates can take as many arguments (currently up to ten) as possible and pass them to a constructor of the data member. Since C++ does not allow any way to explicitly state the template parameters of a templated constructor, make sure that the arguments are already close as possible to the actual type used in the data member's desired constructor.

The BOOST_BASE_FROM_MEMBER_MAX_ARITY macro constant specifies the maximum argument length for the constructor templates. The constant may be overridden if more (or less) argument configurations are needed. The constant may be read for code that is expandable like the class template and needs to maintain the same maximum size. (Example code would be a class that uses this class template as a base class for a member with a flexible set of constructors.)

Usage

With the starting example, the fdoutbuf sub-object needs to be encapsulated in a base class that is inheirited before std::ostream.

#include <boost/utility/base_from_member.hpp>

#include <streambuf>  // for std::streambuf
#include <ostream>    // for std::ostream

class fdoutbuf
    : public std::streambuf
{
public:
    explicit fdoutbuf( int fd );
    //...
};

class fdostream
    : private boost::base_from_member<fdoutbuf>
    , public std::ostream
{
    // Helper typedef's
    typedef boost::base_from_member<fdoutbuf>  pbase_type;
    typedef std::ostream                        base_type;

public:
    explicit fdostream( int fd )
        : pbase_type( fd ), base_type( &member )
        {}
    //...
};

The base-from-member idiom is an implementation detail, so it should not be visible to the clients (or any derived classes) of fdostream. Due to the initialization order, the fdoutbuf sub-object will get initialized before the std::ostream sub-object does, making the former sub-object safe to use in the latter sub-object's construction. Since the fdoutbuf sub-object of the final type is the only sub-object with the name "member," that name can be used unqualified within the final class.

Example

The base-from-member class templates should commonly involve only one base-from-member sub-object, usually for attaching a stream-buffer to an I/O stream. The next example demonstrates how to use multiple base-from-member sub-objects and the resulting qualification issues.

#include <boost/utility/base_from_member.hpp>

#include <cstddef>  // for NULL

struct an_int
{
    int  y;

    an_int( float yf );
};

class switcher
{
public:
    switcher();
    switcher( double, int * );
    //...
};

class flow_regulator
{
public:
    flow_regulator( switcher &, switcher & );
    //...
};

template < unsigned Size >
class fan
{
public:
    explicit fan( switcher );
    //...
};

class system
    : private boost::base_from_member<an_int>
    , private boost::base_from_member<switcher>
    , private boost::base_from_member<switcher, 1>
    , private boost::base_from_member<switcher, 2>
    , protected flow_regulator
    , public fan<6>
{
    // Helper typedef's
    typedef boost::base_from_member<an_int>       pbase0_type;
    typedef boost::base_from_member<switcher>     pbase1_type;
    typedef boost::base_from_member<switcher, 1>  pbase2_type;
    typedef boost::base_from_member<switcher, 2>  pbase3_type;

    typedef flow_regulator  base1_type;
    typedef fan<6>          base2_type;

public:
    system( double x );
    //...
};

system::system( double x )
    : pbase0_type( 0.2 )
    , pbase1_type()
    , pbase2_type( -16, &this->pbase0_type::member )
    , pbase3_type( x, static_cast<int *>(NULL) )
    , base1_type( pbase3_type::member, pbase1_type::member )
    , base2_type( pbase2_type::member )
{
    //...
}

The final class has multiple sub-objects with the name "member," so any use of that name needs qualification by a name of the appropriate base type. (Using typedefs ease mentioning the base types.) However, the fix introduces a new problem when a pointer is needed. Using the address operator with a sub-object qualified with its class's name results in a pointer-to-member (here, having a type of an_int boost::base_from_member<an_int, 0> :: *) instead of a pointer to the member (having a type of an_int *). The new problem is fixed by qualifying the sub-object with "this->," and is needed just for pointers, and not for references or values.

There are some argument conversions in the initialization. The constructor argument for pbase0_type is converted from double to float. The first constructor argument for pbase2_type is converted from int to double. The second constructor argument for pbase3_type is a special case of necessary conversion; all forms of the null-pointer literal in C++ also look like compile-time integral expressions, so C++ always interprets such code as an integer when it has overloads that can take either an integer or a pointer. The last conversion is necessary for the compiler to call a constructor form with the exact pointer type used in switcher's constructor.

Copyright 2001, 2003, 2004 Daryle Walker. Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file License.html or a copy at <http://www.boost.org/License.html>.)


Boost: bind.hpp documentation

bind.hpp

Purpose

Using bind with functions and function pointers

Using bind with function objects

Using bind with pointers to members

Using nested binds for function composition

Overloaded operators

Examples

Using bind with standard algorithms

Using bind with Boost.Function

Limitations

Frequently Asked Questions

Troubleshooting

Incorrect number of arguments

The function object cannot be called with the specified arguments

Accessing an argument that does not exist

Inappropriate use of bind(f, ...)

Inappropriate use of bind<R>(f, ...)

Binding a nonstandard function

const in signatures

MSVC specific: using boost::bind;

MSVC specific: class templates shadow function templates

MSVC specific: ... in signatures treated as type

Interface

Synopsis

Common requirements

Common definitions

bind

Additional overloads

Implementation

Files

Dependencies

Number of Arguments

"__stdcall", "__cdecl", "__fastcall", and "pascal" Support

visit_each support

Purpose

boost::bind is a generalization of the standard functions std::bind1st and std::bind2nd. It supports arbitrary function objects, functions, function pointers, and member function pointers, and is able to bind any argument to a specific value or route input arguments into arbitrary positions. bind does not place any requirements on the function object; in particular, it does not need the result_type, first_argument_type and second_argument_type standard typedefs.

Using bind with functions and function pointers

Given these definitions:

int f(int a, int b)
{
    return a + b;
}

int g(int a, int b, int c)
{
    return a + b + c;
}

bind(f, 1, 2) will produce a "nullary" function object that takes no
            arguments and returns  f(1, 2). Similarly,
          bind(g, 1, 2, 3)() is
            equivalent to   g(1, 2, 3).

It is possible to selectively bind only some of the arguments. bind(f, _1, 5)(x) is equivalent to f(x, 5); here _1 is a placeholder argument that means "substitute with the first input argument."

For comparison, here is the same operation expressed with the standard library primitives:

std::bind2nd(std::ptr_fun(f), 5)(x);

bind covers the functionality of std::bind1st as well:

std::bind1st(std::ptr_fun(f), 5)(x);   // f(5, x)
bind(f, 5, _1)(x);                     // f(5, x)

bind can handle functions with more than two arguments, and its argument substitution mechanism is more general:

bind(f, _2, _1)(x, y);                 // f(y, x)

bind(g, _1, 9, _1)(x);                 // g(x, 9, x)

bind(g, _3, _3, _3)(x, y, z);          // g(z, z, z)

bind(g, _1, _1, _1)(x, y, z);          // g(x, x, x)

Note that, in the last example, the function object produced by bind(g, _1, _1, _1) does not contain references to any arguments beyond the first, but it can still be used with more than one argument. Any extra arguments are silently ignored, just like the first and the second argument are ignored in the third example.

The arguments that bind takes are copied and held internally by the returned function object. For example, in the following code:

int i = 5;

bind(f, i, _1);

a copy of the value of i is stored into the function object. boost::ref and boost::cref can be used to make the function object store a reference to an object, rather than a copy:

int i = 5;

bind(f, ref(i), _1);

bind(f, cref(42), _1);

Using bind with function objects

bind is not limited to functions; it accepts arbitrary function objects. In the general case, the return type of the generated function object's operator() has to be specified explicitly (without a typeof operator the return type cannot be inferred):

struct F
{
    int operator()(int a, int b) { return a - b; }
    bool operator()(long a, long b) { return a == b; }
};

F f;

int x = 104;

bind<int>(f, _1, _1)(x);		// f(x, x), i.e. zero

Some compilers have trouble with the bind<R>(f, ...) syntax. For portability reasons, an alternative way to express the above is supported:

boost::bind(boost::type<int>(), f, _1, _1)(x);

Note, however, that the alternative syntax is provided only as a workaround. It is not part of the interface.

When the function object exposes a nested type named result_type, the explicit return type can be omitted:

int x = 8;

bind(std::less<int>(), _1, 9)(x);	// x < 9

[Note: the ability to omit the return type is not available on all compilers.]

Using bind with pointers to members

Pointers to member functions and pointers to data members are not function objects, because they do not support operator(). For convenience, bind accepts member pointers as its first argument, and the behavior is as if boost::mem_fn has been used to convert the member pointer into a function object. In other words, the expression

bind(&X::f, args)

is equivalent to

bind<R>(mem_fn(&X::f), args)

where R is the return type of X::f (for member functions) or the type of the member (for data members.)

[Note: mem_fn creates function objects that are able to accept a pointer, a reference, or a smart pointer to an object as its first argument; for additional information, see the mem_fn documentation.]

Example:

struct X
{
    bool f(int a);
};

X x;

shared_ptr<X> p(new X);

int i = 5;

bind(&X::f, ref(x), _1)(i);		// x.f(i)
bind(&X::f, &x, _1)(i);			//(&x)->f(i)
bind(&X::f, x, _1)(i);			// (internal copy of x).f(i)
bind(&X::f, p, _1)(i);			// (internal copy of p)->f(i)

The last two examples are interesting in that they produce "self-contained" function objects. bind(&X::f, x, _1) stores a copy of x. bind(&X::f, p, _1) stores a copy of p, and since p is a boost::shared_ptr, the function object retains a reference to its instance of X and will remain valid even when p goes out of scope or is reset().

Using nested binds for function composition

Some of the arguments passed to bind may be nested bind expressions themselves:

bind(f, bind(g, _1))(x);               // f(g(x))

The inner bind expressions are evaluated, in unspecified order, before the outer bind when the function object is called; the results of the evaluation are then substituted in their place when the outer bind is evaluated. In the example above, when the function object is called with the argument list (x), bind(g, _1)(x) is evaluated first, yielding g(x), and then bind(f, g(x))(x) is evaluated, yielding the final result f(g(x)).

This feature of bind can be used to perform function composition. See bind_as_compose.cpp for an example that demonstrates how to use bind to achieve similar functionality to Boost.Compose (This library was deprecated in favor of the Bind or Lambda libraries and has been removed from Boost).

Note: The first argument - the bound function object - is not evaluated, even when it is a function object that is produced by bind or a placeholder argument, so the example below does not work as expected:

typedef void (*pf)(int);

std::vector<pf> v;

std::for_each(v.begin(), v.end(), bind(_1, 5));

The desired effect can be achieved via a helper function object apply that applies its first argument, as a function object, to the rest of its argument list. For convenience, an implementation of apply is provided in the boost/bind/apply.hpp header file. The code snippet below shows the modified version of the previous example:

typedef void (*pf)(int);

std::vector<pf> v;

std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5));

Although the first argument is, by default, not evaluated, all other arguments are. Sometimes it is necessary not to evaluate arguments subsequent to the first, even when they are nested bind subexpressions. This can be achieved with the help of another function object, protect, that masks the type so that bind does not recognize and evaluate it. When called, protect simply forwards the argument list to the other function object unmodified.

The header boost/bind/protect.hpp contains an implementation of protect. To protect a bind function object from evaluation, use
protect(bind(f, ...)).

Overloaded operators (new in Boost 1.33)

For convenience, the function objects produced by bind overload the logical not operator ! and the relational operators ==, !=, <, <=, >, >=.

!bind(f, ...) is equivalent to bind( logical_not(), bind(f, ...) ), where logical_not is a function object that takes one argument x and returns !x.

bind(f, ...) op x, where op is a relational operator, is equivalent to bind( relation(), bind(f, ...), x ), where relation is a function object that takes two arguments a and b and returns a op b.

What this means in practice is that you can conveniently negate the result of bind:

std::remove_if( first, last, !bind( &X::visible, _1 ) ); // remove invisible objects

and compare the result of bind against a value:

std::find_if( first, last, bind( &X::name, _1 ) == "peter" );

against a placeholder:

bind( &X::name, _1 ) == _2

or against another bind expression:

std::sort( first, last, bind( &X::name, _1 ) < bind( &X::name, _2 ) ); // sort by name

Examples

Using bind with standard algorithms

class image;

class animation
{
public:

    void advance(int ms);
    bool inactive() const;
    void render(image & target) const;
};

std::vector<animation> anims;

template<class C, class P> void erase_if(C & c, P pred)
{
    c.erase(std::remove_if(c.begin(), c.end(), pred), c.end());
}

void update(int ms)
{
    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::advance, _1, ms));
    erase_if(anims, boost::mem_fn(&animation::inactive));
}

void render(image & target)
{
    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::render, _1, boost::ref(target)));
}

Using bind with Boost.Function

class button
{
public:

    boost::function<void> onClick;
};

class player
{
public:

    void play();
    void stop();
};

button playButton, stopButton;
player thePlayer;

void connect()
{
    playButton.onClick = boost::bind(&player::play, &thePlayer);
    stopButton.onClick = boost::bind(&player::stop, &thePlayer);
}

Limitations

The function objects generated by bind take their arguments by reference and cannot, therefore, accept non-const temporaries or literal constants. This is an inherent limitation of the C++ language, known as the forwarding problem.

The library uses signatures of the form

template<class T> void f(T & t);

to accept arguments of arbitrary types and pass them on unmodified. As noted, this does not work with non-const r-values.

An oft-proposed "solution" to this problem is to add an overload:

template<class T> void f(T & t);
template<class T> void f(T const & t);

Unfortunately, this requires providing 512 overloads for nine arguments and does not actually work for const arguments, both l- and r-values, since the two templates produce the exact same signature and cannot be partially ordered.

[Note: this is a dark corner of the language, and the corresponding issue has only recently been resolved.]

Frequently Asked Questions

Why doesn't this compile?

See the dedicated Troubleshooting section.

Why does this compile? It should not.

Probably because the general bind<R>(f, ...) syntax was used, thereby instructing bind to not "inspect" f to detect arity and return type errors.

What is the difference between bind(f, ...) and bind<R>(f, ...)?

The first form instructs bind to inspect the type of f in order to determine its arity (number of arguments) and return type. Arity errors will be detected at "bind time". This syntax, of course, places some requirements on f. It must be a function, function pointer, member function pointer, or a function object that defines a nested type named result_type; in short, it must be something that bind can recognize.

The second form instructs bind to not attempt to recognize the type of f. It is generally used with function objects that do not, or cannot, expose result_type, but it can also be used with nonstandard functions. For example, the current implementation does not automatically recognize variable-argument functions like printf, so use bind<int>(printf, ...).

Note: An alternative bind(type<R>(), f, ...) syntax is supported for portability reasons.

Another important factor to consider is that compilers without partial template specialization or function template partial ordering support cannot handle the first form when f is a function object, and in most cases will not handle the second form when f is a function (pointer) or a member function pointer.

Does bind work with Windows API functions?

Yes, if the user #define BOOST_BIND_ENABLE_STDCALL. An alternative is to treat the function as a generic function object and use the bind<R>(f, ...) syntax.

Does bind work with COM methods?

Yes, if the user #define BOOST_MEM_FN_ENABLE_STDCALL.

Does bind work with Mac toolbox functions?

Yes, if the user #define BOOST_BIND_ENABLE_PASCAL. An alternative is to treat the function as a generic function object and use the bind<R>(f, ...) syntax.

Does bind work with extern "C" functions?

Sometimes. On some platforms, pointers to extern "C" functions are equivalent to "ordinary" function pointers, so they work fine. Other platforms treat them as different types. A platform-specific implementation of bind is expected to handle the problem transparently; this implementation does not. As usual, the workaround is to treat the function as a generic function object and use the bind<R>(f, ...) syntax.

Why doesn't bind automatically recognize nonstandard functions?

Non-portable extensions, in general, should default to off to prevent vendor lock-in. If the appropriate macros are defined automatically, the user could have accidentally taken advantage of them without realizing that the code is, perhaps, no longer portable. In addition, some compilers have the option to make __stdcall (__fastcall) their default calling convention, in which case no separate support would be necessary.

Troubleshooting

Incorrect number of arguments

In a bind(f, a1, a2, ..., aN) expression, the function object f must be able to take exactly N arguments. This error is normally detected at "bind time"; in other words, the compilation error is reported on the line where bind() is invoked:

int f(int, int);

int main()
{
    boost::bind(f, 1);    // error, f takes two arguments
    boost::bind(f, 1, 2); // OK
}

A common variation of this error is to forget that member functions have an implicit "this" argument:

struct X
{
    int f(int);
}

int main()
{
    boost::bind(&X::f, 1);     // error, X::f takes two arguments
    boost::bind(&X::f, _1, 1); // OK
}

The function object cannot be called with the specified arguments

As in normal function calls, the function object that is bound must be compatible with the argument list. The incompatibility will usually be detected by the compiler at "call time" and the result is typically an error in bind.hpp on a line that looks like:

    return f(a[a1_], a[a2_]);

An example of this kind of error:

int f(int);

int main()
{
    boost::bind(f, "incompatible");      // OK so far, no call
    boost::bind(f, "incompatible")();    // error, "incompatible" is not an int
    boost::bind(f, _1);                  // OK
    boost::bind(f, _1)("incompatible");  // error, "incompatible" is not an int
}

Accessing an argument that does not exist

The placeholder _N selects the argument at position N from the argument list passed at "call time." Naturally, it is an error to attempt to access beyond the end of this list:

int f(int);

int main()
{
    boost::bind(f, _1);                  // OK
    boost::bind(f, _1)();                // error, there is no argument number 1
}

The error is usually reported in bind.hpp, at a line similar to:

    return f(a[a1_]);

When emulating std::bind1st(f, a), a common mistake of this category is to type bind(f, a, _2) instead of the correct bind(f, a, _1).

Inappropriate use of bind(f, ...)

The bind(f, a1, a2, ..., aN) form causes automatic recognition of the type of f. It will not work with arbitrary function objects; f must be a function or a member function pointer.

It is possible to use this form with function objects that define result_type, but only on compilers that support partial specialization and partial ordering. In particular, MSVC up to version 7.0 does not support this syntax for function objects.

Inappropriate use of bind<R>(f, ...)

The bind<R>(f, a1, a2, ..., aN) form supports arbitrary function objects.

It is possible (but not recommended) to use this form with functions or member function pointers, but only on compilers that support partial ordering. In particular, MSVC up to version 7.0 does not fully support this syntax for functions and member function pointers.

Binding a nonstandard function

By default, the bind(f, a1, a2, ..., aN) form recognizes "ordinary" C++ functions and function pointers. Functions that use a different calling convention, or variable-argument functions such as std::printf, do not work. The general bind<R>(f, a1, a2, ..., aN) form works with nonstandard functions.

On some platforms, extern "C" functions, like std::strcmp, are not recognized by the short form of bind.

See also " __stdcall" and "pascal" Support .

const in signatures

Some compilers, including MSVC 6.0 and Borland C++ 5.5.1, have problems with the top-level const in function signatures:

int f(int const);

int main()
{
    boost::bind(f, 1);     // error
}

Workaround: remove the const qualifier from the argument.

MSVC specific: using boost::bind;

On MSVC (up to version 7.0), when boost::bind is brought into scope with an using declaration:

using boost::bind;

the syntax bind<R>(f, ...) does not work. Workaround: either use the qualified name, boost::bind, or use an using directive instead:

using namespace boost;

MSVC specific: class templates shadow function templates

On MSVC (up to version 7.0), a nested class template named bind will shadow the function template boost::bind, breaking the bind<R>(f, ...) syntax. Unfortunately, some libraries contain nested class templates named bind (ironically, such code is often an MSVC specific workaround.)

The workaround is to use the alternative bind(type<R>(), f, ...) syntax.

MSVC specific: ... in signatures treated as type

MSVC (up to version 7.0) treats the ellipsis in a variable argument function (such as std::printf) as a type. Therefore, it will accept the (incorrect in the current implementation) form:

    bind(printf, "%s\n", _1);

and will reject the correct version:

    bind<int>(printf, "%s\n", _1);

Interface

Synopsis

namespace boost
{

// no arguments

template<class R, class F> unspecified-1 bind(F f);

template<class F> unspecified-1-1 bind(F f);

template<class R> unspecified-2 bind(R (*f) ());

// one argument

template<class R, class F, class A1> unspecified-3 bind(F f, A1 a1);

template<class F, class A1> unspecified-3-1 bind(F f, A1 a1);

template<class R, class B1, class A1> unspecified-4 bind(R (*f) (B1), A1 a1);

template<class R, class T, class A1> unspecified-5 bind(R (T::*f) (), A1 a1);

template<class R, class T, class A1> unspecified-6 bind(R (T::*f) () const, A1 a1);

template<class R, class T, class A1> unspecified-6-1 bind(R T::*f, A1 a1);

// two arguments

template<class R, class F, class A1, class A2> unspecified-7 bind(F f, A1 a1, A2 a2);

template<class F, class A1, class A2> unspecified-7-1 bind(F f, A1 a1, A2 a2);

template<class R, class B1, class B2, class A1, class A2> unspecified-8 bind(R (*f) (B1, B2), A1 a1, A2 a2);

template<class R, class T, class B1, class A1, class A2> unspecified-9 bind(R (T::*f) (B1), A1 a1, A2 a2);

template<class R, class T, class B1, class A1, class A2> unspecified-10 bind(R (T::*f) (B1) const, A1 a1, A2 a2);

// implementation defined number of additional overloads for more arguments

}

namespace
{

unspecified-placeholder-type-1 _1;

unspecified-placeholder-type-2 _2;

unspecified-placeholder-type-3 _3;

// implementation defined number of additional placeholder definitions

}

Common requirements

All unspecified-N types returned by bind are CopyConstructible. unspecified-N::result_type is defined as the return type of unspecified-N::operator().

All unspecified-placeholder-N types are CopyConstructible. Their copy constructors do not throw exceptions.

Common definitions

The function µ(x, v1, v2, ..., vm), where m is a nonnegative integer, is defined as:

  • x.get(), when x is of type boost::reference_wrapper<T> for some type T;

  • vk, when x is (a copy of) the placeholder _k for some positive integer k;

  • x(v1, v2, ..., vm) when x is (a copy of) a function object returned by bind;

  • x otherwise.

bind

template<class R, class F> unspecified-1 bind(F f)

Returns: A function object λ such that the expression λ(v1, v2, ..., vm) is equivalent to f(), implicitly converted to R.

Throws: Nothing unless the copy constructor of F throws an exception.

template<class F> unspecified-1-1 bind(F f)

Effects: Equivalent to bind<typename F::result_type, F>(f);

Notes: Implementations are allowed to infer the return type of f via other means as an extension, without relying on the result_type member.

template<class R> unspecified-2 bind(R (*f) ())

Returns: A function object λ such that the expression λ(v1, v2, ..., vm) is equivalent to f().

Throws: Nothing.

template<class R, class F, class A1> unspecified-3 bind(F f, A1 a1)

Returns: A function object λ such that the expression λ(v1, v2, ..., vm) is equivalent to f(µ(a1, v1, v2, ..., vm)), implicitly converted to R.

Throws: Nothing unless the copy constructors of F or A1 throw an exception.

template<class F, class A1> unspecified-3-1 bind(F f, A1 a1)

Effects: Equivalent to bind<typename F::result_type, F, A1>(f, a1);

Notes: Implementations are allowed to infer the return type of f via other means as an extension, without relying on the result_type member.

template<class R, class B1, class A1> unspecified-4 bind(R (*f) (B1), A1 a1)

Returns: A function object λ such that the expression λ(v1, v2, ..., vm) is equivalent to f(µ(a1, v1, v2, ..., vm)).

Throws: Nothing unless the copy constructor of A1 throws an exception.

template<class R, class T, class A1> unspecified-5 bind(R (T::*f) (), A1 a1)

Effects: Equivalent to bind<R>(boost::mem_fn(f), a1);

template<class R, class T, class A1> unspecified-6 bind(R (T::*f) () const, A1 a1)

Effects: Equivalent to bind<R>(boost::mem_fn(f), a1);

template<class R, class T, class A1> unspecified-6-1 bind(R T::*f, A1 a1)

Effects: Equivalent to bind<R>(boost::mem_fn(f), a1);

template<class R, class F, class A1, class A2> unspecified-7 bind(F f, A1 a1, A2 a2)

Returns: A function object λ such that the expression λ(v1, v2, ..., vm) is equivalent to f(µ(a1, v1, v2, ..., vm), µ(a2, v1, v2, ..., vm)), implicitly converted to R.

Throws: Nothing unless the copy constructors of F, A1 or A2 throw an exception.

template<class F, class A1, class A2> unspecified-7-1 bind(F f, A1 a1, A2 a2)

Effects: Equivalent to bind<typename F::result_type, F, A1, A2>(f, a1, a2);

Notes: Implementations are allowed to infer the return type of f via other means as an extension, without relying on the result_type member.

template<class R, class B1, class B2, class A1, class A2> unspecified-8 bind(R (*f) (B1, B2), A1 a1, A2 a2)

Returns: A function object λ such that the expression λ(v1, v2, ..., vm) is equivalent to f(µ(a1, v1, v2, ..., vm), µ(a2, v1, v2, ..., vm)).

Throws: Nothing unless the copy constructors of A1 or A2 throw an exception.

template<class R, class T, class B1, class A1, class A2> unspecified-9 bind(R (T::*f) (B1), A1 a1, A2 a2)

Effects: Equivalent to bind<R>(boost::mem_fn(f), a1, a2);

template<class R, class T, class B1, class A1, class A2> unspecified-10 bind(R (T::*f) (B1) const, A1 a1, A2 a2)

Effects: Equivalent to bind<R>(boost::mem_fn(f), a1, a2);

Additional overloads

Implementations are allowed to provide additional bind overloads in order to support more arguments or different function pointer variations.

Implementation

Files

  • boost/bind.hpp (main header)

  • boost/bind/bind_cc.hpp (used by bind.hpp, do not include directly)

  • boost/bind/bind_mf_cc.hpp (used by bind.hpp, do not include directly)

  • boost/bind/bind_template.hpp (used by bind.hpp, do not include directly)

  • boost/bind/arg.hpp (defines the type of the placeholder arguments)

  • boost/bind/placeholders.hpp (defines the _1, _2, ... _9 placeholders)

  • boost/bind/apply.hpp (apply helper function object)

  • boost/bind/protect.hpp (protect helper function)

  • boost/bind/make_adaptable.hpp (make_adaptable helper function)

  • libs/bind/test/bind_test.cpp (test)

  • libs/bind/bind_as_compose.cpp (function composition example)

  • libs/bind/bind_visitor.cpp (visitor example)

  • libs/bind/test/bind_stdcall_test.cpp (test with __stdcall functions)

  • libs/bind/test/bind_stdcall_mf_test.cpp (test with __stdcall member functions)

  • libs/bind/test/bind_fastcall_test.cpp (test with __fastcall functions)

  • libs/bind/test/bind_fastcall_mf_test.cpp (test with __fastcall member functions)

Dependencies

  • Boost.Config

  • boost/ref.hpp

  • boost/mem_fn.hpp

  • boost/type.hpp

Number of Arguments

This implementation supports function objects with up to nine arguments. This is an implementation detail, not an inherent limitation of the design.

"__stdcall", "__cdecl", "__fastcall", and "pascal" Support

Some platforms allow several types of (member) functions that differ by their calling convention (the rules by which the function is invoked: how are arguments passed, how is the return value handled, and who cleans up the stack - if any.)

For example, Windows API functions and COM interface member functions use a calling convention known as __stdcall. Borland VCL components use __fastcall. Mac toolbox functions use a pascal calling convention.

To use bind with __stdcall functions, #define the macro BOOST_BIND_ENABLE_STDCALL before including <boost/bind.hpp>.

To use bind with __stdcall member functions, #define the macro BOOST_MEM_FN_ENABLE_STDCALL before including <boost/bind.hpp>.

To use bind with __fastcall functions, #define the macro BOOST_BIND_ENABLE_FASTCALL before including <boost/bind.hpp>.

To use bind with __fastcall member functions, #define the macro BOOST_MEM_FN_ENABLE_FASTCALL before including <boost/bind.hpp>.

To use bind with pascal functions, #define the macro BOOST_BIND_ENABLE_PASCAL before including <boost/bind.hpp>.

To use bind with __cdecl member functions, #define the macro BOOST_MEM_FN_ENABLE_CDECL before including <boost/bind.hpp>.

It is best to define these macros in the project options, via -D on the command line, or as the first line in the translation unit (.cpp file) where bind is used. Not following this rule can lead to obscure errors when a header includes bind.hpp before the macro has been defined.

[Note: this is a non-portable extension. It is not part of the interface.]

[Note: Some compilers provide only minimal support for the __stdcall keyword.]

visit_each support

Function objects returned by bind support the experimental and undocumented, as of yet, visit_each enumeration interface.

See bind_visitor.cpp for an example.

Copyright © 2001, 2002 by Peter Dimov and Multi Media Ltd. Copyright 2003-2005 Peter Dimov. Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


Boost.Function

Introduction

History & Compatibility Notes

Tutorial
Basic Usage
Free functions
Member functions
References to Function Objects
Comparing Boost. Function function objects
Reference
Definitions
Header <boost/function.hpp>
Header <boost/function_equal.hpp>
Frequently Asked Questions
Miscellaneous Notes
Boost. Function vs. Function Pointers
Performance
Combatting virtual function "bloat"
Test suite
Acceptance tests
Negative tests

Introduction

The Boost.Function library contains a family of class templates that are function object wrappers. The notion is similar to a generalized callback. It shares features with function pointers in that both define a call interface (e.g., a function taking two integer arguments and returning a floating-point value) through which some implementation can be called, and the implementation that is invoked may change throughout the course of the program.

Generally, any place in which a function pointer would be used to defer a call or make a callback, Boost.Function can be used instead to allow the user greater flexibility in the implementation of the target. Targets can be any 'compatible' function object (or function pointer), meaning that the arguments to the interface designated by Boost.Function can be converted to the arguments of the target function object.

Douglas Gregor Copyright © 2001-2004 Douglas Gregor Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Reference

Definitions

Header <boost/function.hpp>

Header <boost/function_equal.hpp>

Definitions

  • A function object f is compatible if for the given set of argument types Arg1, Arg2, ..., ArgN and a return type ResultType, the appropriate following function is well-formed:

      // if ResultType is not void
  ResultType foo(Arg1 arg1, Arg2 arg2, ..., ArgN argN)
  {
    return f(arg1, arg2, ..., argN);
  }

  // if ResultType is void
  ResultType foo(Arg1 arg1, Arg2 arg2, ..., ArgN argN)
  {
    f(arg1, arg2, ..., argN);
  }

    A special provision is made for pointers to member functions. Though they are not function objects, Boost.Function will adapt them internally to function objects. This requires that a pointer to member function of the form R (X::*mf)(Arg1, Arg2, ..., ArgN) cv-quals be adapted to a function object with the following function call operator overloads:

      template<typename P>
  R operator()(cv-quals P& x, Arg1 arg1, Arg2 arg2, ..., ArgN argN) const
  {
    return (*x).*mf(arg1, arg2, ..., argN);
  }

  • A function object f of type F is stateless if it is a function pointer or if boost::is_stateless<T> is true. The construction of or copy to a Boost.Function object from a stateless function object will not cause exceptions to be thrown and will not allocate any storage.

Header <boost/function.hpp>

namespace boost {
  class bad_function_call;
  class function_base;
  template<typename R, typename T1, typename T2, ..., typename TN,
           typename Allocator = std::allocator<void> >
    class functionN;
  template<typename T1, typename T2, ..., typename TN, typename Allocator>
    void swap(functionN<T1, T2, ..., TN, Allocator>&,
              functionN<T1, T2, ..., TN, Allocator>&);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator==(const functionN<T1, T2, ..., TN, Allocator>&, Functor);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator==(Functor, const functionN<T1, T2, ..., TN, Allocator>&);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator==(const functionN<T1, T2, ..., TN, Allocator>&,
                    reference_wrapper<Functor>);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator==(reference_wrapper<Functor>,
                    const functionN<T1, T2, ..., TN, Allocator>&);
  template<typename T1, typename T2, ..., typename TN, typename Allocator1,
           typename U1, typename U2, ..., typename UN, typename Allocator2>
    void operator==(const functionN<T1, T2, ..., TN, Allocator1>&,
                    const functionN<U1, U2, ..., UN, Allocator2>&);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator!=(const functionN<T1, T2, ..., TN, Allocator>&, Functor);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator!=(Functor, const functionN<T1, T2, ..., TN, Allocator>&);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator!=(const functionN<T1, T2, ..., TN, Allocator>&,
                    reference_wrapper<Functor>);
  template<typename T1, typename T2, ..., typename TN, typename Allocator,
           typename Functor>
    bool operator!=(reference_wrapper<Functor>,
                    const functionN<T1, T2, ..., TN, Allocator>&);
  template<typename T1, typename T2, ..., typename TN, typename Allocator1,
           typename U1, typename U2, ..., typename UN, typename Allocator2>
    void operator!=(const functionN<T1, T2, ..., TN, Allocator1>&,
                    const functionN<U1, U2, ..., UN, Allocator2>&);
  template<typename Signature, typename Allocator = std::allocator<void> >
    class function;
  template<typename Signature, typename Allocator>
    void swap(function<Signature, Allocator>&,
              function<Signature, Allocator>&);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator==(const function<Signature, Allocator>&, Functor);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator==(Functor, const function<Signature, Allocator>&);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator==(const function<Signature, Allocator>&,
                    reference_wrapper<Functor>);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator==(reference_wrapper<Functor>,
                    const function<Signature, Allocator>&);
  template<typename Signature1, typename Allocator1, typename Signature2,
           typename Allocator2>
    void operator==(const function<Signature1, Allocator1>&,
                    const function<Signature2, Allocator2>&);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator!=(const function<Signature, Allocator>&, Functor);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator!=(Functor, const function<Signature, Allocator>&);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator!=(const function<Signature, Allocator>&,
                    reference_wrapper<Functor>);
  template<typename Signature, typename Allocator, typename Functor>
    bool operator!=(reference_wrapper<Functor>,
                    const function<Signature, Allocator>&);
  template<typename Signature1, typename Allocator1, typename Signature2,
           typename Allocator2>
    void operator!=(const function<Signature1, Allocator1>&,
                    const function<Signature2, Allocator2>&);
}

Header <boost/function_equal.hpp>

namespace boost {
  template<typename F, typename G> bool function_equal(const F&, const G&);
}
Copyright © 2001-2004 Douglas Gregor


Class template reference_wrapper

boost::reference_wrapper — Contains a reference to an object of type T.

Synopsis

template<typename T>
class reference_wrapper {
public:
  // types
  typedef T type;

  // construct/copy/destruct
  explicit reference_wrapper(T&);

  // access
  operator T&() const;
  T& get() const;
  T* get_pointer() const;
};

// constructors
reference_wrapper<T> ref(T&);
reference_wrapper<T const> cref(T const&);

Description

reference_wrapper is primarily used to "feed" references to function templates (algorithms) that take their parameter by value. It provides an implicit conversion to T&, which usually allows the function templates to work on references unmodified.

reference_wrapper construct/copy/destruct

  1. explicit reference_wrapper(T& t);
    Effects: Constructs a reference_wrapper object that stores a reference to t.
    Throws: Does not throw.

reference_wrapper access

  1. operator T&() const;
    Returns: The stored reference.
    Throws: Does not throw.
  2. T& get() const;
    Returns: The stored reference.
    Throws: Does not throw.
  3. T* get_pointer() const;
    Returns: A pointer to the object referenced by the stored reference.
    Throws: Does not throw.

reference_wrapper constructors

  1. reference_wrapper<T> ref(T& t);
    Returns: reference_wrapper<T>(t)
    Throws: Does not throw.
  2. reference_wrapper<T const> cref(T const& t);
    Returns:

    reference_wrapper<T const>(t)

    Throws: Does not throw.

Copyright © 1999, 2000 Jaakko Järvi
Copyright © 2001, 2002 Peter Dimov
Copyright © 2002 David Abrahams


Boost: checked_delete.hpp documentation

checked_delete.hpp

The header <boost/checked_delete.hpp> defines two function templates, checked_delete and checked_array_delete, and two class templates, checked_deleter and checked_array_deleter.

The C++ Standard allows, in 5.3.5/5, pointers to incomplete class types to be deleted with a delete-expression. When the class has a non-trivial destructor, or a class-specific operator delete, the behavior is undefined. Some compilers issue a warning when an incomplete type is deleted, but unfortunately, not all do, and programmers sometimes ignore or disable warnings.

A particularly troublesome case is when a smart pointer's destructor, such as boost::scoped_ptr<T>::~scoped_ptr, is instantiated with an incomplete type. This can often lead to silent, hard to track failures.

The supplied function and class templates can be used to prevent these problems, as they require a complete type, and cause a compilation error otherwise.

Synopsis

namespace boost
{

template<class T> void checked_delete(T * p);
template<class T> void checked_array_delete(T * p);
template<class T> struct checked_deleter;
template<class T> struct checked_array_deleter;

}

checked_delete

template<class T> void checked_delete(T * p);

Requires: T must be a complete type. The expression delete p must be well-formed.

Effects: delete p;

checked_array_delete

template<class T> void checked_array_delete(T * p);

Requires: T must be a complete type. The expression delete [] p must be well-formed.

Effects: delete [] p;

checked_deleter

template<class T> struct checked_deleter
{
    typedef void result_type;
    typedef T * argument_type;
    void operator()(T * p) const;
};

void checked_deleter<T>::operator()(T * p) const;

Requires: T must be a complete type. The expression delete p must be well-formed.

Effects: delete p;

checked_array_deleter

template<class T> struct checked_array_deleter
{
    typedef void result_type;
    typedef T * argument_type;
    void operator()(T * p) const;
};

void checked_array_deleter<T>::operator()(T * p) const;

Requires: T must be a complete type. The expression delete [] p must be well-formed.

Effects: delete [] p;

Copyright © 2002 by Peter Dimov. Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


Smart Pointer Changes

The change to the Boost smart pointers in February 2002, introduced a number of changes. Since the previous version of the smart pointers was in use for a long time, it's useful to have a detailed list of what changed from a library user's point of view.

Note that for compilers that don't support member templates well enough, a separate implementation is used that lacks many of the new features and is more like the old version.

Features Requiring Code Changes to Take Advantage

  • Each of the smart pointer class templates now have their own header file. For compatibility, the <boost/smart_ptr.hpp> header now includes the headers for the four classic smart pointer class templates.

  • The weak_ptr template was added.

  • The new shared_ptr and shared_array relax the requirement that the pointed-to object's destructor must be visible when instantiating the shared_ptr destructor. This makes it easier to have shared_ptr members in classes without explicit destructors.

  • A custom deallocator can be passed in when creating a shared_ptr or shared_array .

  • shared_static_cast and shared_dynamic_cast function templates are provided which work for shared_ptr and weak_ptr as static_cast and dynamic_cast do for pointers.

  • The self-assignment misfeature has been removed from shared_ptr::reset, although it is still present in scoped_ptr, and in std::auto_ptr. Calling reset with a pointer to the object that is already owned by the shared_ptr results in undefined behavior (an assertion, or eventually a double-delete if assertions are off).

  • The BOOST_SMART_PTR_CONVERSION feature has been removed.

  • shared_ptr <void> is now allowed.

Features That Improve Robustness

  • The manipulation of use counts is now thread safe on Windows, Linux, and platforms that support pthreads. See the <boost/detail/atomic_count.hpp> file for details

  • The new shared_ptr will always delete the object using the pointer it was originally constructed with. This prevents subtle problems that could happen if the last shared_ptr was a pointer to a sub-object of a class that did not have a virtual destructor.

Implementation Details

  • Some bugs in the assignment operator implementations and in reset have been fixed by using the "copy and swap" idiom.

  • Assertions have been added to check preconditions of various functions; however, since these use the new <boost/assert.hpp> header, the assertions are disabled by default.

  • The partial specialization of std::less has been replaced by operator< overloads which accomplish the same thing without relying on undefined behavior.

  • The incorrect overload of std::swap has been replaced by boost::swap, which has many of the same advantages for generic programming but does not violate the C++ standard.

Copyright 2002 Darin Adler. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template array

boost::array — STL compliant container wrapper for arrays of constant size.

Synopsis

template<typename T, std::size_t N>
class array {
public:
  // types
  typedef T                                     value_type;
  typedef T*                                    iterator;
  typedef const T*                              const_iterator;
  typedef std::reverse_iterator<iterator>       reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
  typedef T&                                    reference;
  typedef const T&                              const_reference;
  typedef std::size_t                           size_type;
  typedef std::ptrdiff_t                        difference_type;

  // static constants
  static const size_type static_size = N;

  // construct/copy/destruct
  template<typename U> array& operator=(const array<U, N>&);

  // iterator support
  iterator begin();
  const_iterator begin() const;
  iterator end();
  const_iterator end() const;

  // reverse iterator support
  reverse_iterator rbegin();
  const_reverse_iterator rbegin() const;
  reverse_iterator rend();
  const_reverse_iterator rend() const;

  // capacity
  size_type size();
  bool empty();
  size_type max_size();

  // element access
  reference operator[](size_type);
  const_reference operator[](size_type) const;
  reference at(size_type);
  const_reference at(size_type) const;
  reference front();
  const_reference front() const;
  reference back();
  const_reference back() const;
  const T* data() const;
  T* c_array();

  // modifiers
  void swap(array<T, N>&);
  void assign(const T&);

  T elems[N];
};

// specialized algorithms
template<typename T, std::size_t N> void swap(array<T, N>&, array<T, N>&);

// comparisons
template<typename T, std::size_t N>
  bool operator==(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator!=(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator<(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator>(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator<=(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator>=(const array<T, N>&, const array<T, N>&);

Description

array construct/copy/destruct

  1. template<typename U> array& operator=(const array<U, N>& other);
    Effects: std::copy(rhs.begin(),rhs.end(), begin())

array iterator support

  1. iterator begin();
const_iterator begin() const;
    Returns: iterator for the first element
    Throws: will not throw

  2. iterator end();
const_iterator end() const;
    Returns: iterator for position after the last element
    Throws: will not throw

array reverse iterator support

  1. reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
    Returns: reverse iterator for the first element of reverse iteration

  2. reverse_iterator rend();
const_reverse_iterator rend() const;
    Returns: reverse iterator for position after the last element in reverse iteration

array capacity

  1. size_type size();
    Returns: N
  2. bool empty();
    Returns: N==0
    Throws: will not throw
  3. size_type max_size();
    Returns: N
    Throws: will not throw

array element access

  1. reference operator[](size_type i);
const_reference operator[](size_type i) const;
    Requires: i < N
    Returns: element with index i
    Throws: will not throw.

  2. reference at(size_type i);
const_reference at(size_type i) const;
    Returns: element with index i
    Throws: std::range_error if i >= N

  3. reference front();
const_reference front() const;
    Requires: N > 0
    Returns: the first element
    Throws: will not throw

  4. reference back();
const_reference back() const;
    Requires:

    N > 0

    Returns: the last element
    Throws: will not throw
  5. const T* data() const;
    Returns: elems
    Throws: will not throw
  6. T* c_array();
    Returns: elems
    Throws: will not throw

array modifiers

  1. void swap(array<T, N>& other);
    Effects: std::swap_ranges(begin(), end(), other.begin())
    Complexity: linear in N
  2. void assign(const T& value);
    Effects: std::fill_n(begin(), N, value)

array specialized algorithms

  1. template<typename T, std::size_t N> void swap(array<T, N>& x, array<T, N>& y);
    Effects: x.swap(y)
    Throws: will not throw.

array comparisons

  1. template<typename T, std::size_t N>
  bool operator==(const array<T, N>& x, const array<T, N>& y);
    Returns: std::equal(x.begin(), x.end(), y.begin())
  2. template<typename T, std::size_t N>
  bool operator!=(const array<T, N>& x, const array<T, N>& y);
    Returns: !(x == y)
  3. template<typename T, std::size_t N>
  bool operator<(const array<T, N>& x, const array<T, N>& y);
    Returns: std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end())
  4. template<typename T, std::size_t N>
  bool operator>(const array<T, N>& x, const array<T, N>& y);
    Returns: y < x
  5. template<typename T, std::size_t N>
  bool operator<=(const array<T, N>& x, const array<T, N>& y);
    Returns: !(y < x)
  6. template<typename T, std::size_t N>
  bool operator>=(const array<T, N>& x, const array<T, N>& y);
    Returns: !(x < y)
Copyright © 2001-2004 Nicolai M. Josuttis


For more information

To find more details about using ordinary arrays in C++ and the framework of the STL, see e.g.

The C++ Standard Library - A Tutorial and Reference
by Nicolai M. Josuttis
Addison Wesley Longman, 1999
ISBN 0-201-37926-0

Home Page of Nicolai Josuttis

Copyright © 2001-2004 Nicolai M. Josuttis


Chapter 2. Boost.Array

Boost.Array

Introduction

Reference

Header <boost/array.hpp>

Design Rationale

For more information...

Introduction

The C++ Standard Template Library (STL) as part of the C++ Standard Library provides a framework for processing algorithms on different kinds of containers. However, ordinary arrays don't provide the interface of STL containers (although, they provide the iterator interface of STL containers).

As replacement for ordinary arrays, the STL provides class std::vector. However, std::vector<> provides the semantics of dynamic arrays. Thus, it manages data to be able to change the number of elements. This results in some overhead in case only arrays with static size are needed.

In his book, Generic Programming and the STL, Matthew H. Austern introduces a useful wrapper class for ordinary arrays with static size, called block. It is safer and has no worse performance than ordinary arrays. In The C++ Programming Language, 3rd edition, Bjarne Stroustrup introduces a similar class, called c_array, which Nicolai Josuttis presents slightly modified in his book The C++ Standard Library - A Tutorial and Reference, called carray. This is the essence of these approaches spiced with many feedback from boost.

Note that Class array is suggested to be part of the next Technical Report, which will extend the C++ Standard (see http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1548.htm).

Class array fulfills most but not all of the requirements of "reversible containers" (see Section 23.1, [lib.container.requirements] of the C++ Standard). The reasons array is not an reversible STL container is because:

  • No constructors are provided.

  • Elements may have an undetermined initial value (see the section called “Design Rationale”).

  • swap() has no constant complexity.

  • size() is always constant, based on the second template argument of the type.

  • The container provides no allocator support.

It doesn't fulfill the requirements of a "sequence" (see Section 23.1.1, [lib.sequence.reqmts] of the C++ Standard), except that:

  • front() and back() are provided.

  • operator[] and at() are provided.

Copyright © 2001-2004 Nicolai M. Josuttis
Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.Array

Name

boost::array: This is a STL compliant container wrapper for arrays of constant size.

Synopsis

template<typename T, std::size_t N>
class array {
public:
  // types
  typedef T                                     value_type;
  typedef T*                                    iterator;
  typedef const T*                              const_iterator;
  typedef std::reverse_iterator<iterator>       reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
  typedef T&                                    reference;
  typedef const T&                              const_reference;
  typedef std::size_t                           size_type;
  typedef std::ptrdiff_t                        difference_type;

  // static constants
  static const size_type static_size = N;

  // construct/copy/destruct
  template<typename U> array& operator=(const array<U, N>&);

  // iterator support
  iterator begin();
  const_iterator begin() const;
  iterator end();
  const_iterator end() const;

  // reverse iterator support
  reverse_iterator rbegin();
  const_reverse_iterator rbegin() const;
  reverse_iterator rend();
  const_reverse_iterator rend() const;

  // capacity
  size_type size();
  bool empty();
  size_type max_size();

  // element access
  reference operator[](size_type);
  const_reference operator[](size_type) const;
  reference at(size_type);
  const_reference at(size_type) const;
  reference front();
  const_reference front() const;
  reference back();
  const_reference back() const;
  const T* data() const;
  T* c_array();

  // modifiers
  void swap(array<T, N>&);
  void assign(const T&);

  T elems[N];
};

// specialized algorithms
template<typename T, std::size_t N> void swap(array<T, N>&, array<T, N>&);

// comparisons
template<typename T, std::size_t N>
  bool operator==(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator!=(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator<(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator>(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator<=(const array<T, N>&, const array<T, N>&);
template<typename T, std::size_t N>
  bool operator>=(const array<T, N>&, const array<T, N>&);

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Design Rationale

There was an important design tradeoff regarding the constructors: if the user implements the array as an "aggregate" (see Section 8.5.1, [dcl.init.aggr], of the C++ Standard). This would mean:

  • An array can be initialized with a brace-enclosing, comma-separated list of initializers for the elements of the container, written in increasing subscript order:

    boost::array<int,4> a = { { 1, 2, 3 } };

    Note: If there are fewer elements in the initializer list, then each remaining element gets default-initialized (thus, it has a defined value).

However, this approach has its drawbacks: passing no initializer list means that the elements have an undetermined initial value, because the rule says that aggregates may have:

  • No user-declared constructors.

  • No private or protected non-static data members.

  • No base classes.

  • No virtual functions.

Nevertheless, The current implementation uses this approach.

Note:For standard conforming compilers it is possible to use fewer braces (according to 8.5.1 (11) of the Standard). That is, initialize an array as follows:

boost::array<int,4> a = { 1, 2, 3 };

The code is provided "as is" without expressed or implied warranty.

Copyright © 2001-2004 Nicolai M. Josuttis


Reference

Header <boost/array.hpp>

namespace boost {
  template<typename T, std::size_t N> class array;
  template<typename T, std::size_t N> void swap(array<T, N>&, array<T, N>&);
  template<typename T, std::size_t N>
    bool operator==(const array<T, N>&, const array<T, N>&);
  template<typename T, std::size_t N>
    bool operator!=(const array<T, N>&, const array<T, N>&);
  template<typename T, std::size_t N>
    bool operator<(const array<T, N>&, const array<T, N>&);
  template<typename T, std::size_t N>
    bool operator>(const array<T, N>&, const array<T, N>&);
  template<typename T, std::size_t N>
    bool operator<=(const array<T, N>&, const array<T, N>&);
  template<typename T, std::size_t N>
    bool operator>=(const array<T, N>&, const array<T, N>&);
}
Copyright © 2001-2004 Nicolai M. Josuttis


dynamic_bitset<Block, Allocator>

Description

Synopsis

Definitions

Examples

Rationale

Header Files

Template Parameters

Concepts modeled

Type requirements

Public base classes

Nested type names

Public data members

Constructors

Destructor

Member functions

Non-member functions

Exception guarantees

Changes from previous version(s)

See also

Description

The dynamic_bitset class represents a set of bits. It provides access to the value of individual bits via an operator[] and provides all of the bitwise operators that one can apply to built in integers, such as operator& and operator<<. Specify the number of bits in the set at runtime via a parameter to the constructor of the dynamic_bitset.

The dynamic_bitset class is nearly identical to the std::bitset class. The difference is that the size of the dynamic_bitset (the number of bits) is specified at run-time during the construction of a dynamic_bitset object, whereas the size of a std::bitset is specified at compile-time through an integer template parameter.

The main problem that dynamic_bitset is designed to solve is that of representing a subset of a finite set. Each bit represents whether an element of the finite set is in the subset or not. As such the bitwise operations of dynamic_bitset, such as operator& and operator|, correspond to set operations, such as intersection and union.

Synopsis

namespace boost {

template <typename Block, typename Allocator>
class dynamic_bitset
{
public:
    typedef Block block_type;
    typedef Allocator allocator_type;
    typedef implementation-defined size_type;

    static const int bits_per_block = implementation-defined;
    static const size_type npos = implementation-defined;

    class reference
    {
        void operator&(); // not defined
    public:
        // An automatically generated copy constructor.

        reference& operator=(bool value);
        reference& operator=(const reference& rhs);

        reference& operator|=(bool value);
        reference& operator&=(bool value);
        reference& operator^=(bool value);
        reference& operator-=(bool value);

        bool operator~() const;
        operator bool() const;
        reference& flip();
    };
    typedef bool const_reference;

    explicit dynamic_bitset(const Allocator& alloc = Allocator());

    explicit dynamic_bitset(size_type num_bits, unsigned long value = 0,
                            const Allocator& alloc = Allocator());

    template <typename CharT, typename Traits, typename Alloc>
    explicit dynamic_bitset(const std::basic_string<CharT, Traits, Alloc>& s,
        typename std::basic_string<CharT, Traits, Alloc>::size_type pos = 0,
        typename std::basic_string<CharT, Traits, Alloc>::size_type n = std::basic_string<CharT, Traits, Alloc>::npos,
        const Allocator& alloc = Allocator());

    template <typename BlockInputIterator>
    dynamic_bitset(BlockInputIterator first, BlockInputIterator last,
                   const Allocator& alloc = Allocator());

    dynamic_bitset(const dynamic_bitset& b);

    void swap(dynamic_bitset& b);

    dynamic_bitset& operator=(const dynamic_bitset& b);

    allocator_type get_allocator() const;

    void resize(size_type num_bits, bool value = false);
    void clear();
    void push_back(bool bit);
    void append(Block block);
    template <typename BlockInputIterator>
    void append(BlockInputIterator first, BlockInputIterator last);

    dynamic_bitset& operator&=(const dynamic_bitset& b);
    dynamic_bitset& operator|=(const dynamic_bitset& b);
    dynamic_bitset& operator^=(const dynamic_bitset& b);
    dynamic_bitset& operator-=(const dynamic_bitset& b);
    dynamic_bitset& operator<<=(size_type n);
    dynamic_bitset& operator>>=(size_type n);
    dynamic_bitset operator<<(size_type n) const;
    dynamic_bitset operator>>(size_type n) const;

    dynamic_bitset& set(size_type n, bool val = true);
    dynamic_bitset& set();
    dynamic_bitset& reset(size_type n);
    dynamic_bitset& reset();
    dynamic_bitset& flip(size_type n);
    dynamic_bitset& flip();
    bool test(size_type n) const;
    bool any() const;
    bool none() const;
    dynamic_bitset operator~() const;
    size_type count() const;

    reference operator[](size_type pos);
    bool operator[](size_type pos) const;

    unsigned long to_ulong() const;

    size_type size() const;
    size_type num_blocks() const;
    size_type max_size() const;
    bool empty() const;

    bool is_subset_of(const dynamic_bitset& a) const;
    bool is_proper_subset_of(const dynamic_bitset& a) const;

    size_type find_first() const;
    size_type find_next(size_type pos) const;

};

template <typename B, typename A>
bool operator==(const dynamic_bitset<B, A>& a, const dynamic_bitset<B, A>& b);

template <typename Block, typename Allocator>
bool operator!=(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename B, typename A>
bool operator<(const dynamic_bitset<B, A>& a, const dynamic_bitset<B, A>& b);

template <typename Block, typename Allocator>
bool operator<=(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename Block, typename Allocator>
bool operator>(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename Block, typename Allocator>
bool operator>=(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator&(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator|(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator^(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator-(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator, typename CharT, typename Alloc>
void to_string(const dynamic_bitset<Block, Allocator>& b,
          std::basic_string<CharT, Alloc>& s);

template <typename Block, typename Allocator, typename BlockOutputIterator>
void to_block_range(const dynamic_bitset<Block, Allocator>& b,
                    BlockOutputIterator result);

template <typename CharT, typename Traits, typename Block, typename Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const dynamic_bitset<Block, Allocator>& b);

template <typename CharT, typename Traits, typename Block, typename Allocator>
std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is, dynamic_bitset<Block, Allocator>& b);

} // namespace boost

Definitions

Each bit represents the Boolean value true or false (1 or 0). Assign a '1' to set a bit and a '0' to clear or reset a bit. To flip a bit is to change the value to 1 if it was 0 and to 0 if it was 1. Each bit has a non-negative position. A bitset x contains x.size() bits, with each bit assigned a unique position in the range [0,x.size()]. The bit at position 0 is called the least significant bit and the bit at position size() - 1 is the most significant bit. When converting an instance of dynamic_bitset to or from an unsigned long n, the bit at position i of the bitset has the same value as (n >> i) & 1.

Examples

The code snippet below provides an example of setting and reading some bits.
Note: The operator[] goes from the least-significant bit at 0 to the most significant bit at size()-1. The operator<< for dynamic_bitset prints the bitset from most-significant to least-significant, since that is the format most people are use to reading.

#include <iostream>
#include <boost/dynamic_bitset.hpp>
int main(int, char*[]) {
  boost::dynamic_bitset<> x(5); // all 0's by default
  x[0] = 1;
  x[1] = 1;
  x[4] = 1;
  for (boost::dynamic_bitset<>::size_type i = 0; i < x.size(); ++i)
    std::cout << x[i];
  std::cout << "\n";
  std::cout << x << "\n";
  return EXIT_SUCCESS;
}

Output:

11001
10011

The code snippet below provides an example of creating some bitsets from integers (unsigned longs).

#include <iostream>
#include <boost/dynamic_bitset.hpp>
int main(int, char*[])
{
  const boost::dynamic_bitset<> b0(2, 0ul);
  std::cout << "bits(0) = " << b0 << std::endl;

  const boost::dynamic_bitset<> b1(2, 1ul);
  std::cout << "bits(1) = " << b1 << std::endl;

  const boost::dynamic_bitset<> b2(2, 2ul);
  std::cout << "bits(2) = " << b2 << std::endl;

  const boost::dynamic_bitset<> b3(2, 3ul);
  std::cout << "bits(3) = " << b3 << std::endl;

  return EXIT_SUCCESS;
}

Output:

bits(0) = 00
bits(1) = 01
bits(2) = 10
bits(3) = 11

The code snippet below shows how to perform some bitwise operations:

#include <iostream>
#include <boost/dynamic_bitset.hpp>
int main(int, char*[]) {
  const boost::dynamic_bitset<> mask(12, 2730ul);
  std::cout << "mask = " << mask << std::endl;

  boost::dynamic_bitset<> x(12);

  std::cout << "Enter a 12-bit bitset in binary: " << std::flush;
  if (std::cin >> x) {
    std::cout << "input number:     " << x << std::endl;
    std::cout << "As unsigned long: " << x.to_ulong() << std::endl;
    std::cout << "And with mask:    " << (x & mask) << std::endl;
    std::cout << "Or with mask:     " << (x | mask) << std::endl;
    std::cout << "Shifted left:     " << (x << 1) << std::endl;
    std::cout << "Shifted right:    " << (x >> 1) << std::endl;
  }
  return EXIT_SUCCESS;
}

Output:

mask = 101010101010
Enter a 12-bit bitset in binary: 100110101101
input number =    100110101101
As unsigned long: 2477
And with mask:    100010101000
Or with mask:     101110101111
Shifted left:     001101011010
Shifted right:    010011010110

Rationale

The dynamic_bitset does not provide iterators (and therefore is not a Container) for the following reasons:

  1. std::bitset does not have iterators, and dynamic_bitset is meant to be a run-time sized version of std::bitset.

  2. The dynamic_bitset is not designed to be a Container.

  3. A container with a proxy reference type can not fulfill the container requirements as specified in the C++ standard (unless one resorts to strange iterator semantics). std::vector<bool> has a proxy reference type and does not fulfill the container requirements, andAs a result has caused many problems. One common problem is when people try to use iterators from std::vector<bool> with a Standard algorithm such as std::search. The std::search requirements say that the iterator must be a Forward Iterator, but the std::vector<bool>::iterator does not meet this requirement because of the proxy reference. Depending on the implementation, they may or not be a compile error or even a run-time error due to this misuse. For further discussion of the problem see Effective STL by Scott Meyers). So dynamic_bitset tries to avoid these problems by not pretending to be a container.

Some people prefer the name "toggle" to "flip". The name "flip" was chosen because that is the name used in std::bitset. In fact, most of the function names for dynamic_bitset were chosen for this reason.

dynamic_bitset does not throw exceptions when a precondition is violated (as is done in std::bitset). Instead assert is used. See the guidelines for Error and Exception Handling for the explanation.

Header Files

The class dynamic_bitset is defined in the header boost/dynamic_bitset.hpp. Also, there is a forward declaration for dynamic_bitset in the header boost/dynamic_bitset_fwd.hpp.

Template parameters

Parameter | Description | Default
Block | The integer type in which the bits are stored. | unsigned long
Allocator | The allocator type used for all internal memory management. | std::allocator<Block>

Concepts Modeled

Assignable, Default Constructible, Equality Comparable, LessThan Comparable.

Type requirements

Block is an unsigned integer type. Allocator satisfies the Standard requirements for an allocator.

Public base classes

None.

Nested type names

dynamic_bitset::reference

A proxy class that acts as a reference to a single bit. It contains an assignment operator, a conversion to bool, an operator~, and a member function flip. It exists only as a helper class for dynamic_bitset's operator[]. The following table describes the valid operations on the reference type. Assume,b is an instance of dynamic_bitset, i, j are of size_type and in the range [0,b.size()].

Note: Write b[i] only when an object of type reference that was initialized from b[i]. The variable x is a bool.

Expression
x = b[i] | Assign the ith bit of b to x.
(bool)b[i] | Return the ith bit of b.
b[i] = x | Set the ith bit of b to the value of x and return b[i].
b[i] |= x | Or the ith bit of b with the value of x and return b[i].
b[i] &= x | And the ith bit of b with the value of x and return b[i].
b[i] ^= x | Exclusive-Or the ith bit of b with the value of x and return b[i].
b[i] -= x | If x==true, clear the ith bit of b. Returns b[i].
b[i] = b[j] | Set the ith bit of b to the value of the jth bit of b and return b[i].
b[i] |= b[j] | Or the ith bit of b with the jth bit of b and return b[i].
b[i] &= b[j] | And the ith bit of b with the jthbit of b and return b[i].
b[i] ^= b[j] | Exclusive-Or the ith bit of b with the jth bit of b and return b[i].
b[i] -= b[j] | If the jth bit of b is set, clear the ith bit of b. Returns b[i].
x = ~b[i] | Assign the opposite of the ith bit of b to x.
(bool)~b[i] | Return the opposite of the ith bit of b.
b[i].flip() | Flip the ith bit of b and return b[i].
dynamic_bitset::const_reference

The type bool.

dynamic_bitset::size_type

The unsigned integer type for representing the size of the bit set.

dynamic_bitset::block_type

The same type as Block.

dynamic_bitset::allocator_type;

The same type as Allocator.

Public data members

dynamic_bitset::bits_per_block

The number of bits the type Block uses to represent values, excluding any padding bits. Numerically, equal to std::numeric_limits<Block>::digits.

dynamic_bitset::npos

The maximum value of size_type. [gps]

Constructors

dynamic_bitset(const Allocator& alloc = Allocator())

Effects: Constructs a bitset of size zero. A copy of the alloc object will be used in subsequent bitset operations such as resize to allocate memory.
Postconditions: this->size() == 0
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).
(Required by Default Constructible.)

dynamic_bitset(size_type num_bits,
               unsigned long value = 0,
               const Allocator& alloc = Allocator())

Effects: Constructs a bitset from an integer. The first M bits are initialized to the corresponding bits in val and all other bits, if any, to zero (where M = min(num_bits, std::numeric_limits<unsigned long>::digits)). A copy of the alloc object will be used in subsequent bitset operations such as resize to allocate memory.
Postconditions:

  • this->size() == num_bits

  • For all i in the range [0,M), (*this)[i] == (value >> i) & 1

  • For all i in the range [M,num_bits), (*this)[i] == false

Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset(const dynamic_bitset& x)

Effects: Constructs a bitset that is a copy of the bitset x. The allocator for this bitset is a copy of the allocator in x.
Postconditions: For all i in the range [0,x.size()), (*this)[i] == x[i].
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).
(Required by Assignable.)

template <typename BlockInputIterator>
explicit
dynamic_bitset(BlockInputIterator first, BlockInputIterator last,
               const Allocator& alloc = Allocator());

Effects: Constructs a bitset based on a range of blocks. Let *first be block number 0, *++first block number 1, etc. Block number b is used to initialize the bits of the dynamic_bitset in the position range [b*bits_per_block, (b+1)*bits_per_block). For each block number b with value bval, the bit (bval >> i) & 1 corresponds to the bit at position (b * bits_per_block + i) in the bitset (where i goes through the range [0, bits_per_block)).
Requires: The type BlockInputIterator must be a model of Input Iterator and its value_type must be the same type as Block.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

template<typename Char, typename Traits, typename Alloc>
explicit
dynamic_bitset(const std::basic_string<Char,Traits,Alloc>& s,
               typename std::basic_string<CharT, Traits, Alloc>::size_type pos = 0,
               typename std::basic_string<CharT, Traits, Alloc>::size_type n = std::basic_string<Char,Traits,Alloc>::npos,
               const Allocator& alloc = Allocator())

Precondition: pos <= s.size() and the characters used to initialize the bits must be 0 or 1.
Effects: Constructs a bitset from a string of 0's and 1's. The first M bits are initialized to the corresponding characters in s, where M = min(s.size() - pos, n). Note that the highest character position in s, not the lowest, corresponds to the least significant bit. That is, character position pos + M - 1 - i corresponds to bit i. So, for example, dynamic_bitset(string("1101")) is the same as dynamic_bitset(13ul).
Throws: an allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

Destructor

~dynamic_bitset()

Effects: Throws:: nothing.

Member Functions

void swap(dynamic_bitset& b);

Effects: The contents of this bitset and bitset b are exchanged.
Postconditions: This bitset is equal to the original b, and b is equal to the previous version of this bitset.
Throws: nothing.

dynamic_bitset& operator=(const dynamic_bitset& x)

Effects: This bitset becomes a copy of the bitset x.
Postconditions: For all i in the range [0,x.size()), (*this)[i] == x[i].
Returns: *this
Throws: nothing
(Required by Assignable.)

allocator_type get_allocator() const;

Returns: A copy of the allocator object used to construct *this.

void resize(size_type num_bits, bool value = false);

Effects: Changes the number of bits of the bitset to num_bits. If num_bits > size() then the bits in the range [0,size()) remain the same, and the bits in [size(),num_bits) are all set to value. If num_bits < size() then the bits in the range [0,num_bits) stay the same (and the remaining bits are discarded).
Postconditions: this->size() == num_bits
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

void clear()

Effects: The size of the bitset becomes zero.
Throws: nothing.

void push_back(bool value);

Effects: Increases the size of the bitset by one, and sets the value of the new most-significant bit to value.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

void append(Block value);

Effects: Appends the bits in value to the bitset (appends to the most-significant end). This increases the size of the bitset by bits_per_block. Let s be the old size of the bitset, then for i in the range [0,bits_per_block), the bit at position (s + i) is set to ((value >> i) & 1).
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

template <typename BlockInputIterator>
void append(BlockInputIterator first, BlockInputIterator last);

Effects: This function provides the same end result as the following code, but is typically more efficient. [gps]

for (; first != last; ++first)
  append(*first);

Requires: The BlockInputIterator type must be a model of Input Iterator and the value_type must be the same type as Block.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset& operator&=(const dynamic_bitset& rhs)

Requires: this->size() == rhs.size().
Effects: Bitwise-AND all the bits in rhs with the bits in this bitset. This is equivalent to:

for (size_type i = 0; i != this->size(); ++i)
  (*this)[i] = (*this)[i] & rhs[i];

Returns: *this
Throws: nothing.

dynamic_bitset& operator|=(const dynamic_bitset& rhs)

Requires: this->size() == rhs.size()
Effects: Bitwise-OR's all the bits in rhs with the bits in this bitset. This is equivalent to:

for (size_type i = 0; i != this->size(); ++i)
  (*this)[i] = (*this)[i] | rhs[i];

Returns: *this
Throws: nothing.

dynamic_bitset& operator^=(const dynamic_bitset& rhs)

Requires: this->size() == rhs.size()
Effects: Bitwise-XOR's all the bits in rhs with the bits in this bitset. This is equivalent to:

for (size_type i = 0; i != this->size(); ++i)
  (*this)[i] = (*this)[i] ^ rhs[i];

Returns: *this
Throws: nothing.

dynamic_bitset& operator-=(const dynamic_bitset& rhs)

Requires: this->size() == rhs.size()
Effects: Computes the set difference of this bitset and the rhs bitset. This is equivalent to:

for (size_type i = 0; i != this->size(); ++i)
  (*this)[i] = (*this)[i] && !rhs[i];

Returns: *this
Throws: nothing.

dynamic_bitset& operator<<=(size_type n)

Effects: Shifts the bits in this bitset to the left by n bits. For each bit in the bitset, the bit at position pos takes on the previous value of the bit at position pos - n, or zero if no such bit exists.
Returns: *this
Throws: nothing.

dynamic_bitset& operator>>=(size_type n)

Effects: Shifts the bits in this bitset to the right by n bits. For each bit in the bitset, the bit at position pos takes on the previous value of bit pos + n, or zero if no such bit exists.
Returns: *this
Throws: nothing.

dynamic_bitset operator<<(size_type n) const

Returns: a copy of *this shifted to the left by n bits. For each bit in the returned bitset, the bit at position pos takes on the value of the bit at position pos - n of this bitset, or zero if no such bit exists.

Note: The expression b << n is equivalent to constructing a temporary copy of b and then using operator<<=.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset operator>>(size_type n) const

Returns: a copy of *this shifted to the right by n bits. For each bit in the returned bitset, the bit at position pos takes on the value of the bit at position pos + n of this bitset, or zero if no such bit exists.

Note: The expression b >> n is equivalent to constructing a temporary copy of b and then using operator>>=.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset& set()

Effects: Sets every bit in this bitset to 1.
Returns: *this
Throws: nothing

dynamic_bitset& flip()

Effects: Flips the value of every bit in this bitset.
Returns: *this
Throws: nothing

dynamic_bitset operator~() const

Returns: a copy of *this with all of its bits flipped.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset& reset()

Effects: Clears every bit in this bitset.
Returns: *this
Throws: nothing

dynamic_bitset& set(size_type n, bool val = true)

Precondition: n < this->size()
Effects: Sets bit n if val is true, and clears bit n if val is false.
Returns: *this

dynamic_bitset& reset(size_type n)

Precondition: n < this->size()
Effects: Clears bit n
Returns: *this

dynamic_bitset& flip(size_type n)

Precondition: n < this->size()
Effects: Flips bit n.
Returns: *this

size_type size() const

Returns: the number of bits in this bitset.
Throws: nothing

size_type num_blocks() const

Returns: the number of blocks in this bitset.
Throws: nothing.

size_type max_size() const;

Returns: the maximum size of a dynamic_bitset object having the same type as *this.

Note: If any dynamic_bitset operation causes size() to exceed max_size() then the behavior is undefined.

[The semantics of this function could change slightly when lib issue 197 will be closed - G.P.S.]

bool empty() const;

Returns: true if this->size() == 0, false otherwise.
Note: Do not confuse with none(), it has different semantics.

size_type count() const

Returns: the number of bits in this bitset that are set.
Throws: nothing

bool any() const

Returns: True if any bits in this bitset are set, and otherwise returns false.
Throws: nothing

bool none() const

Returns: True if no bits are set, and otherwise returns false.
Throws: nothing

bool test(size_type n) const

Precondition: n < this->size()
Returns: True if bit n is set and false is bit n is 0.

reference operator[](size_type n)

Precondition: n < this->size()
Returns: A reference to bit n.

Note: Reference is a proxy class with an assignment operator and a conversion to bool, which allows to use operator[] for assignment. That is, the user can write both x = b[n] and b[n] = x. However, in many other respects the proxy is not the same as the true reference type bool&.

bool operator[](size_type n) const

Precondition: n < this->size()
Returns: The same as test(n).

unsigned long to_ulong() const

Returns: The numeric value corresponding to the bits in *this.
Throws: std::overflow_error if that value is too large to be represented in an unsigned long, i.e. if *this has any non-zero bit at a position >= std::numeric_limits<unsigned long>::digits.

bool is_subset_of(const dynamic_bitset& a) const

Requires: this->size() == a.size()
Returns: True if this bitset is a subset of bitset a. That is, it returns true if, for every bit that is set in this bitset, the corresponding bit in bitseta is also set. Otherwise this function returns false.
Throws: nothing

bool is_proper_subset_of(const dynamic_bitset& a) const

Requires: this->size() == a.size()
Returns: True if this bitset is a proper subset of bitset a. That is, it returns true if, for every bit that is set in this bitset , the corresponding bit in bitset a is also set and if this->count() < a.count(). Otherwise this function returns false.
Throws: nothing

size_type find_first() const;

Returns: the lowest index i such as bit i is set, or npos if *this has no on bits.

size_type find_next(size_type pos) const;

Returns: the lowest index i greater than pos such as bit i is set, or npos if no such index exists.

bool operator==(const dynamic_bitset& rhs) const

Returns: True if this->size() == rhs.size() and if for all i in the range [0,rhs.size()), (*this)[i] == rhs[i]. Otherwise returns false.
Throws: nothing
(Required by Equality Comparable.)

bool operator!=(const dynamic_bitset& rhs) const

Returns: !((*this) == rhs)
Throws: nothing
(Required by Equality Comparable.)

bool operator<(const dynamic_bitset& rhs) const

Returns: True if this bitset is lexicographically less than rhs, and returns false otherwise. (See the description of lexicographical_compare for a definition of lexicographic ordering).
Throws: nothing
(Required by Less Than Comparable.)

bool operator>(const dynamic_bitset& rhs) const

Returns: !((*this) < rhs || (*this) == rhs)
Throws: nothing
(Required by Less Than Comparable.)

bool operator<=(const dynamic_bitset& rhs) const

Returns: (*this) < rhs || (*this) == rhs
Throws: nothing
(Required by Less Than Comparable.)

bool operator>=(const dynamic_bitset& rhs) const

Returns: (*this) > rhs || (*this) == rhs
Throws: nothing
(Required by Less Than Comparable.)

Non-Member Functions

dynamic_bitset operator&(const dynamic_bitset& a, const dynamic_bitset& b)

Requires: a.size() == b.size()
Returns: A new bitset that is the bitwise-AND of the bitsets a and b.
Note: The expression b1 & b2 is equivalent to creating a temporary copy of b1, using operator&=, and returning the temporary copy.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset operator|(const dynamic_bitset& a, const dynamic_bitset& b)

Requires: a.size() == b.size()
Returns: A new bitset that is the bitwise-OR of the bitsets a and b.
Note: The expression b1 & b2 is equivalent to creating a temporary copy of b1, using operator&=, and returning the temporary copy.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset operator^(const dynamic_bitset& a, const dynamic_bitset& b)

Requires: a.size() == b.size()
Returns: A new bitset that is the bitwise-XOR of the bitsets a and b.
Note:The expression b1 & b2 is equivalent to creating a temporary copy of b1, using operator&=, and returning the temporary copy.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

dynamic_bitset operator-(const dynamic_bitset& a, const dynamic_bitset& b)

Requires: a.size() == b.size()
Returns: A new bitset that is the set difference of the bitsets a and b.
Note: The expression b1 - b2 is equivalent to creating a temporary copy of b1, using operator-=, and returning the temporary copy.
Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator).

template <typename CharT, typename Alloc>
void to_string(const dynamic_bitset<Block, Allocator>& b,
               std::basic_string<Char,Traits,Alloc>& s)

Effects: Copies a representation of b into the string s. A character in the string is '1' if the corresponding bit is set, and '0' if it is not. Character position i in the string corresponds to bit position b.size() - 1 - i.
Throws: The string throws an allocation error if memory is exhausted.
Rationale: This function is not a member function taking zero arguments and returning a string for a couple reasons. First, this version is more efficient as, the string is not copied (due to being passed by value). Second, as a member function, to allow for flexibility with regards to the template parameters of basic_string, the member function would require explicit template parameters. Few C++ programmers are familiar with explicit template parameters, and some C++ compilers do not handle them properly.

template <typename Block, typename Alloc, typename BlockOutputIterator>
void to_block_range(const dynamic_bitset<Block, Alloc>& b, BlockOutputIterator result)

Effects: Writes the bits of the bitset into the iterator result a block at a time. The first block written represents the bits in the position range [0,bits_per_block) in the bitset , the second block written the bits in the range [bits_pre_block,2*bits_per_block), and so on. For each block bval written, the bit (bval >> i) & 1 corresponds to the bit at position (b * bits_per_block + i) in the bitset.
Requires: The type BlockOutputIterator must be a model of Output Iterator and its value_type must be the same type as Block. Further, the size of the output range must be greater or equal b.num_blocks().

template <typename BlockIterator, typename Block, typename Alloc>
void from_block_range(BlockIterator first,
    BlockIterator last, const dynamic_bitset<Block, Alloc>& b)

Effects: Reads blocks from the iterator range into the bitset.
Requires: The type BlockIterator must be a model of Input Iterator and its value_type must be the same type as Block. The size of the iterator range must be less or equal to b.num_blocks().

template <typename Char, typename Traits, typename Block, typename Alloc>
basic_ostream<Char, Traits>&
operator<<(basic_ostream<Char, Traits>& os, const dynamic_bitset<Block, Alloc>& b)

Effects: Inserts a textual representation of b into the stream os (highest bit first). Informally, the output is the same as doing

std::basic_string<Char, Traits> s;
boost::to_string(x, s):
os << s;

except, that the stream inserter takes into account the locale imbued into os, which boost::to_string() can't do. Here is a more precise specification, given in terms of "as if" rule:

  • For each valid position i into the bitset b let's put: character_of(b[i)]) = b[i]? os.widen('1') : os.widen('0');

  • Also let, s be a std::basic_string<Char, Traits> object, having length b.size() and such as, for each i in [0, b.size()), s[i] character_of(b[i])

  • Then, the output, the effects on os and the exception behavior is the same as outputting the object s to os (same width, same exception mask, same padding, same setstate() logic)
    Returns:     os
    Throws:     std::ios_base::failure if there is a problem writing to the stream.

template <typename Char, typename Traits, typename Block, typename Alloc>
std::basic_istream<Char,Traits>&
operator>>(std::basic_istream<Char,Traits>& is, dynamic_bitset<Block, Alloc>& b)

Effects: Extracts a dynamic_bitset from an input stream.

Definitions:

Let Tr be the traits_type of is. Then:

  1. A (non-eof) character c extracted from is is a bitset digit if and only if either Tr::eq(c, is.widen('0')) or Tr::eq(c, is.widen('1')) return true.

  2. If c is a bitset digit, it's corresponding bit value is 0 if Tr::eq(c, is.widen('0')) is true, 1 otherwise.

The function begins by constructing a sentry object k as if k were constructed by typename std::basic_istream<Char, Traits>::sentry k(is). If bool(k) is true, it calls b.clear() then attempts to extract characters from is. For each character c that is a bitset digit the corresponding bit value is appended to the less significant end of b (appending may throw - gps ). If is.width() is greater than zero and smaller than b.max_size() then the maximum number n of bits appended is is.width(); otherwise n = b.max_size(). Unless the extractor is exited via an exception, characters are extracted (and corresponding bits appended) until any of the following occurs:

  • n bits are stored into the bitset;

  • end-of-file, or an error, occurs on the input sequence;

  • the next available input character isn't a bitset digit

If no exception caused the function to exit then is.width(0) is called, regardless of how many characters were actually extracted. The sentry object k is destroyed.

If the function extracts no characters[???], it calls is.setstate(std::ios::failbit), which may throw std::ios_base::failure.

Throws: An allocation error if memory is exhausted (std::bad_alloc if Allocator=std::allocator). A std::ios_base::failure if there is a problem reading from the stream.

Exception guarantees

All of dynamic_bitset functions offer at least the basic guarantee. In addition some functions offer the strong or the nothrow guarantee as summarized in the table below (the destructor, as usual, does not throw):

- | strong | nothrow
f |  | x

Changes from previous version(s)

Changes from Boost 1.31.0

  • The stream extractor has completely different semantics: as natural for a dynamic structure, it expands the bitset as needed during extraction. The new behavior mimics that of the basic_string extractor but there are some differences the user should be aware of; look at the documentation. (One of the differences concern the case where stream.width() > bitset.max_size() > 0; in that circumstance the extractor of dynamic_bitset never attempts to extract more than max_size() characters, whereas the extractor of basic_string goes on and, on conforming implementations, eventually throws a length_error. Note: That's what the standard mandates -see especially library issue 83- but not all implementations conform.

    The stream extractor is now also 'exception-aware' in the sense that it works correctly when setting exception masks on the stream.

  • Several member functions (empty(), find_first()
, find_next(), get_allocator(), intersects()
, max_size() )
have been added.

  • The constructor from basic_string has a new parameter that was totally forgotten before.

Technicalities and minor changes

  • The class reference has been reimplemented so that dynamic_bitset's references behave more like references to standard container elements. In particular it is now guaranteed that they cannot be invalidated from a standard library swap() function applied to their corresponding dynamic_bitset.

General improvements

Several optimizations to member and non-member functions and to the nested class reference.

See also

std::bitset, std::vector.

Copyright © 2001Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Chuck Allison, Senior Editor, C/C++ Users Journal (cda@freshsources.com)
Copyright © 2003-2004 Gennaro Prota


Error and Exception Handling

References

The following paper is a good introduction to some of the issues of writing robust generic components:

D. Abrahams: ``Exception Safety in Generic Components'', originally published in M. Jazayeri, R. Loos, D. Musser (eds.): Generic Programming, Proc. of a Dagstuhl Seminar, Lecture Notes on Computer Science. Volume. 1766

Guidelines

When should I use exceptions?

The simple answer is: ``whenever the semantic and performance characteristics of exceptions are appropriate.''

An oft-cited guideline is to ask yourself the question ``is this an exceptional (or unexpected) situation?'' This guideline has an attractive ring to it, but is usually a mistake. The problem is that one person's ``exceptional'' is another's ``expected'': when you really look at the terms carefully, the distinction evaporates and you're left with no guideline. After all, if you check for an error condition, then in some sense you expect it to happen, or the check is wasted code.

A more appropriate question to ask is: ``do we want stack unwinding here?'' Because actually handling an exception is likely to be significantly slower than executing mainline code, you should also ask: ``Can I afford stack unwinding here?'' For example, a desktop application performing a long computation might periodically check to see whether the user had pressed a cancel button. Throwing an exception could allow the operation to be cancelled gracefully. On the other hand, it would probably be inappropriate to throw and handle exceptions in the inner loop of this computation because that could have a significant performance impact. The guideline mentioned above has a grain of truth in it: in time critical code, throwing an exception should be the exception, not the rule.

How should I design my exception classes?

  1. Derive your exception class from std::exception. Except in *very* rare circumstances where you can't afford the cost of a virtual table, std::exception makes a reasonable exception base class, and when used universally, allows programmers to catch "everything" without resorting to catch(...). For more about catch(...), see below.

  2. Use virtual inheritance. This insight is due to Andrew Koenig. Using virtual inheritance from your exception's base class(es) prevents ambiguity problems at the catch-site in case someone throws an exception derived from multiple bases which have a base class in common:

    #include <iostream>
struct my_exc1 : std::exception { char const* what() const throw(); };
struct my_exc2 : std::exception { char const* what() const throw(); };
struct your_exc3 : my_exc1, my_exc2 {};

int main()
{
   try { throw your_exc3(); }
   catch(std::exception const& e) {}
   catch(...) { std::cout << "whoops!" << std::endl; }
}

    The program above prints "whoops" because the C++ runtime can't resolve which exception instance to match in the first catch clause.

  3. Don't embed a std::string object or any other data member or base class whose copy constructor could throw an exception. That could lead directly to std::terminate() at the throw point. Similarly, it's a bad idea to use a base or member whose ordinary constructor(s) might throw, because, though not necessarily fatal to your program, you may report a different exception than intended from a throw-expression that includes construction such as:

    throw some_exception();

    There are various ways to avoid copying string objects when exceptions are copied, including embedding a fixed-length buffer in the exception object, or managing strings via reference-counting. However, consider the next point before pursuing either of these approaches.

  4. Format the what() message on demand, if you feel you really must format the message. Formatting an exception error message is typically a memory-intensive operation that could potentially throw an exception. This is an operation best delayed until after stack unwinding has occurred, and presumably, released some resources. It's a good idea in this case to protect your what() function with a catch(...) block so that you have a fallback in case the formatting code throws

  5. Don't worry too much about the what() message. It's nice to have a message that a programmer stands a chance of figuring out, but you're very unlikely to be able to compose a relevant and user-comprehensible error message at the point an exception is thrown. Certainly, internationalization is beyond the scope of the exception class author. Peter Dimov makes an excellent argument that the proper use of a what() string is to serve as a key into a table of error message formatters. Now if only we could get standardized what() strings for exceptions thrown by the standard library...

  6. Expose relevant information about the cause of the error in your exception class' public interface. A fixation on the what() message is likely to mean that you neglect to expose information someone might need in order to make a coherent message for users. For example, if your exception reports a numeric range error, it's important to have the actual numbers involved available as numbers in the exception class' public interface where error reporting code can do something intelligent with them. If you only expose a textual representation of those numbers in the what() string, you will make life very difficult for programmers who need to do something more (e.g. subtraction) with them than dumb output.

  7. Make your exception class immune to double-destruction if possible. Unfortunately, several popular compilers occasionally cause exception objects to be destroyed twice. If you can arrange for that to be harmless (e.g. by zeroing deleted pointers) your code will be more robust.

What About Programmer Errors?

As a developer, if I have violated a precondition of a library I'm using, I don't want stack unwinding. What I want is a core dump or the equivalent - a way to inspect the state of the program at the exact point where the problem was detected. That usually means assert() or something like it.

Sometimes it is necessary to have resilient APIs which can stand up to nearly any kind of client abuse, but there is usually a significant cost to this approach. For example, it usually requires that each object used by a client be tracked so that it can be checked for validity. If you need that sort of protection, it can usually be provided as a layer on top of a simpler API. Beware half-measures, though. An API which promises resilience against some, but not all abuse is an invitation to disaster. Clients will begin to rely on the protection and their expectations will grow to cover unprotected parts of the interface.

Note for Windows developers: unfortunately, the native exception-handling used by most Windows compilers actually throws an exception when you use assert(). Actually, this is true of other programmer errors such as segmentation faults and divide-by-zero errors. One problem with this is that if you use JIT (Just In Time) debugging, there will be collateral exception-unwinding before the debugger comes up because catch(...) will catch these not-really-C++ exceptions. Fortunately, there is a simple but little-known workaround, which is to use the following incantation:

extern "C" void straight_to_debugger(unsigned int, EXCEPTION_POINTERS*)
{
    throw;
}
extern "C" void (*old_translator)(unsigned, EXCEPTION_POINTERS*)
         = _set_se_translator(straight_to_debugger);

This technique doesn't work if the SEH is raised from within a catch block (or a function called from within a catch block), but it still eliminates the vast majority of JIT-masking problems.

How should I handle exceptions?

Often the best way to deal with exceptions is to not handle them at all. If you can let them pass through your code and allow destructors to handle cleanup, your code will be cleaner.

Avoid catch(...) when possible

Unfortunately, operating systems other than Windows also wind non-C++ "exceptions" (such as thread cancellation) into the C++ EH machinery, and there is sometimes no workaround corresponding to the _set_se_translator hack described above. The result is that catch(...) can have the effect of making some unexpected system notification at a point where recovery is impossible look just like a C++ exception thrown from a reasonable place, invalidating the usual safe assumptions that destructors and catch blocks have taken valid steps to ensure program invariants during unwinding.

I reluctantly concede this point to Hillel Y. Sims, after many long debates in the newsgroups: until all OSes are "fixed", if every exception were derived from std::exception and everyone substituted catch(std::exception&) for catch(...), the world would be a better place.

Sometimes, catch(...), is still the most appropriate pattern, in spite of bad interactions with OS/platform design choices. If you have no idea what kind of exception might be thrown and you really must stop unwinding it's probably still your best bet. One obvious place where this occurs is at language boundaries.

© Copyright David Abrahams 2001-2003. All rights reserved.


Exception-Safety in Generic Components

Abstract. This paper represents the knowledge accumulated in response to a real-world need: that the C++ Standard Template Library exhibits useful and well-defined interactions with exceptions, the error-handling mechanism built-in to the core C++ language. It explores the meaning of exception-safety, reveals surprising myths about exceptions and genericity, describes valuable tools for reasoning about program correctness, and outlines an automated testing procedure for verifying exception-safety.

Keywords: exception-safety, exceptions, STL, C++

1 What is exception-safety?

Informally, exception-safety in a component means that it exhibits reasonable behavior when an exception is thrown during its execution. For most people, the term ``reasonable'' includes all the usual expectations for error-handling: that resources should not be leaked, and that the program should remain in a well-defined state so that execution can continue. For most components, it also includes the expectation that when an error is encountered, it is reported to the caller.

More formally, describe a component as minimally exception-safe if, its variants are intact when exceptions are thrown from within that component. There are at least three different levels of exception-safety that can be usefully distinguished. These distinctions can help to describe and reason about the behavior of large systems.

In a generic component, there is an additional expectation of exception-neutrality, which means that exceptions thrown by a component's type parameters should be propagated, unchanged, to the component's caller.

2 Myths and Superstitions

Exception-safety seems straightforward so far: it doesn't constitute anything more than expected from code using more traditional error-handling techniques. It might be worthwhile, however, to examine the term from a psychological viewpoint. Nobody ever spoke of ``error-safety'' before C++ had exceptions.

It's almost as though exceptions are viewed as a mysterious attack on otherwise correct code. This doesn't lead to a healthy relationship with error handling! During standardization, a democratic process which requires broad support for changes, the user encounters many widely-held superstitions. In order to even begin the discussion of exception-safety in generic components, it may be worthwhile confronting a few of them.

``Interactions between templates and exceptions are not well-understood.'' This myth, often heard from those who consider these both new language features, is easily disposed of: there simply are no interactions. A template, once instantiated, works in all respects like an ordinary class or function. A simple way to reason about the behavior of a template with exceptions is to think of how a specific instantiation of that template works. Finally, the genericity of templates should not cause special concern. Although the component's client supplies part of the operation (which may, unless otherwise specified, throw arbitrary exceptions), the same is true of operations using familiar virtual functions or simple function pointers.

``It is well known to be impossible to write an exception-safe generic container.'' This claim is often heard with reference to an article by Tom Cargill [4] in which he explores the problem of exception-safety for a generic stack template. In his article, Cargill raises many useful questions, but unfortunately fails to present a solution to his problem.1 The author concludes by suggesting that a solution may not be possible. Unfortunately, his article was read by many as ``proof'' of that speculation. Since it was published there have been many examples of exception-safe generic components, among them the C++ standard library containers.

``Dealing with exceptions will slow code down, and templates are used specifically to get the best possible performance.'' A good implementation of C++ will not devote a single instruction cycle to dealing with exceptions until one is thrown, and then it can be handled at a speed comparable with that of calling a function [7]. That alone gives programs using exceptions performance equivalent to that of a program which ignores the possibility of errors. Using exceptions can actually result in faster programs than ``traditional'' error handling methods for other reasons. First, a catch block clearly indicates to the compiler which code is devoted to error-handling; it can then be separated from the usual execution path, improving locality of reference. Second, code using ``traditional'' error handling must typically test a return value for errors after every single function call; using exceptions completely eliminates that overhead.

``Exceptions make it more difficult to reason about a program's behavior.'' Usually cited in support of this myth is the way ``hidden'' execution paths are followed during stack-unwinding. Hidden execution paths are nothing new to any C++ programmer who expects local variables to be destroyed upon returning from a function:

ErrorCode f( int& result )         // 1
{                                  // 2
    X x;                           // 3
    ErrorCode err = x.g( result ); // 4
    if ( err != kNoError )         // 5
        return err;                // 6
    // ...More code here...
    return kNoError;               // 7
}

In the example above, there is a ``hidden'' call to X::~X() in lines 6 and 7. Granted, using exceptions, there is no code devoted to error handling visible:

int f()                 // 1
{                       // 2
    X x;                // 3
    int result = x.g(); // 4
    // ...More code here...
    return result;      // 5
}

For many programmers more familiar with exceptions, the second example is actually more readable and understandable than the first. The ``hidden'' code paths include the same calls to destructors of local variables. In addition, they follow a simple pattern which acts exactly as though there were a potential return statement after each function call in case of an exception. Readability is enhanced because the normal path of execution is unobscured by error-handling, and return values are freed up to be used in a natural way.

There is an even more important way in which exceptions can enhance correctness: by allowing simple class invariants. In the first example, if x's constructor should need to allocate resources, it has no way to report a failure: in C++, constructors have no return values. The usual result when exceptions are avoided is that classes requiring resources must include a separate initializer function which finishes the job of construction. The programmer can therefore never be sure, when an object of class X is used, whether he is handling a full-fledged X or some abortive attempt to construct one (or worse: someone simply forgot to call the initializer!)

3 A contractual basis for exception-safety

A non-generic component can be described as exception-safe in isolation, but because of its configurability by client code, exception-safety in a generic component usually depends on a contract between the component and its clients. For example, the designer of a generic component might require that an operation which is used in the component's destructor not throw any exceptions.2 The generic component might, in return, provide one of the following guarantees:

  • The basic guarantee: that the invariants of the component are preserved, and no resources are leaked.
  • The strong guarantee: that the operation has either completed successfully or thrown an exception, leaving the program state exactly as it was before the operation started.
  • The no-throw guarantee: that the operation will not throw an exception.

The basic guarantee is a simple minimum standard for exception-safety to which we can hold all components. It says simply that after an exception, the component can still be used as before. Importantly, the preservation of invariants allows the component to be destroyed, potentially as part of stack-unwinding. This guarantee is actually less useful than it might at first appear. If a component has many valid states, after an exception we have no idea what state the component is in|only that the state is valid. The options for recovery in this case are limited: either destruction or resetting the component to some known state before further use. Consider the following example:

template <class X>
void print_random_sequence()
{
    std::vector<X> v(10); // A vector of 10 items
    try {
        // Provides only the basic guarantee
        v.insert( v.begin(), X() );
    }
    catch(...) {} // ignore any exceptions above
    // print the vector's contents
    std::cout "(" << v.size() << ") ";
    std::copy( v.begin(), v.end(),
    std::ostream_iterator<X>( std::cout, " " ) );
}

Since all we know about v after an exception is that it is valid, the function is allowed to print any random sequence of Xs.3 It is ``safe'' in the sense that it is not allowed to crash, but its output may be unpredictable.

The strong guarantee provides full ``commit-or-rollback'' semantics. In the case of C++ standard containers, this means, for example, that if an exception is thrown all iterators remain valid. We also know that the container has exactly the same elements as before the exception was thrown. A transaction that has no effects if it fails has obvious benefits: the program state is simple and predictable in case of an exception. In the C++ standard library, nearly all of the operations on the node-based containers list, set, multiset, map, and multimap provide the strong guarantee.4).

The no-throw guarantee is the strongest of all, and it says that an operation is guaranteed not to throw an exception: it always completes successfully. This guarantee is necessary for most destructors, and indeed the destructors of C++ standard library components are all guaranteed not to throw exceptions. The no-throw guarantee turns out to be important for other reasons, as we shall see.5

4 Legal Wrangling

Inevitably, the contract can get more complicated: a quid pro quo arrangement is possible. Some components in the C++ Standard Library give one guarantee for arbitrary type parameters, but give a stronger guarantee in exchange for additional promises from the client type that no exceptions will be thrown. For example, the standard container operation vector<T>::erase gives the basic guarantee for any T, but for types whose copy constructor and copy assignment operator do not throw, it gives the no-throw guarantee.6

5 What level of exception-safety should a component specify?

From a client's point-of-view, the strongest possible level of safety would be ideal. Of course, the no-throw guarantee is simply impossible for many operations, but what about the strong guarantee? For example, suppose we wanted atomic behavior for vector<T>::insert. Insertion into the middle of a vector requires copying elements after the insertion point into later positions, to make room for the new element. If copying an element can fail, rolling back the operation would require ``undoing'' the previous copies...which depends on copying again. If copying back should fail (as it likely would), we have failed to meet our guarantee.

One possible alternative would be to redefine insert to build the new array contents in a fresh piece of memory each time, and only destroy the old contents when that has succeeded. Unfortunately, there is a non-trivial cost if this approach is followed: insertions near the end of a vector which might have previously caused only a few copies would now cause every element to be copied. The basic guarantee is a ``natural'' level of safety for this operation, which it can provide without violating its performance guarantees. In fact all of the operations in the library appear to have such a ``natural'' level of safety.

Because performance requirements were already a well-established part of the draft standard and because performance is a primary goal of the STL, there was no attempt to specify more safety than could be provided within those requirements. Although not all of the library gives the strong guarantee, almost any operation on a standard container which gives the basic guarantee can be made strong using the ``make a new copy'' strategy described above:

template <class Container, class BasicOp>
void MakeOperationStrong( Container& c, const BasicOp& op )
{
    Container tmp(c); // Copy c
    op(tmp); // Work on the copy
    c.swap(tmp); // Cannot fail7
}

This technique can be folded into a wrapper class to make a similar container which provides stronger guarantees (and different performance characteristics).8

6 Should we take everything we can get?

By considering a particular implementation, we can hope to discern a natural level of safety. The danger in using this to establish requirements for a component is that the implementation might be restricted. If someone should come up with a more-efficient implementation which we'd like to use, we may find that it's incompatible with our exception-safety requirements. One might expect this to be of no concern in the well-explored domains of data structures and algorithms covered by the STL, but even there, advances are being made. A good example is the recent introsort algorithm [6], which represents a substantial improvement in worst-case complexity over the well-established quicksort.

To determine exactly how much to demand of the standard components, I looked at a typical real-world scenario. The chosen test case was a ``composite container.'' Such a container, built of two or more standard container components, is not only commonly needed, but serves as a simple representative case for maintaining invariants in larger systems:

// SearchableStack - A stack which can be efficiently searched
// for any value.
template <class T>
class SearchableStack
{
 public:
    void push(const T& t); // O(log n)
    void pop(); // O(log n)
    bool contains(const T& t) const; // O(log n)
    const T& top() const; // O(1)
 private:
    std::set<T> set_impl;
    std::list<std::set<T>::iterator> list_impl;
};

The idea is that the list acts as a stack of set iterators: every element goes into the set first, and the resulting position is pushed onto the list. The invariant is straightforward: the set and the list should always have the same number of elements, and every element of the set should be referenced by an element of the list. The following implementation of the push function is designed to give the strong guarantee within the natural levels of safety provided by set and list:

template <class T>                                // 1
void SearchableStack<T>::push(const T& t)         // 2
{                                                       // 3
    set<T>::iterator i = set_impl.insert(t);      // 4
    try                                                 // 5
    {                                                   // 6
        list_impl.push_back(i);                         // 7
    }                                                   // 8
    catch(...)                                          // 9
    {                                                   // 10
        set_impl.erase(i);                              // 11
        throw;                                          // 12
    }                                                   // 13
}                                                       // 14

What does our code actually require of the library? We need to examine the lines where non-const operations occur:

  • Line 4: if the insertion fails but set_impl is modified in the process, our invariant is violated. We need to be able to rely on the strong guarantee from set<T>::insert.
  • Line 7: likewise, if push_back fails, but list_impl is modified in the process, our invariant is violated, so we need to be able to rely on the strong guarantee from list<T>::insert.
  • Line 11: here we are ``rolling back'' the insertion on line 4. If this operation should fail, we will be unable to restore our invariant. We absolutely depend on the no-throw guarantee from set<T>::erase.9
  • Line 11: for the same reasons, we also depend on being able to pass the i to the erase function: we need the no-throw guarantee from the copy constructor of set<T>::iterator.

I learned a great deal by approaching the question this way during standardization. First, the guarantee specified for the composite container actually depends on stronger guarantees from its components (the no-throw guarantees in line 11). Also, I took advantage of all of the natural level of safety to implement this simple example. Finally, the analysis revealed a requirement on iterators which I had previously overlooked when operations were considered on their own. The conclusion was that we should provide as much of the natural level of safety as possible. Faster but less-safe implementations could always be provided as extensions to the standard components. 10

7 Automated testing for exception-safety

As part of the standardization process, I produced an exception-safe reference implementation of the STL. Error-handling code is seldom rigorously tested in real life, in part because it is difficult to cause error conditions to occur. It is very common to see error-handling code which crashes the first time it is executed ...in a shipping product! To bolster confidence that the implementation actually worked as advertised, I designed an automated test suite, based on an exhaustive technique due to my colleague Matt Arnold.

The test program started with the basics: reinforcement and instrumentation, especially of the global operators new and delete.11Instances of the components (containers and algorithms) were created, with type parameters chosen to reveal as many potential problems as possible. For example, all type parameters were given a pointer to heap-allocated memory, so that leaking a contained object would be detected as a memory leak.

Finally, a scheme was designed that could cause an operation to throw an exception at each possible point of failure. At the beginning of every client-supplied operation which is allowed to throw an exception, a call to ThisCanThrow was added. A call to ThisCanThrow also had to be added everywhere that the generic operation being tested might throw an exception, for example in the global operator new, for which an instrumented replacement was supplied.

// Use this as a type parameter, e.g. vector<TestClass>
struct TestClass
{
    TestClass( int v = 0 )
        : p( ThisCanThrow(), new int( v ) ) {}
    TestClass( const TestClass& rhs )
        : p( ThisCanThrow(), new int( *rhs.p ) ) {}
    const TestClass& operator=( const TestClass& rhs )
        { ThisCanThrow(); *p = *rhs.p; }
    bool operator==( const TestClass& rhs ) const
        { ThisCanThrow(); return *p == *rhs.p; }
    ...etc...
    ~TestClass() { delete p; }
};

ThisCanThrow simply decrements a ``throw counter'' and, if it has reached zero, throws an exception. Each test takes a form which begins the counter at successively higher values in an outer loop and repeatedly attempts to complete the operation being tested. The result is that the operation throws an exception at each successive step along its execution path that can possibly fail. For example, here is a simplified version of the function used to test the strong guarantee: 12

extern int gThrowCounter; // The throw counter
void ThisCanThrow()
{
    if (gThrowCounter-- == 0)
        throw 0;
}

template <class Value, class Operation>
void StrongCheck(const Value& v, const Operation& op)
{
    bool succeeded = false;
    for (long nextThrowCount = 0; !succeeded; ++nextThrowCount)
    {
        Value duplicate = v;
        try
        {
            gThrowCounter = nextThrowCount;
            op( duplicate ); // Try the operation
            succeeded = true;
        }
        catch(...) // Catch all exceptions
        {
            bool unchanged = duplicate == v; // Test strong guarantee
            assert( unchanged );
        }
        // Specialize as desired for each container type, to check
        // integrity. For example, size() == distance(begin(),end())
        CheckInvariant(v); // Check any invariant
    }
}

Notably, this kind of testing is much easier and less intrusive with a generic component than with non-generics, because testing-specific type parameters can be used without modifying the source code of the component being tested. Also, generic functions like StrongCheck above were instrumental in performing the tests on a wide range of values and operations.

8 Further Reading

To my knowledge, there are currently only two descriptions of STL exception-safety available. The original specification [2] for the reference exception-safe implementation of the STL is an informal specification, simple and self-explanatory (also verbose), and uses the basic- and strong-guarantee distinctions outlined in this article. It explicitly forbids leaks, and differs substantively from the final C++ standard in the guarantees it makes, though they are largely identical. I hope to produce an updated version of this document soon.

The description of exception-safety in the C++ Standard [1] is only slightly more formal, but relies on hard-to-read ``standardese'' and an occasionally subtle web of implication.13 In particular, leaks are not treated directly at all. It does have the advantage that it is the standard.

The original reference implementation [5] of the exception-safe STL is an adaptation of an old version of the SGI STL, designed for C++ compilers with limited features. Although it is not a complete STL implementation, the code may be easier to read, and it illustrates a useful base-class technique for eliminating exception-handling code in constructors. The full test suite [3] used to validate the reference implementation has been used successfully to validate all recent versions of the SGI STL, and has been adapted to test one other vendor's implementation (which failed). As noted on the documentation page, it also seems to have the power to reveal hidden compiler bugs, particularly where optimizers interact with exception-handling code.

References

  1. International Standard ISO/IEC 14882, Information Technology-Programming Languages-C++, Document Number ISO/IEC 14882-1998, available from http://webstore.ansi.org/ansidocstore/default.asp.
  2. D. Abrahams, Exception Safety in STLport, available at http://www.stlport.org/doc/exception_safety.html.
  3. D. Abrahams and B. Fomitchev, Exception Handling Test Suite, available at http://www.stlport.org/doc/eh_testsuite.html.
  4. Tom Cargill, ``Exception Handling: A False Sense of Security,'' C++ Report, Nov-Dec 1994, also available at http://www.awprofessional.com/content/images/020163371x/supplements/Exception_Handling_Article.html.
  5. B. Fomitchev, Adapted SGI STL Version 1.0, with exception handling code by D. Abrahams, available at http://www.stlport.org.
  6. D. R. Musser, ``Introspective Sorting and Selection Algorithms,'' Software-Practice and Experience 27(8):983-993, 1997.
  7. Bjarne Stroustrup, The Design And Evolution of C++. Addison Wesley, Reading, MA, 1995, ISBN 0-201-54330-3, Section 16.9.1.

Footnotes

1 Probably the greatest impediment to a solution in Cargill's case was an unfortunate combination of choices on his part: the interface he chose for his container was incompatible with his particular demands for safety. By changing either one he might have solved the problem.

2 It is usually inadvisable to throw an exception from a destructor in C++, since the destructor may itself be called during the stack-unwinding caused by another exception. If the second exception is allowed to propagate beyond the destructor, the program is immediately terminated.

3 In practice of course, this function would make an extremely poor random sequence generator!

4 It is worth noting that mutating algorithms usually cannot provide the strong guarantee: to roll back a modified element of a range, it must be set back to its previous value using operator=, which itself might throw. In the C++ standard library, there are a few exceptions to this rule, whose rollback behavior consists only of destruction: uninitialized_copy, uninitialized_fill, and uninitialized_fill_n.

5 All type parameters supplied by clients of the C++ standard library are required not to throw from their destructors. In return, all components of the C++ standard library provide at least the basic guarantee.

6 Similar arrangements might have been made in the C++ standard for many of the mutating algorithms, but were never considered due to time constraints on the standardization process.

7 Associative containers whose Compare object might throw an exception when copied cannot use this technique, since the swap function might fail.

8 This suggests another potential use for the oft-wished-for but as yet unseen container_traits<> template: automated container selection to meet exception-safety constraints.

9 One might be tempted to surround the erase operation with a try/catch block to reduce the requirements on set<T> and the problems that arise in case of an exception, but in the end that just begs the question. First, erase just failed and in this case there are no viable alternative ways to produce the necessary result. Second and more generally, because of the variability of its type parameters a generic component can seldom be assured that any alternatives will succeed.

10 The prevalent philosophy in the design of STL was that functionality that wasn't essential to all uses should be left out in favor of efficiency, as long as that functionality could be obtained when needed by adapting the base components. This departs from that philosophy, but it would be difficult or impossible to obtain even the basic guarantee by adapting a base component that doesn't already have it.

11 An excellent discussion on how to fortify memory subsystems can be found in: Steve Maguire, Writing Solid Code, Microsoft Press, Redmond, WA, 1993, ISBN 1-55615- 551-4.

12 Note that this technique requires that the operation being tested be exception-neutral. If the operation ever tries to recover from an exception and proceed, the throw counter will be negative, and subsequent operations that might fail will not be tested for exception-safety.

13 The changes to the draft standard which introduced exception-safety were made late in the process, when amendments were likely to be rejected solely on the basis of the number of altered words. Unfortunately, the result compromises clarity somewhat in favor of brevity. Greg Colvin was responsible for the clever language-lawyering needed to minimize the extent of these changes.


dynamic_bitset<Block, Allocator>

Name

dynamic_bitset: This class represents a set of bits.

Synopsis

namespace boost {

template <typename Block, typename Allocator>
class dynamic_bitset
{
public:
    typedef Block block_type;
    typedef Allocator allocator_type;
    typedef implementation-defined size_type;

    static const int bits_per_block = implementation-defined;
    static const size_type npos = implementation-defined;

    class reference
    {
        void operator&(); // not defined
    public:
        // An automatically generated copy constructor.

        reference& operator=(bool value);
        reference& operator=(const reference& rhs);

        reference& operator|=(bool value);
        reference& operator&=(bool value);
        reference& operator^=(bool value);
        reference& operator-=(bool value);

        bool operator~() const;
        operator bool() const;
        reference& flip();
    };
    typedef bool const_reference;

    explicit dynamic_bitset(const Allocator& alloc = Allocator());

    explicit dynamic_bitset(size_type num_bits, unsigned long value = 0,
                            const Allocator& alloc = Allocator());

    template <typename CharT, typename Traits, typename Alloc>
    explicit dynamic_bitset(const std::basic_string<CharT, Traits, Alloc>& s,
        typename std::basic_string<CharT, Traits, Alloc>::size_type pos = 0,
        typename std::basic_string<CharT, Traits, Alloc>::size_type n = std::basic_string<CharT, Traits, Alloc>::npos,
        const Allocator& alloc = Allocator());

    template <typename BlockInputIterator>
    dynamic_bitset(BlockInputIterator first, BlockInputIterator last,
                   const Allocator& alloc = Allocator());

    dynamic_bitset(const dynamic_bitset& b);

    void swap(dynamic_bitset& b);

    dynamic_bitset& operator=(const dynamic_bitset& b);

    allocator_type get_allocator() const;

    void resize(size_type num_bits, bool value = false);
    void clear();
    void push_back(bool bit);
    void append(Block block);
    template <typename BlockInputIterator>
    void append(BlockInputIterator first, BlockInputIterator last);

    dynamic_bitset& operator&=(const dynamic_bitset& b);
    dynamic_bitset& operator|=(const dynamic_bitset& b);
    dynamic_bitset& operator^=(const dynamic_bitset& b);
    dynamic_bitset& operator-=(const dynamic_bitset& b);
    dynamic_bitset& operator<<=(size_type n);
    dynamic_bitset& operator>>=(size_type n);
    dynamic_bitset operator<<(size_type n) const;
    dynamic_bitset operator>>(size_type n) const;

    dynamic_bitset& set(size_type n, bool val = true);
    dynamic_bitset& set();
    dynamic_bitset& reset(size_type n);
    dynamic_bitset& reset();
    dynamic_bitset& flip(size_type n);
    dynamic_bitset& flip();
    bool test(size_type n) const;
    bool any() const;
    bool none() const;
    dynamic_bitset operator~() const;
    size_type count() const;

    reference operator[](size_type pos);
    bool operator[](size_type pos) const;

    unsigned long to_ulong() const;

    size_type size() const;
    size_type num_blocks() const;
    size_type max_size() const;
    bool empty() const;

    bool is_subset_of(const dynamic_bitset& a) const;
    bool is_proper_subset_of(const dynamic_bitset& a) const;

    size_type find_first() const;
    size_type find_next(size_type pos) const;

};

template <typename B, typename A>
bool operator==(const dynamic_bitset<B, A>& a, const dynamic_bitset<B, A>& b);

template <typename Block, typename Allocator>
bool operator!=(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename B, typename A>
bool operator<(const dynamic_bitset<B, A>& a, const dynamic_bitset<B, A>& b);

template <typename Block, typename Allocator>
bool operator<=(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename Block, typename Allocator>
bool operator>(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename Block, typename Allocator>
bool operator>=(const dynamic_bitset<Block, Allocator>& a, const dynamic_bitset<Block, Allocator>& b);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator&(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator|(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator^(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator>
dynamic_bitset<Block, Allocator>
operator-(const dynamic_bitset<Block, Allocator>& b1, const dynamic_bitset<Block, Allocator>& b2);

template <typename Block, typename Allocator, typename CharT, typename Alloc>
void to_string(const dynamic_bitset<Block, Allocator>& b,
          std::basic_string<CharT, Alloc>& s);

template <typename Block, typename Allocator, typename BlockOutputIterator>
void to_block_range(const dynamic_bitset<Block, Allocator>& b,
                    BlockOutputIterator result);

template <typename CharT, typename Traits, typename Block, typename Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const dynamic_bitset<Block, Allocator>& b);

template <typename CharT, typename Traits, typename Block, typename Allocator>
std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is, dynamic_bitset<Block, Allocator>& b);

} // namespace boost

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Graph Library: Acknowledgements

Acknowledgements

We owe many debts of thanks to a number of individuals who both inspired and encouraged us in developing the Boost Graph Library.

A most profound thanks goes to Alexander Stepanov for his pioneering work in generic programming, for his encouragement, and for his algorithm contributions to the BGL. We thank Matthew Austern for his work on documenting the concepts of STL which provided a foundation for creating the concepts in the BGL. We thank Dietmar Kühl for his work on generic graph algorithms and design patterns; especially for the property map abstraction.

Dave Abrahams, Jens Maurer, Beman Dawes, Gary Powell, Greg Colvin, Valentin Bonnard, and the rest of the group at Boost provided valuable input to the BGL interface, numerous suggestions for improvement, proof reads of the documentation, and help with polishing the code. A special thanks to Dave Abrahams for managing the formal review.

We also thank the following BGL users whose questions helped to improve the BGL: Gordon Woodhull, Dave Longhorn, Joel Phillips, and Edward Luke.

A special thanks to Jeffrey Squyres for editing and proof reading of the documentation.

Our original work on the Boost Graph Library was supported in part by NSF grant ACI-9982205 and by the Director, Office of Science, Division of Mathematical, Information, and Computational Sciences of the U.S. Department of Energy under contract number DE-AC03-76SF00098.

In our work we also used resources of the National Energy Research Scientific Computing Center, which is supported by the Office of Science of the U.S. Department of Energy.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Adjacency Iterator Adaptor Documentation

Adjacency Iterator Adaptor

Defined in header boost/graph/adjacency_iterator.hpp

The adjacency iterator adaptor transforms an out_edge_iterator into an adjacency iterator. That is, it takes an iterator that traverses over edges, and creates an iterator that traverses over the target vertices of those edges. With this adaptor it is trivial to take a graph type that models Incidence Graph and add the capabilities required of Adjacency Graph.

Synopsis

namespace boost {{
  template <class Graph, class VertexDescriptor, class OutEdgeIter>
  class adjacency_iterator_generator {
  public:
    typedef iterator_adaptor<...> type;
  };
}

Example

The following is an example of how to use the adjacency_iterator_generator class.

#include <boost/graph/adjacency_iterator.hpp>

class my_graph {
  // ...
  typedef ... out_edge_iterator;
  typedef ... vertex_descriptor;
  typedef boost::adjacency_iterator_generator<my_graph, vertex_descriptor, out_edge_iterator>::type adjacency_iterator;
  // ...
};

Template Parameters

Parameter | Description
Graph | The graph type, which must model Incidence Graph.
VertexDescriptor This must be the same type as graph_traits<Graph>::vertex_descriptor. The reason why this is a template parameter is that the primary use of adjacency_iterator_generator is inside the definition of the graph class, and in that context we can not use graph_traits on the not yet fully defined graph class.
Default: graph_traits<Graph>::vertex_descriptor
OutEdgeIter This must be the same type as graph_traits<Graph>::out_edge_iterator.
Default: graph_traits<Graph>::out_edge_iterator

Model of

The adjacency iterator adaptor (the type adjacency_iterator_generator<...>::type) is a model of Multi-Pass Input Iterator .

Members

The adjacency iterator type implements the member functions and operators required of the Random Access Iterator concept, except that the reference type is the same as the value_type so operator*() returns by-value. In addition it has the following constructor:

adjacency_iterator_generator::type(const OutEdgeIter& it, const Graph* g))

© Copyright Jeremy Siek 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Boost Graph Library: Adjacency List

Adjacency List

adjacency_list<OutEdgeList, VertexList, Directed,
               VertexProperties, EdgeProperties,
               GraphProperties, EdgeList>

The adjacency_list class implements a generalized adjacency list graph structure. The template parameters provide many configuration options so that the user picks a version of the class that best meets the needs. An adjacency-list is basically a two-dimensional structure, where each element of the first dimension represents a vertex, and each of the vertices contains a one-dimensional structure that is its edge list. Figure 1 shows an adjacency list representation of a directed graph.

Figure 1: Adjacency List Representation of a Directed Graph.

The VertexList template parameter of the adjacency_list class controls what kind of container is used to represent the outer two-dimensional container. The OutEdgeList template parameter controls what kind of container is used to represent the edge lists. The choices for OutEdgeList and VertexList will determine the space complexity of the graph structure, and will determine the time complexity of the various graph operations. The possible choices and tradeoffs are discussed in Section Choosing the Edgelist and VertexList.

The Directed template parameter controls whether the graph is directed, undirected, or directed with access to both the in-edges and out-edges (bidirectional). The bidirectional graph takes up twice the space (per edge) of a directed graph since each edge will appear in both an out-edge and in-edge list. Figure 2 shows an adjacency list representation of an undirected graph.

Figure 2: Adjacency List Representation of an Undirected Graph.

A tutorial on how to use the adjacency_list class is in Section Using adjacency_list.

Example

The code snippet shows how to represent a family tree with a graph.

//=======================================================================
// Copyright 2001 Jeremy G. Siek, Andrew Lumsdaine, Lie-Quan Lee,
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#include <boost/config.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <boost/graph/adjacency_list.hpp>
#include <boost/tuple/tuple.hpp>
enum family
{ Jeanie, Debbie, Rick, John, Amanda, Margaret, Benjamin, N };
int
main()
{
  using namespace boost;
  const char *name[] = { "Jeanie", "Debbie", "Rick", "John", "Amanda",
    "Margaret", "Benjamin"
  };

  adjacency_list <> g(N);
  add_edge(Jeanie, Debbie, g);
  add_edge(Jeanie, Rick, g);
  add_edge(Jeanie, John, g);
  add_edge(Debbie, Amanda, g);
  add_edge(Rick, Margaret, g);
  add_edge(John, Benjamin, g);

  graph_traits < adjacency_list <> >::vertex_iterator i, end;
  graph_traits < adjacency_list <> >::adjacency_iterator ai, a_end;
  property_map < adjacency_list <>, vertex_index_t >::type
    index_map = get(vertex_index, g);

  for (tie(i, end) = vertices(g); i != end; ++i) {
    std::cout << name[get(index_map, *i)];
    tie(ai, a_end) = adjacent_vertices(*i, g);
    if (ai == a_end)
      std::cout << " has no children";
    else
      std::cout << " is the parent of ";
    for (; ai != a_end; ++ai) {
      std::cout << name[get(index_map, *ai)];
      if (boost::next(ai) != a_end)
        std::cout << ", ";
    }
    std::cout << std::endl;
  }
  return EXIT_SUCCESS;
}

Template Parameters

Parameter | Description | Default
OutEdgeList | The selector for the container used to represent the edge-list for each of the vertices. | vecS
VertexList | The selector for the container used to represent the vertex-list of the graph. | vecS
Directed | A selector to choose whether the graph is directed, undirected, or directed with bidirectional edge access (access to both out-edges and in-edges). The options are directedS, undirectedS, and bidirectionalS. | directedS
VertexProperties | for specifying internal property storage. | no_property
EdgeProperties | for specifying internal property storage. | no_property
GraphProperties | for specifying property storage for the graph object. | no_property
EdgeList | The selector for the container used to represent the edge-list for the graph. | listS

Model of

VertexAndEdgeListGraph, MutablePropertyGraph, CopyConstructible, Assignable, and Serializable.

Where Defined

boost/graph/adjacency_list.hpp

Also, the serialization functionality is in boost/graph/adj_list_serialize.hpp.

Vertex and Edge Properties

Use Properties to attach properties such as color, distance, weight, and user-defined properties to the vertices and edges of the graph. The property values can be read from and written to via the property maps provided by the graph. The property maps are obtained via the get(property, g) function. How to use properties is described in Section Internal Properties . The property maps are objects that implement the interface defined in Section Property Map Concepts or may be bundled properties, which have a more succinct syntax. The types of all property values must be Copy Constructible, Assignable, and Default Constructible. The property maps obtained from the adjacency_list class are models of the Lvalue Property Map concept. If the adjacency_list is const, then the property map is constant, otherwise the property map is mutable.

If the VertexList of the graph is vecS, then the graph has built-in vertex indices accessed via the property map for the vertex_index_t property. The indices fall in the range [0, num_vertices(g)) and are contiguous. When a vertex is removed the indices are adjusted so that they retain these properties. Some care must be taken when using these indices to access exterior property storage. The property map for vertex index is a model of Readable Property Map.

Iterator and Descriptor Stability/Invalidation

The user must be careful when changing the structure of a graph (via adding or removing edges). Depending on the type of adjacency_list and on the operation, some of the iterator or descriptor objects that point into the graph may become invalid. For example, the following code will result in undefined (bad) behavior:

  typedef adjacency_list<listS, vecS> Graph; // VertexList=vecS
  Graph G(N);
  // Fill in the graph...

  // Attempt to remove all the vertices. Wrong!
  graph_traits<Graph>::vertex_iterator vi, vi_end;
  for (tie(vi, vi_end) = vertices(G); vi != vi_end; ++vi)
    remove_vertex(*vi, G);

  // Remove all the vertices. This is still wrong!
  graph_traits<Graph>::vertex_iterator vi, vi_end, next;
  tie(vi, vi_end) = vertices(G);
  for (next = vi; vi != vi_end; vi = next) {
    ++next;
    remove_vertex(*vi, G);
  }

This is a problem because remove_vertex() is invoked, which when used with an adjacency_list where VertexList=vecS, invalidates all iterators and descriptors for the graph (such as vi and vi_end), thereby causing trouble in subsequent iterations of the loop.

If a different kind of adjacency_list is used, where VertexList=listS, then the iterators are not invalidated by calling remove_vertex unless the iterator is pointing to the actual vertex that was removed. The following code demonstrates this.

  typedef adjacency_list<listS, listS> Graph; // VertexList=listS
  Graph G(N);
  // Fill in the graph...

  // Attempt to remove all the vertices. Wrong!
  graph_traits<Graph>::vertex_iterator vi, vi_end;
  for (tie(vi, vi_end) = vertices(G); vi != vi_end; ++vi)
    remove_vertex(*vi, G);

  // Remove all the vertices. This is OK.
  graph_traits<Graph>::vertex_iterator vi, vi_end, next;
  tie(vi, vi_end) = vertices(G);
  for (next = vi; vi != vi_end; vi = next) {
    ++next;
    remove_vertex(*vi, G);
  }

The stability issue also affects vertex and edge descriptors. For example, suppose vector of vertex descriptors is used to keep track of the parents (or predecessors) of vertices in a shortest paths tree (see examples/dijkstra-example.cpp). Create the parent vector with a call to dijkstra_shortest_paths(), and then remove a vertex from the graph. Subsequently, use the parent vector, but since all vertex descriptors have become invalid, the result is incorrect.

  std::vector<Vertex> parent(num_vertices(G));
  std::vector<Vertex> distance(num_vertices(G));

  dijkstra_shortest_paths(G, s, distance_map(&distance[0]).
    predecessor_map(&parent[0]));

  remove_vertex(s, G); // Bad idea! Invalidates vertex descriptors in parent vector.

  // The following will produce incorrect results
  for(tie(vi, vend) = vertices(G); vi != vend; ++vi)
    std::cout << p[*vi] << " is the parent of " << *vi << std::endl;

Note:In this discussion iterator and descriptor invalidation is concerned with the invalidation of iterators and descriptors that are not directly affected by the operation. For example, performing remove_edge(u, v, g) will always invalidate any edge descriptor for (u,v) or edge iterator pointing to (u,v), regardless of the kind adjacency_list. This discussion is concerned only with the affect of remove_edge(u, v, g) on edge descriptors and iterators that point to other edges (not (u,v)).

In general, for the vertex and edge descriptors to be stable (never invalidated) use listS or setS for the VertexList and OutEdgeList template parameters of adjacency_list. If descriptor and iterator stability is not a concern, but memory consumption and graph traversal speed are a concern, use vecS for the VertexList and/or OutEdgeList template parameters.

The following table summarizes which operations cause descriptors and iterators to become invalid. In the table, EL is an abbreviation for OutEdgeList and VL means VertexList. The Adj Iter category includes the out_edge_iterator, in_edge_iterator, and adjacency_iterator types. A more detailed description of descriptor and iterator invalidation is given in the documentation for each operation.

Table: Summary of Descriptor and Iterator Invalidation.
Function | Vertex Desc | Edge Desc | Vertex Iter | Edge Iter | Adj Iter
add_edge() OK OK OK EL=vecS &&
Directed=directedS 		EL=vecS
remove_edge()
remove_edge_if()
remove_out_edge_if()
remove_in_edge_if()
clear_vertex() 		OK OK OK EL=vecS &&
Directed=directedS 		EL=vecS
add_vertex() | OK | OK | OK | OK | OK
remove_vertex() | VL=vecS | VL=vecS | VL=vecS | VL=vecS | VL=vecS

Associated Types

graph_traits<adjacency_list>::vertex_descriptor
and
adjacency_list_traits<OutEdgeList, VertexList, Directed>::vertex_descriptor

The type for the vertex descriptors associated with the adjacency_list.

graph_traits<adjacency_list>::edge_descriptor
and
adjacency_list_traits<OutEdgeList, VertexList, Directed>::edge_descriptor

The type for the edge descriptors associated with the adjacency_list.

graph_traits<adjacency_list>::vertex_iterator

The type for the iterators returned by vertices(). When VertexList=vecS then the vertex_iterator models RandomAccessIterator.
Otherwise the vertex_iterator models BidirectionalIterator.

graph_traits<adjacency_list>::edge_iterator

The type for the iterators returned by edges(). The edge_iterator models BidirectionalIterator.

graph_traits<adjacency_list>::out_edge_iterator

The type for the iterators returned by out_edges(). When OutEdgeList=vecS then the out_edge_iterator models RandomAccessIterator.
When OutEdgeList=slistS then the out_edge_iterator models ForwardIterator. Otherwise the out_edge_iterator models BidirectionalIterator.

graph_traits<adjacency_list>::adjacency_iterator

The type for the iterators returned by adjacent_vertices(). The adjacency_iterator models the same iterator concept as out_edge_iterator.

adjacency_list::inv_adjacency_iterator

The type for the iterators returned by inv_adjacent_vertices(). The inv_adjacency_iterator models the same iterator concept as out_edge_iterator.

graph_traits<adjacency_list>::directed_category
and
adjacency_list_traits<OutEdgeList, VertexList, Directed>::directed_category

Provides information about whether the graph is directed (directed_tag) or undirected (undirected_tag).

graph_traits<adjacency_list>::edge_parallel_category
and
adjacency_list_traits<OutEdgeList, VertexList, Directed>::edge_parallel_category

This describes whether the graph class allows the insertion of parallel edges (edges with the same source and target). The two tags are allow_parallel_edge_tag and disallow_parallel_edge_tag. The setS and hash_setS variants disallow parallel edges while the others allow parallel edges.

graph_traits<adjacency_list>::vertices_size_type

The type used for dealing with the number of vertices in the graph.
graph_traits<adjacency_list>::edge_size_type

The type used for dealing with the number of edges in the graph.
graph_traits<adjacency_list>::degree_size_type

The type used for dealing with the number of edges incident to a vertex in the graph.
property_map<adjacency_list, Property>::type
and
property_map<adjacency_list, Property>::const_type

The property map type for vertex or edge properties in the graph. The specific property is specified by the Property template argument,
and must match one of the properties specified in the VertexProperties or EdgeProperties for the graph.
graph_property<adjacency_list, Property>::type

The property value type for the graph property specified by the Property tag.
adjacency_list::out_edge_list_selector

The type OutEdgeListS.

adjacency_list::vertex_list_selector

The type VertexListS.

adjacency_list::directed_selector

The type DirectedS.
adjacency_list::edge_list_selector

The type EdgeListS.

Member Functions

adjacency_list(const GraphProperty& p = GraphProperty())

Default constructor. Creates an empty graph object with zero vertices and zero edges.

adjacency_list(const adjacency_list& x)

Copy constructor. Creates a new graph that is a copy of graph x, including the edges, vertices, and properties.

adjacency_list& operator=(const adjacency_list& x)

Assignment operator. Makes this graph a copy of graph x, including the edges, vertices, and properties.

adjacency_list(vertices_size_type n,
               const GraphProperty& p = GraphProperty())

Creates a graph object with n vertices and zero edges.

template <class EdgeIterator>
adjacency_list(EdgeIterator first, EdgeIterator last,
               vertices_size_type n,
               edges_size_type m = 0,
               const GraphProperty& p = GraphProperty())

Creates a graph object with n vertices and with the edges specified in the edge list given by the range [first, last). The EdgeIterator must be a model of InputIterator. The value type of the EdgeIterator must be a std::pair, where the type in the pair is an integer type. The integers will correspond to vertices, and they must all fall in the range of [0, n).

template <class EdgeIterator, class EdgePropertyIterator>
adjacency_list(EdgeIterator first, EdgeIterator last,
               EdgePropertyIterator ep_iter,
               vertices_size_type n,
               vertices_size_type m = 0,
               const GraphProperty& p = GraphProperty())

Creates a graph object with n vertices and with the edges specified in the edge list given by the range [first, last). The EdgeIterator and EdgePropertyIterator must be a model of InputIterator. The value type of the EdgeIterator must be a std::pair, where the type in the pair is an integer type. The integers will correspond to vertices, and they must all fall in the range of [0, n). The value_type of the ep_iter should be EdgeProperties.

void clear()

Remove all of the edges and vertices from the graph.

void swap(adjacency_list& x)

Swap the vertices, edges, and properties of this graph with the vertices, edges, and properties of graph x.

Non-Member Functions

Structure Access

std::pair<vertex_iterator, vertex_iterator>
vertices(const adjacency_list& g)

Returns an iterator-range providing access to the vertex set of graph g.

std::pair<edge_iterator, edge_iterator>
edges(const adjacency_list& g)

Returns an iterator-range providing access to the edge set of graph g.

std::pair<adjacency_iterator, adjacency_iterator>
adjacent_vertices(vertex_descriptor u, const adjacency_list& g)

Returns an iterator-range providing access to the vertices adjacent to vertex u in graph g. For example, if u -> v is an edge in the graph, then v will be in this iterator-range.

std::pair<inv_adjacency_iterator, inv_adjacency_iterator>
inv_adjacent_vertices(vertex_descriptor u, const adjacency_list& g)

Returns an iterator-range providing access to the vertices in graph g to which u is adjacent. (inv is for inverse.) For example, if v -> u is an edge in the graph, then v will be in this iterator range. This function is only available for bidirectional and undirected adjacency_list.

std::pair<out_edge_iterator, out_edge_iterator>
out_edges(vertex_descriptor u, const adjacency_list& g)

Returns an iterator-range providing access to the out-edges of vertex u in graph g. If the graph is undirected, this iterator-range provides access to all edges incident on vertex u. For both directed and undirected graphs, for an out-edge e, source(e, g) == u and target(e, g) == v where v is a vertex adjacent to u.

std::pair<in_edge_iterator, in_edge_iterator>
in_edges(vertex_descriptor v, const adjacency_list& g)

Returns an iterator-range providing access to the in-edges of vertex v in graph g. This operation is only available if bidirectionalS was specified for the Directed template parameter. For an in-edge e, target(e, g) == v and source(e, g) == u for some vertex u that is adjacent to v, whether the graph is directed or undirected.

vertex_descriptor
source(edge_descriptor e, const adjacency_list& g)

Returns the source vertex of edge e.

vertex_descriptor
target(edge_descriptor e, const adjacency_list& g)
Returns the target vertex of edge e.
degree_size_type
out_degree(vertex_descriptor u, const adjacency_list& g)

Returns the number of edges leaving vertex u.

degree_size_type
in_degree(vertex_descriptor u, const adjacency_list& g)
Returns the number of edges entering vertex u. This operation is only available if bidirectionalS was specified for the Directed template parameter.
vertices_size_type
num_vertices(const adjacency_list& g)
Returns the number of vertices in the graph g.
edges_size_type
num_edges(const adjacency_list& g)
Returns the number of edges in the graph g.
vertex_descriptor
vertex(vertices_size_type n, const adjacency_list& g)

Returns the nth vertex in the graph's vertex list.

std::pair<edge_descriptor, bool>
edge(vertex_descriptor u, vertex_descriptor v,
     const adjacency_list& g)

Returns an edge connecting vertex u to vertex v in graph g.

std::pair<out_edge_iterator, out_edge_iterator>
edge_range(vertex_descriptor u, vertex_descriptor v,
           const adjacency_list& g)

Returns a pair of out-edge iterators that give the range for all the parallel edges from u to v. This function only works when the OutEdgeList for the adjacency_list is a container that sorts the out edges according to target vertex, and allows for parallel edges. The multisetS selector chooses such a container.

Structure Modification

std::pair<edge_descriptor, bool>
add_edge(vertex_descriptor u, vertex_descriptor v,
         adjacency_list& g)

Adds edge (u,v) to the graph and returns the edge descriptor for the new edge. For graphs that do not allow parallel edges, if the edge is already in the graph then a duplicate will not be added and the bool flag will be false. When the flag is false, the returned edge descriptor points to the already existing edge.

The placement of the new edge in the out-edge list is in general unspecified, though ordering of the out-edge list can be accomplished through the choice of OutEdgeList. If the VertexList selector is vecS, and if either vertex descriptor u or v (which are integers) has a value greater than the current number of vertices in the graph, the graph is enlarged so that the number of vertices is std::max(u,v) + 1.

If the OutEdgeListselector is vecS then this operation will invalidate any out_edge_iterator for vertex u. This also applies if the OutEdgeList is a user-defined container that invalidates its iterators when push(container, x) is invoked (see Section Customizing the Adjacency List Storage). If the graph is also bidirectional then any in_edge_iterator for v is also invalidated. If instead the graph is undirected then any out_edge_iterator for v is also invalidated. If instead the graph is directed, then add_edge() also invalidates any edge_iterator.

std::pair<edge_descriptor, bool>
add_edge(vertex_descriptor u, vertex_descriptor v,
         const bsp;EdgeProperties& p,
         adjacency_list& g)

Adds edge (u,v) to the graph and attaches p as the value of the edge's internal property storage. Also see the previous add_edge() member function for more details.

void remove_edge(vertex_descriptor u, vertex_descriptor v,
                 adjacency_list& g)

Removes the edge (u,v) from the graph.

This operation causes any outstanding edge descriptors or iterators that point to edge (u,v) to become invalid. In addition, if the OutEdgeList selector is vecS then this operation will invalidate any iterators that point into the edge-list for vertex u and also for vertex v in the undirected and bidirectional case. Also, for directed graphs this invalidates any edge_iterator.

void remove_edge(edge_descriptor e, adjacency_list& g); g)

Removes the edge e from the graph. This differs from the remove_edge(u, v, g) function in the case of a multigraph. This remove_edge(e, g) function removes a single edge, whereas the remove_edge(u, v, g) function removes all edges (u,v).

This operation invalidates any outstanding edge descriptors and iterators for the same edge pointed to by descriptor e. In addition, this operation will invalidate any iterators that point into the edge-list for the target(e, g). Also, for directed graphs this invalidates any edge_iterator for the graph.

void remove_edge(out_edge_iterator iter, adjacency_list& g)

This has the same effect as remove_edge(*iter, g). The difference is that this function has constant time complexity in the case of directed graphs, whereas remove_edge(e, g) has time complexity O(E/V).

template <class Predicate>
void remove_out_edge_if(vertex_descriptor u, Predicate predicate,
                        adjacency_list& g)

Removes all out-edges of vertex u from the graph that satisfy the predicate. That is, if the predicate returns true when applied to an edge descriptor, then the edge is removed.

The affect on descriptor and iterator stability is the same as that of invoking remove_edge() on each of the removed edges.

template <class Predicate>
void remove_in_edge_if(vertex_descriptor v, Predicate predicate,
                       adjacency_list& g)

Removes all in-edges of vertex v from the graph that satisfy the predicate. That is, if the predicate returns true when applied to an edge descriptor, then the edge is removed.

The affect on descriptor and iterator stability is the same as that of invoking remove_edge() on each of the removed edges.

This operation is available for undirected and bidirectional adjacency_list graphs, but not for directed.

template <class Predicate>
void remove_edge_if(Predicate predicate, adjacency_list& g)

Removes all edges from the graph that satisfy the predicate. That is, if the predicate returns true when applied to an edge descriptor, then the edge is removed.

The affect on descriptor and iterator stability is the same as that of invoking remove_edge() on each of the removed edges.

vertex_descriptor
add_vertex(adjacency_list& g)

Adds a vertex to the graph and returns the vertex descriptor for the new vertex.

vertex_descriptor
add_vertex(const VertexProperties& p,
           adjacency_list& g)

Adds a vertex to the graph with the specified properties. Returns the vertex descriptor for the new vertex.

void clear_vertex(vertex_descriptor u, adjacency_list& g)

Removes all edges to and from vertex u. The vertex still appears in the vertex set of the graph.

The affect on descriptor and iterator stability is the same as that of invoking remove_edge() for all of the edges that have u as the source or target.

void clear_out_edges(vertex_descriptor u, adjacency_list& g)

Removes all out-edges from vertex u. The vertex still appears in the vertex set of the graph.

The affect on descriptor and iterator stability is the same as that of invoking remove_edge() for all of the edges that have u as the source.

This operation is not applicable to undirected graphs (use clear_vertex() instead).

void clear_in_edges(vertex_descriptor u, adjacency_list& g)

Removes all in-edges from vertex u. The vertex still appears in the vertex set of the graph.

The affect on descriptor and iterator stability is the same as that of invoking remove_edge() for all of the edges that have u as the target.

This operation is only applicable to bidirectional graphs.

void remove_vertex(vertex_descriptor u, adjacency_list& g)

Removes vertex u from the vertex set of the graph. It is assumed that there are no edges to or from vertex u when it is removed. Invoke clear_vertex() beforehand to make that there are no edges to or from the vertex u when it is removed.

If the VertexList template parameter of the adjacency_list was vecS, then all vertex descriptors, edge descriptors, and iterators for the graph are invalidated by this operation. The built-in vertex_index_t property for each vertex is renumbered so that after the operation the vertex indices still form a contiguous range [0, num_vertices(g)). When using external property storage based on the built-in vertex index, adjust the the external storage. Another option is to not use the built-in vertex index, and instead use a property to add own vertex index property. In case of frequent use of the function remove_vertex(), the listS selector is a much better choice for the VertexList template parameter.

Property Map Accessors

template <class PropertyTag>
property_map<adjacency_list, PropertyTag>::type
get(PropertyTag, adjacency_list& g)

template <class PropertyTag>
property_map<adjacency_list, Tag>::const_type
get(PropertyTag, const adjacency_list& g)

Returns the property map object for the vertex property specified by PropertyTag. The PropertyTag must match one of the properties specified in the graph's VertexProperty template argument.

template <class PropertyTag, class X>
typename property_traits<property_map<adjacency_list, PropertyTag>::const_type>::value_type
get(PropertyTag, const adjacency_list& g, X x)

This returns the property value for x, where x is either a vertex or edge descriptor.

template <class PropertyTag, class X, class Value>
void
put(PropertyTag, const adjacency_list& g, X x, const Value& value)

This sets the property value for x to value. x is either a vertex or edge descriptor. Value must be convertible to :

typename property_traits<property_map<adjacency_list, PropertyTag>::type>::value_type
template <class GraphProperties, class GraphPropertyTag>
typename graph_property<adjacency_list, GraphPropertyTag>::type&
get_property(adjacency_list& g, GraphPropertyTag);

Return the property specified by GraphPropertyTag that is attached to the graph object g. The graph_property traits class is defined in boost/graph/adjacency_list.hpp.

template <class GraphProperties, class GraphPropertyTag>
const typename graph_property<adjacency_list, GraphPropertyTag>::type&
get_property(const adjacency_list& g, GraphPropertyTag);

Return the property specified by GraphPropertyTag that is attached to the graph object g. The graph_property traits class is defined in boost/graph/adjacency_list.hpp.

Serialization

template<class SavingArchive>
SavingArchive& operator<<(SavingArchive& ar, const adjacency_list& graph);

Serializes the graph into the archive. Requires the vertex and edge properties of the graph to be Serializable.
Include boost/graph/adj_list_serialize.hpp.

template<class LoadingArchive>
LoadingArchive& operator>>(LoadingArchive& ar, const adjacency_list& graph);

Reads the graph from the archive. Requires the vertex and edge properties of the graph to be Serializable.
Include boost/graph/adj_list_serialize.hpp.

See Also

adjacency_list_traits , property_map , graph_traits

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Adjacency List Traits

Adjacency List Traits

adjacency_list_traits<EdgeList, VertexList, Directed>

This class provides an alternate method for accessing some of the associated types of the adjacency_list class. This class is used to create graph properties whose values are vertex or edge descriptors. Do not use graph_traits for this, as it leads into a problem with mutually recursive types. The adjacency_list_traits class gives the user access to the vertex and edge descriptor types without the user providing the property types for the graph.

  template <class EdgeList, class VertexList, class Directed>
  struct adjacency_list_traits {
    typedef ... vertex_descriptor;
    typedef ... edge_descriptor;
    typedef ... directed_category;
    typedef ... edge_parallel_category;
  };

Where Defined

boost/graph/adjacency_list.hpp

Template Parameters

Parameter | Description | Default
EdgeList | The selector type for the edge container implementation. | vecS
VertexList | The selector type for the vertex container implementation. | vecS
Directed | The selector type whether the graph is directed or undirected. | directedS

Model of

DefaultConstructible and Assignable

Type Requirements

Under construction.

Members

Member | Description
vertex_descriptor | The type for the objects used to identify vertices in the graph.
edge_descriptor | The type for the objects used to identify edges in the graph.
directed_category | This says whether the graph is undirected (undirected_tag) or directed (directed_tag).
edge_parallel_category | This says whether the graph allows parallel edges to be inserted (allow_parallel_edge_tag) or if it automatically removes parallel edges (disallow_parallel_edge_tag).

See Also

adjacency_list

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Adjacency Matrix

Adjacency Matrix

adjacency_matrix<Directed, VertexProperty,
                 EdgeProperty, GraphProperty,
                 Allocator>

The adjacency_matrix class implements the BGL graph interface using the traditional adjacency matrix storage format. For a graph with V vertices, a V x V matrix is used, where each element aij is a boolean flag that says whether there is an edge from vertex i to vertex j. Figure 1 shows the adjacency matrix representation of a graph.

Figure 1: Adjacency Matrix Representation of a Directed Graph.

The advantage of this matrix format over the adjacency list is that edge insertion and removal is constant time. There are several disadvantages. The first is that the amount of memory used is O(V2) instead of O(V + E) (where E is the number of edges). The second is that operations that traverse all the out-edges of each vertex (such as breadth-first search) run in O(V2) time instead of O(V + E) time for the adjacency list. In short, it is better to use the adjacency_matrix for dense graphs (where E is close to V2) and it is better to use adjacency_list for sparse graphs (where E is much smaller than V2). The adjacency_matrix class extends the traditional data-structure by allowing objects to be attached to vertices and edges using the same property template parameters supported by adjacency_list. These may be bundled properties or standard (backward-compatible) interior properties. The types of all property values must be Copy Constructible, Assignable and Default Constructible. In the case of an undirected graph, the adjacency_matrix. class does not use a full V x V matrix but instead uses a lower triangle (the diagonal and below) since the matrix for an undirected graph is symmetric. This reduces the storage to (V2)/2. Figure 2 shows an adjacency matrix representation of an undirected graph.

Figure 1: Adjacency Matrix Representation of an Undirected Graph.

Example

The example below creates the graph shown in Figure 1.

  enum { A, B, C, D, E, F, N };
  const char* name = "ABCDEF";

  typedef boost::adjacency_matrix<boost::directedS> Graph;
  Graph g(N);
  add_edge(B, C, g);
  add_edge(B, F, g);
  add_edge(C, A, g);
  add_edge(C, C, g);
  add_edge(D, E, g);
  add_edge(E, D, g);
  add_edge(F, A, g);

  std::cout << "vertex set: ";
  boost::print_vertices(g, name);
  std::cout << std::endl;

  std::cout << "edge set: ";
  boost::print_edges(g, name);
  std::cout << std::endl;

  std::cout << "out-edges: " << std::endl;
  boost::print_graph(g, name);
  std::cout << std::endl;

The output is:

  vertex set: A B C D E F

  edge set: (B,C) (B,F) (C,A) (C,C) (D,E) (E,D) (F,A)

  out-edges:
  A -->
  B --> C F
  C --> A C
  D --> E
  E --> D
  F --> A

The example below creates the graph shown in Figure 2.

  enum { A, B, C, D, E, F, N };
  const char* name = "ABCDEF";

  typedef boost::adjacency_matrix<boost::undirectedS> UGraph;
  UGraph ug(N);
  add_edge(B, C, ug);
  add_edge(B, F, ug);
  add_edge(C, A, ug);
  add_edge(D, E, ug);
  add_edge(F, A, ug);

  std::cout << "vertex set: ";
  boost::print_vertices(ug, name);
  std::cout << std::endl;

  std::cout << "edge set: ";
  boost::print_edges(ug, name);
  std::cout << std::endl;

  std::cout << "incident edges: " << std::endl;
  boost::print_graph(ug, name);
  std::cout << std::endl;

The output is:

  vertex set: A B C D E F

  edge set: (C,A) (C,B) (E,D) (F,A) (F,B)

  incident edges:
  A <--> C F
  B <--> C F
  C <--> A B
  D <--> E
  E <--> D
  F <--> A B

Where Defined

boost/graph/adjacency_matrix.hpp

Template Parameters

Parameter | Description | Default
Directed | A selector to choose whether the graph is directed or undirected. The options are directedS and undirectedS. | directedS
VertexProperty | for specifying internal property storage. | no_property
EdgeProperty | for specifying internal property storage. | no_property
GraphProperty | for specifying property storage for the graph object. | no_property

Model Of

VertexAndEdgeListGraph, Incidence Graph, Bidirectional Graph, AdjacencyMatrix, MutablePropertyGraph, CopyConstructible, and Assignable.

Associates Types

graph_traits<adjacency_matrix>::vertex_descriptor

The type for the vertex descriptors associated with the adjacency_matrix.
(Required by Graph.)

graph_traits<adjacency_matrix>::edge_descriptor

The type for the edge descriptors associated with the adjacency_matrix.
(Required by Graph.)

graph_traits<adjacency_matrix>::vertex_iterator

The type for the iterators returned by vertices(). The vertex iterator models RandomAccessIterator.
(Required by VertexListGraph.)

graph_traits<adjacency_matrix>::edge_iterator

The type for the iterators returned by edges(). This iterator models MultiPassInputIterator.
(Required by EdgeListGraph.)

graph_traits<adjacency_matrix>::out_edge_iterator

The type for the iterators returned by out_edges(). This iterator models MultiPassInputIterator.
(Required by IncidenceGraph.)

graph_traits<adjacency_matrix>::in_edge_iterator

The type for the iterators returned by in_edges(). This iterator models MultiPassInputIterator.
(Required by BidirectionalGraph.)

graph_traits<adjacency_matrix>::adjacency_iterator

The type for the iterators returned by adjacent_vertices(). This iterator models the same concept as the out-edge iterator.
(Required by AdjacencyGraph.)

graph_traits<adjacency_matrix>::directed_category

Provides information about whether the graph is directed (directed_tag) or undirected (undirected_tag).
(Required by Graph.)

graph_traits<adjacency_matrix>::edge_parallel_category

An adjacency matrix does not allow the insertion of parallel edges, so this type is always disallow_parallel_edge_tag.
(Required by Graph.)

graph_traits<adjacency_matrix>::vertices_size_type

The type used for dealing with the number of vertices in the graph.
(Required by VertexListGraph.)

graph_traits<adjacency_matrix>::edges_size_type

The type used for dealing with the number of edges in the graph.
(Required by EdgeListGraph.)

graph_traits<adjacency_matrix>::degree_size_type

The type used for dealing with the number of out-edges of a vertex.
(Required by IncidenceGraph.)

property_map<adjacency_matrix, PropertyTag>::type
property_map<adjacency_matrix, PropertyTag>::const_type

The map type for vertex or edge properties in the graph. The specific property is specified by the PropertyTag template argument, and must match one of the properties specified in the VertexProperty or EdgeProperty for the graph.
(Required by PropertyGraph.)

Member Functions

adjacency_matrix(vertices_size_type n,
                 const GraphProperty& p = GraphProperty())

Creates a graph object with n vertices and zero edges.
(Required by MutableGraph.)

template <typename EdgeIterator>
adjacency_matrix(EdgeIterator first,
                 EdgeIterator last,
                 vertices_size_type n,
                 const GraphProperty& p = GraphProperty())

Creates a graph object with n vertices with the edges specified in the edge list given by the range [first, last). The value type of the EdgeIterator must be a std::pair, where the type in the pair is an integer type. The integers will correspond to vertices, and they must all fall in the range of [0, n).
(Required by IteratorConstructibleGraph.)

template <typename EdgeIterator, typename EdgePropertyIterator>
adjacency_matrix(EdgeIterator first, EdgeIterator last,
                 EdgePropertyIterator ep_iter,
                 vertices_size_type n,
                 const GraphProperty& p = GraphProperty())

Creates a graph object with n vertices, with the edges specified in the edge list given by the range [first, last). The value type of the EdgeIterator must be a std::pair, where the type in the pair is an integer type. The integers will correspond to vertices, and they must all fall in the range of [0, n). The value_type of the ep_iter should be EdgeProperty.

Non-Member Functions

std::pair<vertex_iterator, vertex_iterator>
vertices(const adjacency_matrix& g)

Returns an iterator-range providing access to the vertex set of graph g.
(Required by VertexListGraph.)

std::pair<edge_iterator, edge_iterator>
edges(const adjacency_matrix& g);

Returns an iterator-range providing access to the edge set of graph g.
(Required by EdgeListGraph.)

std::pair<adjacency_iterator, adjacency_iterator>
adjacent_vertices(vertex_descriptor v, const adjacency_matrix& g)

Returns an iterator-range providing access to the vertices adjacent to vertex v in graph g.
(Required by AdjacencyGraph.)

std::pair<out_edge_iterator, out_edge_iterator>
out_edges(vertex_descriptor v, const adjacency_matrix& g)

Returns an iterator-range providing access to the out-edges of vertex v in graph g. If the graph is undirected, this iterator-range provides access to all edges incident on vertex v.
(Required by IncidenceGraph.)

vertex_descriptor
source(edge_descriptor e, const adjacency_matrix& g)

Returns the source vertex of edge e.
(Required by IncidenceGraph.)

vertex_descriptor
target(edge_descriptor e, const adjacency_matrix& g)

Returns the target vertex of edge e.
(Required by IncidenceGraph.)

degree_size_type
out_degree(vertex_descriptor u, const adjacency_matrix& g)

Returns the number of edges leaving vertex u.
(Required by IncidenceGraph.)

std::pair<in_edge_iterator, in_edge_iterator>
in_edges(vertex_descriptor v, const adjacency_matrix& g)

Returns an iterator-range providing access to the in-edges of vertex v in graph g. If the graph is undirected, this iterator-range provides access to all edges incident on vertex v.
(Required by BidirectionalGraph.)

degree_size_type
in_degree(vertex_descriptor u, const adjacency_matrix& g)

Returns the number of edges entering vertex u.
(Required by BidirectionalGraph.)

vertices_size_type num_vertices(const adjacency_matrix& g)

Returns the number of vertices in the graph g.
(Required by VertexListGraph.)

edges_size_type num_edges(const adjacency_matrix& g)

Returns the number of edges in the graph g.
(Required by EdgeListGraph.)

vertex_descriptor vertex(vertices_size_type n, const adjacency_matrix& g)

Returns the nth vertex in the graph's vertex list.

std::pair<edge_descriptor, bool>
edge(vertex_descriptor u, vertex_descriptor v,
     const adjacency_matrix& g)

Returns the edge connecting vertex u to vertex v in graph g.
(Required by AdjacencyMatrix.)

std::pair<edge_descriptor, bool>
add_edge(vertex_descriptor u, vertex_descriptor v,
         adjacency_matrix& g)

Adds edge (u,v) to the graph and returns the edge descriptor for the new edge. If the edge is already in the graph then a duplicate will not be added and the bool flag will be false. This operation does not invalidate any of the graph's iterators or descriptors.
(Required by MutableGraph.)

std::pair<edge_descriptor, bool>
add_edge(vertex_descriptor u, vertex_descriptor v,
         const EdgeProperty& p,
         adjacency_matrix& g)

Adds edge (u,v) to the graph and attaches p as the value of the edge's internal property storage. Also, see the previous add_edge() member function for more details.

void remove_edge(vertex_descriptor u, vertex_descriptor v,
                 adjacency_matrix& g)

Removes the edge (u,v) from the graph.
(Required by MutableGraph.)

void remove_edge(edge_descriptor e, adjacency_matrix& g)

Removes the edge e from the graph. This is equivalent to calling remove_edge(source(e, g), target(e, g), g).
(Required by MutableGraph.)

void clear_vertex(vertex_descriptor u, adjacency_matrix& g)

Removes all edges to and from vertex u. The vertex still appears in the vertex set of the graph.
(Required by MutableGraph.)

template <typename Property>
property_map<adjacency_matrix, Property>::type
get(Property, adjacency_matrix& g)

template <typename Property>
property_map<adjacency_matrix, Property>::const_type
get(Property, const adjacency_matrix& g)

Returns the property map object for the vertex property specified by Property. The Property must match one of the properties specified in the graph's VertexProperty template argument.
(Required by PropertyGraph.)

template <typename Property, typename X>
typename property_traits<
  typename property_map<adjacency_matrix, Property>::const_type
>::value_type
get(Property, const adjacency_matrix& g, X x)

This returns the property value for x, which is either a vertex or edge descriptor.
(Required by PropertyGraph.)

template <typename Property, typename X, typename Value>
void
put(Property, const adjacency_matrix& g, X x, const Value& value)

This sets the property value for x to value. x is either a vertex or edge descriptor. Value must be convertible to typename property_traits<property_map<adjacency_matrix, Property>::type>::value_type.
(Required by PropertyGraph.)

template <typename GraphProperty, typename GraphProperty>
typename property_value<GraphProperty, GraphProperty>::type&
get_property(adjacency_matrix& g, GraphProperty)
Returns the property specified by GraphProperty that is attached to the graph object g. The property_value traits class is defined in boost/pending/property.hpp.
template <typename GraphProperty, typename GraphProperty>
const typename property_value<GraphProperty, GraphProperty>::type&
get_property(const adjacency_matrix& g, GraphProperty)

Returns the property specified by GraphProperty that is attached to the graph object g. The property_value traits class is defined in boost/pending/property.hpp.


AdjacencyGraph

The AdjacencyGraph concept provides an interface for efficient access of the adjacent vertices to a vertex in a graph. This is quite similar to the IncidenceGraph concept (the target of an out-edge is an adjacent vertex). Both concepts are provided because in some contexts there is concern only for the vertices, whereas in other contexts the edges are also important.

Refinement of

Graph

Notation

G | A type that is a model of Graph.
g | An object of type G.
v | An object of type boost::graph_traits<G>::vertex_descriptor.

Associated Types

boost::graph_traits<G>::traversal_category

This tag type must be convertible to adjacency_graph_tag.

boost::graph_traits<G>::adjacency_iterator

An adjacency iterator for a vertex v provides access to the vertices adjacent to v. As such, the value type of an adjacency iterator is the vertex descriptor type of its graph. An adjacency iterator must meet the requirements of MultiPassInputIterator.

Valid Expressions

adjacent_vertices(v, g)

Returns an iterator-range providing access to the vertices adjacent to vertex v in graph g.[1]
Return type: std::pair<adjacency_iterator, adjacency_iterator>

Complexity guarantees

The adjacent_vertices() function must return in constant time.

See Also

Graph concepts, adjacency_iterator

Concept Checking Class

  template <class G>
  struct AdjacencyGraphConcept
  {
    typedef typename boost::graph_traits<G>::adjacency_iterator
      adjacency_iterator;
    void constraints() {
      function_requires< IncidenceGraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<adjacency_iterator> >();

      p = adjacent_vertices(v, g);
      v = *p.first;
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = adjacent_vertices(v, g);
    }
    std::pair<adjacency_iterator,adjacency_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor v;
    G g;
  };

Design Rationale

The AdjacencyGraph concept is somewhat frivolous since IncidenceGraph really covers the same functionality (and more). The AdjacencyGraph concept exists because there are situations when adjacent_vertices() is more convenient to use than out_edges(). The user can construct a graph class without creating an adjacency iterator. Use the adaptor class named adjacency_iterator to create an adjacency iterator out of an out-edge iterator.

Notes

[1] The case of a multigraph (where multiple edges can connect the same two vertices) brings up an issue as to whether the iterators returned by the adjacent_vertices() function access a range that includes each adjacent vertex once, or whether it should match the behavior of the out_edges() function, and access a range that may include an adjacent vertex more than once. For now the behavior is defined to match that of out_edges(), though this decision may need to be reviewed in light of more experience with graph algorithm implementations.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Adjacency Graph

Name

AdjacencyGraph: Provides an interface for efficient access of the adjacent vertices to a vertex in a graph.

Synopsis

 template <class G>
  struct AdjacencyGraphConcept
  {
    typedef typename boost::graph_traits<G>::adjacency_iterator
      adjacency_iterator;
    void constraints() {
      function_requires< IncidenceGraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<adjacency_iterator> >();

      p = adjacent_vertices(v, g);
      v = *p.first;
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = adjacent_vertices(v, g);
    }
    std::pair<adjacency_iterator,adjacency_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor v;
    G g;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


AdjacencyMatrix

The AdjacencyMatrix concept refines the Graph concept and adds the requirement for efficient access to any edge in the graph given the source and target vertices. No Boost Graph Library algorithms currently use this concept. However there are algorithms not yet implemented such as Floyd-Warshall that would require this concept.

Refinement of

Graph

Associated Types

boost::This tag type must be convertible to adjacency_matrix_tag.

Valid Expressions

Name | Expression | Return Type | Description
Direct Edge Access | edge(u,v,g) | std::pair<edge_descriptor, bool> | Returns a pair consisting of a flag saying whether there exists an edge between u and v in graph g, and consisting of the edge descriptor if the edge was found.

Complexity guarantees

The edge() function must return in constant time.

Models

adjacency_matrix

Concept Checking Class

  template <class G>
  struct AdjacencyMatrix
  {
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    void constraints() {
      p = edge(u, v, g);
    }
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    std::pair<bool, edge_descriptor> p;
    G g;
  };

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Assignable

Description

A type is Assignable if it is possible to assign one object of the type to another object of that type.

Notation

T | is type that is a model of Assignable
t | is an object of type T
u | is an object of type T or possibly const T

Definitions

Valid expressions

Name | Expression | Return type | Semantics
Assignment | t = u | T& | t is equivalent to u

Models

  • int

  • std::pair

See also

DefaultConstructible and CopyConstructible

Copyright © 2000 Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)
Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Iterator Property Map Adaptor

Associative Property Map

associative_property_map<UniquePairAssociativeContainer>

This property map is an adaptor that converts any type that is a model of both Pair Associative Container and Unique Associative Container such as std::map into a mutable Lvalue Property Map. Note that the adaptor only retains a reference to the container, so the lifetime of the container must encompass the use of the adaptor.

Example

//  (C) Copyright Jeremy Siek 2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <map>
#include <string>
#include <boost/property_map.hpp>

template <typename AddressMap>
void foo(AddressMap address)
{
  typedef typename boost::property_traits<AddressMap>::value_type value_type;
  typedef typename boost::property_traits<AddressMap>::key_type key_type;

  value_type old_address, new_address;
  key_type fred = "Fred";
  old_address = get(address, fred);
  new_address = "384 Fitzpatrick Street";
  put(address, fred, new_address);

  key_type joe = "Joe";
  value_type& joes_address = address[joe];
  joes_address = "325 Cushing Avenue";
}

int
main()
{
  std::map<std::string, std::string> name2address;
  boost::associative_property_map< std::map<std::string, std::string> >
    address_map(name2address);

  name2address.insert(make_pair(std::string("Fred"),
                                std::string("710 West 13th Street")));
  name2address.insert(make_pair(std::string("Joe"),
                                std::string("710 West 13th Street")));

  foo(address_map);

  for (std::map<std::string, std::string>::iterator i = name2address.begin();
       i != name2address.end(); ++i)
    std::cout << i->first << ": " << i->second << "\n";

  return EXIT_SUCCESS;
}

#include <iostream>
#include <map>
#include <string>
#include <boost/property_map.hpp>

template <typename AddressMap>
void foo(AddressMap address)
{
  typedef typename boost::property_traits<AddressMap>::value_type value_type;
  typedef typename boost::property_traits<AddressMap>::key_type key_type;

  value_type old_address, new_address;
  key_type fred = "Fred";
  old_address = get(address, fred);
  new_address = "384 Fitzpatrick Street";
  put(address, fred, new_address);

  key_type joe = "Joe";
  value_type& joes_address = address[joe];
  joes_address = "325 Cushing Avenue";
}

int
main()
{
  std::map<std::string, std::string> name2address;
  boost::associative_property_map< std::map<std::string, std::string> >
    address_map(name2address);

  name2address.insert(make_pair(std::string("Fred"),
                std::string("710 West 13th Street")));
  name2address.insert(make_pair(std::string("Joe"),
                std::string("710 West 13th Street")));

  foo(address_map);

  for (std::map<std::string, std::string>::iterator i = name2address.begin();
       i != name2address.end(); ++i)
    std::cout << i->first << ": " << i->second << "\n";

  return EXIT_SUCCESS;
}

Where Defined

boost/property_map.hpp

Model Of

Lvalue Property Map

Template Parameters

Parameter | Description | Default
UniquePairAssociativeContainer | Must be a model of both Pair Associative Container and Unique Associative Container . |

Members

In addition to the methods and functions required by Lvalue Property Map, this class has the following members:

property_traits<associative_property_map>::value_type

This is the same type as UniquePairAssociativeContainer::data_type.

associative_property_map()

Default Constructor.

associative_property_map(UniquePairAssociativeContainer& c)

Constructor.

data_type& operator[](const key_type& k) const

The operator bracket for property access. The key_type and data_type types are from the typedefs inside of UniquePairAssociativeContainer.

Non-Member functions

  template <typename UniquePairAssociativeContainer>
  associative_property_map<UniquePairAssociativeContainer>
  make_assoc_property_map(UniquePairAssociativeContainer& c);

A function for conveniently creating an associative property map.

Copyright © 2002
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee1@osl.iu.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: astar_heuristic

astar_heuristic

astar_heuristic<Graph, CostType>

This class is a default implementation of the AStarHeuristic concept. It implements a "zero" heuristic -- a heuristic function that returns a cost of zero regardless of the vertex it is passed.

Model of

AStarHeuristic

Template Parameters

Parameter | Description | Default
Graph | The type of the graph being searched. |
CostType | A type used with the compare and combine functions passed to A*. Typically an integer or floating point type. |

Where Defined

boost/graph/astar_search.hpp

Member Functions

This class implements the single member function required by AStarHeuristic:

Function | Description
CostType operator()(graph_traits<Graph>::vertex_descriptor u); | Called for the target of every out edge of a vertex being examined. Returns the cost, estimated by the heuristic, from vertex u to a goal.

Copyright © 2004 Kristopher Beevers, Rensselaer Polytechnic Institute (beevek@cs.rpi.edu)


Boost Graph Library: A* Heuristic Search

astar_search

// Named parameter interface
template <typename VertexListGraph,
          typename AStarHeuristic,
          typename P, typename T, typename R>
void
astar_search
  (VertexListGraph &g,
   typename graph_traits<VertexListGraph>::vertex_descriptor s,
   AStarHeuristic h, const bgl_named_params<P, T, R>& params);

// Non-named parameter interface
template <typename VertexListGraph, typename AStarHeuristic,
          typename AStarVisitor, typename PredecessorMap,
          typename CostMap, typename DistanceMap,
          typename WeightMap, typename VertexIndexMap,
      typename ColorMap,
          typename CompareFunction, typename CombineFunction,
          typename CostInf, typename CostZero>
inline void
astar_search
  (VertexListGraph &g,
   typename graph_traits<VertexListGraph>::vertex_descriptor s,
   AStarHeuristic h, AStarVisitor vis,
   PredecessorMap predecessor, CostMap cost,
   DistanceMap distance, WeightMap weight,
   VertexIndexMap index_map, ColorMap color,
   CompareFunction compare, CombineFunction combine,
   CostInf inf, CostZero zero);

This algorithm implements a heuristic search on a weighted, directed or undirected graph for the case where all edge weights are non-negative.

The A* algorithm is a heuristic graph search algorithm: an A* search is "guided" by a heuristic function. A heuristic function h(v) is one which estimates the cost from a non-goal state (v) in the graph to some goal state, g. Intuitively, A* follows paths (through the graph) to the goal that are estimated by the heuristic function to be the best paths. Unlike best-first search, A* takes into account the known cost from the start of the search to v; the paths A* takes are guided by a function f(v) = g(v) + h(v), where h(v) is the heuristic function, and g(v) (sometimes denoted c(s, v)) is the known cost from the start to v. Clearly, the efficiency of A* is highly dependent on the heuristic function with which it is used.

The A* algorithm is very similar to Dijkstra's Shortest Paths algorithm. This implementation finds all the shortest paths from the start vertex to every other vertex by creating a search tree, examining vertices according to their remaining cost to some goal, as estimated by a heuristic function. Most commonly, A* is used to find some specific goal vertex or vertices in a graph, after which the search is terminated.

A* is particularly useful for searching implicit graphs. Implicit graphs are graphs that are not completely known at the beginning of the search. Upon visiting a vertex, its neighbors are "generated" and added to the search. Implicit graphs are particularly useful for searching large state spaces -- in gameplaying scenarios (e.g. chess), for example -- in which it may not be possible to store the entire graph. Implicit searches can be performed with this implementation of A* by creating special visitors that generate neighbors of newly-expanded vertices.

This implementation of A* is based on an OPEN/CLOSED list formulation of the algorithm. Vertices on the OPEN list have been ``discovered'' by the algorithm, but not ``expanded'' (their adjacent vertices are not discovered ). Vertices on the CLOSED list have been completely examined by the search (expanded them and added their children to the OPEN list). Vertices that are on neither list have not been encountered in any context so far in the search. A major advantage of this formulation of the A* algorithm over other approaches is that it avoids ``cycles'' in the state space; the search will not become trapped by loops in the graph. The OPEN/CLOSED lists are implemented using BGL's vertex coloring mechanisms. Vertices in OPEN are colored gray, vertices in CLOSED are colored black, and undiscovered vertices are colored white.

The criteria for expanding a vertex on the OPEN list is that it has the lowest f(v) = g(v) + h(v) value of all vertices on OPEN. Cost information about vertices is stored in a property map.

The following is the pseudocode for the A* heuristic search algorithm. In the pseudocode, h is the heuristic function, w is the edge weight, d is the distance of a vertex from s, and Q is a priority queue, sorted by f, the estimated cost to the goal of the path through a vertex. p is a predecessor map. The visitor event points for the algorithm are indicated by the labels on the right.

A*(G, s, h)
  for each vertex u in V
    d[u] := f[u] := infinity
    color[u] := WHITE
    p[u] := u
  end for
  color[s] := GRAY
  d[s] := 0
  f[s] := h(s)
  INSERT(Q, s)
  while (Q != Ø)
    u := EXTRACT-MIN(Q)
    for each vertex v in Adj[u]
      if (w(u,v) + d[u] < d[v])
        d[v] := w(u,v) + d[u]
    f[v] := d[v] + h(v)
    p[v] := u
    if (color[v] = WHITE)
      color[v] := GRAY
      INSERT(Q, v)
    else if (color[v] = BLACK)
      color[v] := GRAY
      INSERT(Q, v)
    end if
      else
        ...
    end for
    color[u] := BLACK
  end while

initialize vertex u

discover vertex s

examine vertex u
examine edge (u,v)

edge (u,v) relaxed

discover vertex v

reopen vertex v

edge (u,v) not relaxed

finish vertex u

Where Defined

boost/graph/astar_search.hpp

Parameters

IN: VertexListGraph& g

Apply the algorithm on this graph object. The type VertexListGraph must be a model of the Vertex List Graph concept.

IN: vertex_descriptor s

The start vertex for the search. Calculate all distances from this vertex, and the shortest paths tree (recorded in the predecessor map) will be rooted at this vertex.

IN: AStarHeuristic h

The heuristic function that guides the search. The type AStarHeuristic must be a model of the AStarHeuristic concept.

Named Parameters

IN: weight_map(WeightMap w_map)

The weight or ``length'' of each edge in the graph. The weights must all be non-negative; the algorithm will throw a negative_edge exception if one of the edges is negative. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for this map must be the same as the value type of the distance map.
Default: get(edge\_weight, g)

IN: vertex_index_map(VertexIndexMap i_map)

This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g).
Note: To use this default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.

OUT: predecessor_map(PredecessorMap p_map)

The predecessor map records the edges in the minimum spanning tree. Upon completion of the algorithm, the edges (p[u],u) for all u in V are in the minimum spanning tree. If p[u] = u then u is either the start vertex or a vertex that is not reachable from the start. The PredecessorMap type must be a Read/Write Property Map with key and vertex types the same as the vertex descriptor type of the graph.
Default: dummy_property_map

UTIL/OUT: distance_map(DistanceMap d_map)

The shortest path weight from the start vertex s to each vertex in the graph g is recorded in this property map. The shortest path weight is the sum of the edge weights along the shortest path. The type DistanceMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the distance map. The value type of the distance map is the element type of a Monoid formed with the combine function object and the zero object for the identity element. Also the distance value type must have a StrictWeakOrdering provided by the compare function object.
Default: iterator_property_map created from a std::vector with the same value type as the WeightMap, and of size num_vertices(g), and using the i_map for the index map.

UTIL/OUT: rank_map(CostMap c_map)

The f-value for each vertex. The f-value is defined as the sum of the cost to get to a vertex from the start vertex, and the estimated cost (as returned by the heuristic function h) from the vertex to a goal. The type CostMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the distance map. The value type of the distance map is the element type of a Monoid formed with the combine function object and the zero object for the identity element. Also the distance value type must have a StrictWeakOrdering provided by the compare function object. The value type for this map must be the same as the value type for the distance map.
Default: iterator_property_map created from a std::vector with the same value type as the WeightMap, and of size num_vertices(g), and using the i_map for the index map.

UTIL/OUT: color_map(ColorMap c_map)

This is used during the execution of the algorithm to mark the vertices, indicating whether they are on the OPEN or CLOSED lists. The vertices start out white and become gray when they are inserted into the OPEN list. They then turn black when they are examined and placed on the CLOSED list. At the end of the algorithm, vertices reachable from the source vertex will have been colored black. All other vertices will still be white. The type ColorMap must be a model of Read/Write Property Map. A vertex descriptor must be usable as the key type of the map, and the value type of the map must be a model of Color Value.
Default: iterator_property_map created from a std::vector of value type default_color_type, with size num_vertices(g), and using the i_map for the index map.

IN: distance_compare(CompareFunction cmp)

This function is use to compare distances to determine which vertex is closer to the start vertex, and to compare f-values to determine which vertex on the OPEN list to examine next. The CompareFunction type must be a model of Binary Predicate and have argument types that match the value type of the DistanceMap property map.
Default: std::less<D> with D = typename property_traits<DistanceMap>::value_type.

IN: distance_combine(CombineFunction cmb)

This function is used to combine distances to compute the distance of a path, and to combine distance and heuristic values to compute the f-value of a vertex. The CombineFunction type must be a model of Binary Function. Both argument types of the binary function must match the value type of the DistanceMap property map (which is the same as that of the WeightMap and CostMap property maps). The result type must be the same type as the distance value type.
Default: std::plus<D> with D = typename property_traits<DistanceMap>::value_type.

IN: distance_inf(D inf)

The inf object must be the greatest value of any D object. That is, compare(d, inf) == true for any d != inf. The type D is the value type of the DistanceMap.
Default: std::numeric_limits<D>::max()

IN: distance_zero(D zero)

The zero value must be the identity element for the Monoid formed by the distance values and the combine function object. The type D is the value type of the DistanceMap.
Default: D() with D = typename property_traits<DistanceMap>::value_type.

OUT: visitor(AStarVisitor v)

Use this to specify actions that you would like to happen during certain event points within the algorithm. The type AStarVisitor must be a model of the AStarVisitor concept. The visitor object is passed by value [1].
Default: astar_visitor<null_visitor>

Complexity

The time complexity is O((E + V) log V).

Visitor Event Points

  • vis.initialize_vertex(u, g) is invoked on each vertex in the graph before the start of the algorithm.

  • vis.discover_vertex(v, g) is invoked when a vertex is first discovered and is added to the OPEN list.
  • vis.examine_vertex(u, g) is invoked when a vertex is popped from the queue (i.e., it has the lowest cost on the OPEN list).
  • vis.examine_edge(e, g) is invoked on each out-edge of a vertex immediately after it is examined.
  • vis.edge_relaxed(e, g)(u,v) if d[u] + w(u,v) < d[v].
  • vis.edge_not_relaxed(e, g) is invoked if the edge is not relaxed (see above).
  • vis.black_target(u, g) is invoked when a vertex that is on the CLOSED list is "rediscovered" via a more efficient path, and is re-added to the OPEN list.
  • vis.finish_vertex(u, g) is invoked on a vertex when it is added to the CLOSED list, which happens after all of its out edges have been examined.

Example

See example/astar-cities.cpp for an example of using A* search.

Notes

[1] Since the visitor parameter is passed by value, if your visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore you may want the visitor to hold this state by pointer or reference.

Copyright © 2004 Kristopher Beevers, Rensselaer Polytechnic Institute (beevek@cs.rpi.edudu)


Boost Graph Library: astar_visitor

Astar_visitor

astar_visitor<EventVisitorList>: This class is an adapter that converts a list of EventVisitor's (constructed using std::pair) into a AStarVisitor.

Example

See the example for EventVisitor.

Model of

AStarVisitor

Template Parameters

Parameter | Description | Default
EventVisitorList | A list of EventVisitor's created with std::pair. | null_visitor

Where Defined

boost/graph/astar_search.hpp

Member Functions

This class implements all of the member functions required by AStarVisitor. In each function the appropriate event is dispatched to the EventVisitor's in the EventVisitorList.

Non-Member Functions

Function Description

template <class EventVisitorList>
astar_visitor<EventVisitorList>
make_astar_visitor(EventVisitorList ev_list);

Returns the event visitor list adapted to be an A* visitor.

See Also

Visitor concepts

The following are event visitors: predecessor_recorder , distance_recorder , time_stamper , and property_writer .

Copyright © 2004 Kristopher Beevers, Rensselaer Polytechnic Institute (beevek@cs.rpi.edu)


Boost Graph Library: AStarHeuristic

AStar Heuristic Concept

This concept defines the interface for the heuristic function of an A* search, which is responsible for estimating the remaining cost from some vertex to a goal. The user, creates a class that matches this interface, and then passes objects of the class into astar_search() to guide the order of vertex examination of the search. The heuristic instance must incorporate any necessary information about goal vertices in the graph. For further discussion of the use of heuristics in an A* search, see the documentation of astar_search() .

Refinement of

Unary Function (must take a single argument -- a graph vertex -- and return a cost value) and Copy Constructible (copying a heuristic should be a lightweight operation).

Notation

H | A type that is a model of AStar Heuristic.
h | An object of type H.
G | A type that is a model of Graph.
g | An object of type G.
u | An object of type boost::graph_traits<G>::vertex_descriptor.
CostType | A type that can be used with the compare and combine functions passed to A*.
c | An object of type CostType.

Associated Types

none

Valid Expressions

Name | Expression | Return Type | Description
Call Heuristic | CostType c = h(u) | CostType | Called for the target of every out edge of a vertex being examined.

Models

  • astar_heuristic

Concept Checking Class

  template <class Heuristic, class Graph>
  struct AStarHeuristicConcept {
    void constraints()
    {
      function_requires< CopyConstructibleConcept<Heuristic> >();
      h(u);
    }
    Heuristic h;
    typename graph_traits<Graph>::vertex_descriptor u;
  };

Copyright © 2004 Kristopher Beevers, Rensselaer Polytechnic Institute (beevek@cs.rpi.edu)


Boost Graph Library: AStarVisitor

AStar Visitor Concept

This concept defines the visitor interface for astar_search(). Users can define a class with the AStar Visitor interface and pass an object of the class to astar_search(), thereby augmenting the actions taken during the graph search.

Refinement of

Copy Constructible (copying a visitor should be a lightweight operation).

Notation

V | A type that is a model of AStar Visitor.
vis | An object of type V.
G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
s,u,v | An object of type boost::graph_traits<G>::vertex_descriptor.
d | An object of type DistanceMap.
WeightMap | A type that is a model of Readable Property Map.
w | An object of type WeightMap.

Associated Types

none

Valid Expressions

Name | Expression | Return Type | Description
Initialize Vertex | vis.initialize_vertex(u, g) | void | This is invoked on each vertex of the graph when it is first initialized (i.e., when its property maps are initialized).
Discover Vertex | vis.discover_vertex(u, g) | void | This is invoked when a vertex is first discovered and is added to the OPEN list.
Examine Vertex | vis.examine_vertex(u, g) | void | This is invoked on a vertex as it is popped from the queue (i.e. it has the lowest cost on the OPEN list). This happens immediately before examine_edge() is invoked on each of the out-edges of vertex u.
Examine Edge | vis.examine_edge(e, g) | void | This is invoked on every out-edge of each vertex after it is examined.
Edge Relaxed vis.edge_relaxed(e, g) void Upon examination, if the following condition holds then the edge is relaxed (the distance of its target vertex is reduced) and this method is invoked:
tie(u, s) = incident(e, g);
D d_u = get(d, u), d_v = get(d, s);
W w_e = get(w, e);
assert(compare(combine(d_u, w_e), d_s));
Edge Not Relaxed | vis.edge_not_relaxed(e, g) | void | Upon examination, if an edge is not relaxed (see above), then this method is invoked.
Black Target | vis.black_target(e, g) | void | This is invoked when a vertex that is on the CLOSED list is ``rediscovered'' via a more efficient path, and is re-added to the OPEN list.
Finish Vertex | vis.finish_vertex(u, g) | void | This is invoked on a vertex when it is added to the CLOSED list, which happens after all of its out edges have been examined.

Models

  • astar_visitor

See also

Visitor concepts

Copyright © 2004 Kristopher Beevers, Rensselaer Polytechnic Institute (beevek@cs.rpi.edu)


Boost Graph Library: Bandwidth

bandwidth

   (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  bandwidth(const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  bandwidth(const Graph& g, VertexIndexMap index_map)

The bandwidth of an undirected graph is the maximum distance between two adjacent vertices, with distance measured on a line upon which the vertices have been placed at unit intervals. To put it another way, if the vertices of an undirected graph G=(V,E) are each assigned an index from zero to |V| - 1 given by index[v], then the bandwidth of G is

B(G) = max { |index[u] - index[v]|  | (u,v) in E }

Defined in

boost/graph/bandwidth.hpp

ith_bandwidth

  (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  ith_bandwidth(typename graph_traits<Graph>::vertex_descriptor i,
        const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  ith_bandwidth(typename graph_traits<Graph>::vertex_descriptor i,
        const Graph& g,
        VertexIndexMap index)

The i-th bandwidth a graph is the maximum distance between the i-th vertex and any of its neighbors.

Bi(G) = max { |index[i] - index[j]|  | (i,j) in E }

So the bandwidth B(G) can be expressed as the maximum of the i-th bandwidths Bi(G).

B(G) = max { Bi(G)   | i=0...|V|-1 }

Defined in

boost/graph/bandwidth.hpp

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


BasicMatrix

The BasicMatrix concept provides a minimalist interface for accessing elements from a 2 dimensional table of values.

Refinement of

none

Notation

{M,I,V} | The matrix, index, and values types that together model the BasicMatrix concept.
A | An object of type M.
i, j | Objects of type I.

Associated Types

none

Valid Expressions

A[i][j]

Returns a reference to the element object stored at index (i,j)
Return type: V& for mutable A or const V& for constant A.

Complexity guarantees

Element access is constant time.

Concept Checking Class

template <class M, class I, class V>
  struct BasicMatrixConcept
  {
    void constraints() {
      V& elt = A[i][j];
      const_constraints(A);
      ignore_unused_variable_warning(elt);
    }
    void const_constraints(const M& A) {
      const V& elt = A[i][j];
      ignore_unused_variable_warning(elt);
    }
    M A;
    I i, j;
  };

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Function betweenness_centrality_clustering

boost::betweenness_centrality_clustering — Graph clustering based on edge betweenness centrality.

Synopsis

template<typename MutableGraph, typename Done, typename EdgeCentralityMap,
         typename VertexIndexMap>
  void betweenness_centrality_clustering(MutableGraph & g, Done done,
                                         EdgeCentralityMap edge_centrality,
                                         VertexIndexMap vertex_index);
template<typename MutableGraph, typename Done, typename EdgeCentralityMap>
  void betweenness_centrality_clustering(MutableGraph & g, Done done,
                                         EdgeCentralityMap edge_centrality);
template<typename MutableGraph, typename Done>
  void betweenness_centrality_clustering(MutableGraph & g, Done done);

Description

This algorithm implements graph clustering based on edge betweenness centrality. It is an iterative algorithm, where in each step it computes the edge betweenness centrality (via brandes_betweenness_centrality) and removes the edge with the maximum betweenness centrality. The done function object determines when the algorithm terminates (the edge found when the algorithm terminates will not be removed).

Parameters

IN: const Graph& g

Apply the algorithm on this graph object.The type Graph must be a model of Vertex List Graph and Incidence Graph. When an edge centrality map is supplied, it must also model Edge List Graph and MutableGraph.
Python: The parameter is named graph.

IN: Done done

The function object that indicates termination of the algorithm. It must be a ternary function object thats accepts the maximum centrality, the descriptor of the edge that will be removed, and the graph g.
Python: Any callable Python object will suffice.

OUT/UTIL: EdgeCentralityMap edge_centrality_map

This property map is used to accumulate the betweenness centrality of each edge, and is a secondary form of output for the algorithm. The type EdgeCentralityMap must be a model of Read/Write Property Map, with the graph's edge descriptor type as its key type. The value type of this property map should be the same as the value type of the CentralityMap property map.
Default: a dummy_property_map, which requires no work to compute and returns no answer.
Python: The color map must be a edge_double_map for the graph.
Python default: graph.get_edge_double_map("centrality")

IN: VertexIndexMap vertex_index

This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g).
Note: Before using this default, make sure your graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Where Defined

<boost/graph/bc_clustering.hpp>

Copyright © 2004
Douglas Gregor, Indiana University (dgregor@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


bellman_ford_shortest_paths

// named paramter version
template <class EdgeListGraph, class Size, class P, class T, class R>
bool bellman_ford_shortest_paths(const EdgeListGraph& g, Size N,
  const bgl_named_params<P, T, R>& params = all defaults);

template <class VertexAndEdgeListGraph, class P, class T, class R>
bool bellman_ford_shortest_paths(const VertexAndEdgeListGraph& g,
  const bgl_named_params<P, T, R>& params = all defaults);

// non-named parameter version
template <class EdgeListGraph, class Size, class WeightMap,
      class PredecessorMap, class DistanceMap,
      class BinaryFunction, class BinaryPredicate,
      class BellmanFordVisitor>
bool bellman_ford_shortest_paths(EdgeListGraph& g, Size N,
  WeightMap weight, PredecessorMap pred, DistanceMap distance,
  BinaryFunction combine, BinaryPredicate compare, BellmanFordVisitor v)

The Bellman-Ford algorithm [4,11,20,8] solves the single-source shortest paths problem for a graph with both positive and negative edge weights. For the definition of the shortest paths problem see Section Shortest-Paths Algorithms. To solve only the shortest paths problem for positive edge weights, Dijkstra's algorithm provides a more efficient alternative. If all the edge weights are all equal to one then breadth-first search provides an even more efficient alternative.

Before calling the bellman_ford_shortest_paths() function, the user must assign the source vertex a distance of zero and all other vertices a distance of infinity unless the user provides a starting vertex. The Bellman-Ford algorithm proceeds by looping through all of the edges in the graph, applying the relaxation operation to each edge. In the following pseudo-code, v is a vertex adjacent to u, w maps edges to their weight, and d is a distance map that records the length of the shortest path to each vertex seen so far. p is a predecessor map which records the parent of each vertex, which will ultimately be the parent in the shortest paths tree

RELAX(u, v, w, d, p)
  if (w(u,v) + d[u] < d[v])
    d[v] := w(u,v) + d[u]
    p[v] := u
  else
    ...

relax edge (u,v)

edge (u,v) is not relaxed

The algorithm repeats this loop |V| times after which it is guaranteed that the distances to each vertex have been reduced to the minimum possible unless there is a negative cycle in the graph. If there is a negative cycle, then there will be edges in the graph that were not properly minimized. That is, there will be edges (u,v) such that w(u,v) + d[u] < d[v]. The algorithm loops over the edges in the graph one final time to check if all the edges were minimized, returning true if they were and returning false otherwise.

BELLMAN-FORD(G)
  // Optional initialization
  for each vertex u in V
    d[u] := infinity
    p[u] := u
  end for
  for i := 1 to |V|-1
    for each edge (u,v) in E
      RELAX(u, v, w, d, p)
    end for
  end for
  for each edge (u,v) in E
    if (w(u,v) + d[u] < d[v])
      return (false, , )
    else
      ...
  end for
  return (true, p, d)

examine edge (u,v)

edge (u,v) was not minimized

edge (u,v) was minimized

There are two main options for obtaining output from the bellman_ford_shortest_paths() function. If the user provides a distance property map through the distance_map() parameter then the shortest distance from the source vertex to every other vertex in the graph will be recorded in the distance map (provided the function returns true). The second option is recording the shortest paths tree in the predecessor_map(). For each vertex u in V, p[u] will be the predecessor of u in the shortest paths tree (unless p[u] = u, in which case u is either the source vertex or a vertex unreachable from the source). In addition to these two options, the user can provide her own custom-made visitor that can take actions at any of the algorithm's event points.

Parameters

IN: EdgeListGraph& g

A directed or undirected graph whose type must be a model of Edge List Graph. If a root vertex is provided, then the graph must also model Vertex List Graph.
Python: The parameter is named graph.

IN: Size N

The number of vertices in the graph. The type Size must be an integer type.
Default: num_vertices(g).
Python: Unsupported parameter.

Named Parameters

IN: weight_map(WeightMap w)

The weight (also know as ``length'' or ``cost'') of each edge in the graph. The WeightMap type must be a model of Readable Property Map. The key type for this property map must be the edge descriptor of the graph. The value type for the weight map must be Addable with the distance map's value type.
Default: get(edge_weight, g)
Python: Must be an edge_double_map for the graph.
Python default: graph.get_edge_double_map("weight")

OUT: predecessor_map(PredecessorMap p_map)

The predecessor map records the edges in the minimum spanning tree. Upon completion of the algorithm, the edges (p[u],u) for all u in V are in the minimum spanning tree. If p[u] = u then u is either the source vertex or a vertex that is not reachable from the source. The PredecessorMap type must be a Read/Write Property Map which key and vertex types the same as the vertex descriptor type of the graph.
Default: dummy_property_map
Python: Must be a vertex_vertex_map for the graph.

IN/OUT: distance_map(DistanceMap d)

The shortest path weight from the source vertex to each vertex in the graph g is recorded in this property map. The type DistanceMap must be a model of Read/Write Property Map. The key type of the property map must be the vertex descriptor type of the graph, and the value type of the distance map must be Less Than Comparable.
Default: get(vertex_distance, g)
Python: Must be a vertex_double_map for the graph.

IN: root_vertex(Vertex s)

The starting (or "root") vertex from which shortest paths will be computed. When provided, the distance map need not be initialized (the algorithm will perform the initialization itself). However, the graph must model Vertex List Graph when this parameter is provided.
Default: None; if omitted, the user must initialize the distance map.

IN: visitor(BellmanFordVisitor v)

The visitor object, whose type must be a model of Bellman-Ford Visitor. The visitor object is passed by value [1].
Default: bellman_visitor<null_visitor>
Python: The parameter should be an object that derives from the BellmanFordVisitor type of the graph.

IN: distance_combine(BinaryFunction combine)

This function object replaces the role of addition in the relaxation step. The first argument type must match the distance map's value type and the second argument type must match the weight map's value type. The result type must be the same as the distance map's value type.
Default:std::plus<D> with D=typename property_traits<DistanceMap>::value_type.
Python: Unsupported parameter.

IN: distance_compare(BinaryPredicate compare)

This function object replaces the role of the less-than operator that compares distances in the relaxation step. The argument types must match the distance map's value type.
Default: std::less<D> with D=typename property_traits<DistanceMap>::value_type.
Python: Unsupported parameter.

Complexity

The time complexity is O(V E).

Visitor Event Points

  • vis.examine_edge(e, g) is invoked on every edge in the graph |V| times.

  • vis.edge_relaxed(e, g) is invoked when the distance label for the target vertex is decreased. The edge (u,v) that participated in the last relaxation for vertex v is an edge in the shortest paths tree.

  • vis.edge_not_relaxed(e, g) is invoked if the distance label for the target vertex is not decreased.

  • vis.edge_minimized(e, g) is invoked during the second stage of the algorithm, during the test of whether each edge was minimized. If the edge is minimized then this function is invoked.

  • vis.edge_not_minimized(e, g) is also invoked during the second stage of the algorithm, during the test of whether each edge was minimized. If the edge was not minimized, this function is invoked. This happens when there is a negative cycle in the graph.

Example

An example of using the Bellman-Ford algorithm is in examples/bellman-example.cpp.

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore, use by pointer or reference for the visitor to hold this state.

Copyright © 2000 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: bellman_visitor

bellman_visitor

bellman_visitor<EventVisitorList>

This class is an adapter that converts a list of EventVisitor's (constructed using std::pair) into a BellmanFordVisitor.

Example

Model of

BellmanFordVisitor

Template Parameters

Parameter | Description | Default
EventVisitorList | A list of EventVisitor's created with std::pair. | null_visitor

Where Defined

boost/graph/bellman_ford_shortest_paths.hpp

Member Functions

This class implements all of the member functions required by BellmanFordVisitor. In each function the appropriate event is dispatched to the EventVisitor's in the EventVisitorList.

Non-Member Functions

Function Description

template <class EventVisitorList>
bellman_visitor<EventVisitorList>
make_bellman_visitor(EventVisitorList ev_list);

Returns the event visitor list adapted to be a BellmanFordVisitor.

See Also

Visitor concepts

The following are event visitors: predecessor_recorder , distance_recorder time_stamper , and property_writer .

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Bellman Ford Visitor

Bellman Ford Visitor Concept

This concept defines the visitor interface for bellman_ford_shortest_paths(). Users can define a class with the Bellman Ford Visitor interface and pass an object of the class to bellman_ford_shortest_paths(), thereby augmenting the actions taken during the graph search.

Refinement of

Copy Constructible (copying a visitor is a lightweight operation).

Notation

V | A type that is a model of Bellman Ford Visitor.
vis | An object of type V.
G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
s,u | An object of type boost::graph_traits<G>::vertex_descriptor.

Associated Types

none

Valid Expressions

Name | Expression | Return Type | Description
Examine Edge | vis.examine_edge(e, g) | void | This is invoked on every edge in the graph num_vertices(g) times.
Edge Relaxed vis.edge_relaxed(e, g) void Upon examination, if the following condition holds then the edge is relaxed (its distance is reduced), and this method is invoked.
tie(u,v) = incident(e, g);
D d_u = get(d, u), d_v = get(d, v);
W w_e = get(w, e);
assert(compare(combine(d_u, w_e), d_v));
Edge Not Relaxed | edge_not_relaxed(e, g) | void | Upon examination, if the edge is not relaxed (see above) then this method is invoked.
Edge Minimized | vis.edge_minimized(e, g) | void | After the num_vertices(g) iterations through the edge set of the graph is complete, one last iteration is made to test whether each edge was minimized. If the edge is minimized then this function is invoked.
Edge Not Minimized | edge_not_minimized(e, g) | void | If the edge is not minimized, this function is invoked. This happens when there is a negative cycle in the graph.

Models

  • bellman_visitor

Python

To implement a model of the BellmanFordVisitor concept in Python, create a new class that derives from the BellmanFordVisitor type of the graph, named GraphType.BellmanFordVisitor. The events and syntax are the same as with visitors in C++. Here is an example for the Python bgl.Graph graph type:

class count_tree_edges_bellman_ford_visitor(bgl.Graph.BellmanFordVisitor)::
  def __init__(self, name_map):
    bgl.Graph.BellmanFordVisitor.__init__(self)
    self.name_map = name_map

  def edge_relaxed(self, e, g):
    (u, v) = (g.source(e), g.target(e))
    print "Relaxed edge ",
    print self.name_map[u],
    print " -> ",
    print self.name_map[v]

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Brandes' Betweenness Centrality

brandes_betweenness_centrality

// named parameter versions
template<typename Graph, typename Param, typename Tag, typename Rest>
void
brandes_betweenness_centrality(const Graph& g,
                               const bgl_named_params<Param,Tag,Rest>& params);

template<typename Graph, typename CentralityMap>
void
brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map);

template<typename Graph, typename CentralityMap, typename EdgeCentralityMap>
void
brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map,
                               EdgeCentralityMap edge_centrality);

// non-named parameter versions
template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
         typename IncomingMap, typename DistanceMap, typename DependencyMap,
         typename PathCountMap, typename VertexIndexMap>
void
brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map,
                               EdgeCentralityMap edge_centrality,
                               IncomingMap incoming,
                               DistanceMap distance, DependencyMap dependency,
                               PathCountMap path_count,
                               VertexIndexMap vertex_index);

template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
         typename IncomingMap, typename DistanceMap, typename DependencyMap,
         typename PathCountMap, typename VertexIndexMap, typename WeightMap>
void
brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map,
                               EdgeCentralityMap edge_centrality,
                               IncomingMap incoming,
                               DistanceMap distance,  DependencyMap dependency,
                               PathCountMap path_count,
                               VertexIndexMap vertex_index,
                               WeightMap weight_map);

// helper functions
template<typename Graph, typename CentralityMap>
void
relative_betweenness_centrality(const Graph& g, CentralityMap centrality_map);

template<typename Graph, typename CentralityMap>
typename property_traits<CentralityMap>::value_type
central_point_dominance(const Graph& g, CentralityMap centrality_map);

This algorithm [54] computes the betweenness centrality [55,56] of each vertex or each edge in the graph. The betweenness centrality of a vertex v is defined by

,

where [image] is the number of shortest paths from vertex s to vertex t and [image] is the number of shortest paths from vertex s to vertex t that pass through vertex v.

The edge betweenness centrality indicates for each edge the betweenness centrality that was contributed to the target(s) of the edge (plural for undirected graphs). Similar to (vertex) betweenness centrality, edge betweenness centrality can be used to determine the edges through which most shortest paths must pass. A single invocation of this algorithm can compute either the vertex or edge centrality (or both).

This algorithm operates either on weighted graphs (if a suitable edge weight map is supplied) or unweighted graphs (if no edge weight map is supplied). The result is the absolute betweenness centrality; to convert to the relative betweenness centrality, which scales each absolute centrality by [image] (where n is the number of vertices in the graph), use relative_betweenness_centrality. Given the relative betweenness centrality, one can compute the central point dominance [55], which is a measure of the maximum "betweenness" of any point in the graph: it will be 0 for complete graphs and 1 for "wheel" graphs (in which there is a central vertex that all paths include; see Fig. 1). Let [image] be the vertex with the largest relative betweenness centrality; then, the central point dominance is defined as:

Fig. 1: A wheel graph, where every path travels through the central node.
The central point dominance of this graph is 1.

Where Defined

boost/graph/betweenness_centrality.hpp

Parameters

IN: const Graph& g

Applies the algorithm on this graph object. The type Graph must be a model of Vertex List Graph and Incidence Graph. When an edge centrality map is supplied, it must also model Edge List Graph.
Python: The parameter is named graph.

UTIL: IncomingMap incoming

This property map records the set of edges incoming to each vertex that comprise a shortest path from a particular source vertex through this vertex, and is used internally by the algorithm. The IncomingMap type must be a Lvalue Property Map whose key type is the same as the vertex descriptor type of the graph and whose value type is a Sequence (e.g., an std::vector) containing edge descriptors.
Default: iterator_property_map created from a std::vector of std::vector<Edge>, where Edge is the edge descriptor type of the graph.
Python: Unsupported parameter.

UTIL: DistanceMap distance_map

The shortest path weight from each source vertex s to each vertex in the graph g is recorded in this property map, but the result is only used internally. The type DistanceMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the distance map. The value type of the distance map is the element type of a Monoid.
Default: iterator_property_map created from a std::vector of the WeightMap's value type (or the vertices_size_type of the graph when no weight map exists) of size num_vertices(g) and using the vertex_index for the index map.
Python: Unsupported parameter.

UTIL: DependencyMap dependency

Property map used internally to accumulate partial betweenness centrality results. The type DependencyMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the dependency map. The value type of the dependency map must be compatible with the value type of the centrality map.
Default: iterator_property_map created from a std::vector of the CentralityMap's value type of size num_vertices(g) and using the vertex_index for the index map.
Python: Unsupported parameter.

UTIL: PathCountMap path_count

Property map used internally to accumulate the number of paths that pass through each particular vertex. The type PathCountMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the dependency map. The value type of the dependency map must be an integral type large enough to store the number of paths in the graph.
Default: iterator_property_map created from a std::vector of the degree_size_type of the graph of size num_vertices(g) and using the vertex_index for the index map.
Python: Unsupported parameter.

Named parameters

OUT/UTIL: CentralityMap centrality_map

This property map is used to accumulate the betweenness centrality of each vertex, and is the primary output of the algorithm. The type CentralityMap must be a model of Read/Write Property Map, with the graph's vertex descriptor type as its key type. The value type of this property map should be a floating-point or rational type.
Default: a dummy_property_map, which requires no work to compute and returns no answer.
Python: The color map must be a vertex_double_map for the graph.
Python default: graph.get_vertex_double_map("centrality")

OUT/UTIL: EdgeCentralityMap edge_centrality_map

This property map is used to accumulate the betweenness centrality of each edge, and is a secondary form of output for the algorithm. The type EdgeCentralityMap must be a model of Read/Write Property Map, with the graph's edge descriptor type as its key type. The value type of this property map should be the same as the value type of the CentralityMap property map.
Default: a dummy_property_map, which requires no work to compute and returns no answer.
Python: The color map must be a edge_double_map for the graph.
Python default: graph.get_edge_double_map("centrality")

IN: vertex_index_map(VertexIndexMap vertex_index)

This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g). Note: Use this default, only when the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

IN: weight_map(WeightMap w_map)

The weight or ``length'' of each edge in the graph. The weights must all be non-negative, and the algorithm will throw a negative_edge exception is one of the edges is negative. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for this map must be the same as the value type of the distance map.
Default: All edge weights are assumed to be equivalent.
Python: If supplied, must be an edge_double_map for the graph.

Complexity

The time complexity is O(VE) for unweighted graphs and O(VE + V(V+E) log V) for weighted graphs. The space complexity is O(VE).

Copyright © 2004
Douglas Gregor, Indiana University (dgregor@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: bfs_visitor

bfs_visitor

bfs_visitor<EventVisitorList>
This class is an adapter that converts a list of EventVisitor's (constructed using std::pair) into a BFSVisitor.

Example

This is an excerpt from examples/bfs.cpp where three event-visitors are combined to make a BFS visitor. The functions boost::record_distances, boost::record_predecessors, and copy_graph are all functions that create an event visitor.
  // Construct graph G and obtain the source vertex s ...

  boost::breadth_first_search(G, s,
  boost::make_bfs_visitor(
  std::make_pair(boost::record_distances(d, boost::on_tree_edge()),
  std::make_pair(boost::record_predecessors(p.begin(),
  boost::on_tree_edge()),
  copy_graph(G_copy, boost::on_examine_edge())))) );

Model of

BFSVisitor

Template Parameters

Parameter | Description | Default
EventVisitorList | A list of EventVisitor's created with std::pair. | null_visitor

Where Defined

boost/graph/breadth_first_search.hpp

Member Functions

This class implements all of the member functions required by BFSVisitor. In each function the appropriate event is dispatched to the EventVisitor's in the EventVisitorList.

Non-Member Functions

Function Description
template <class EventVisitorList>
bfs_visitor<EventVisitorList>
make_bfs_visitor(EventVisitorList ev_list); 		Returns the event visitor list adapted to be a BFS visitor.

See Also

Visitor concepts

The following are event visitors: predecessor_recorder, distance_recorder, time_stamper, and property_writer.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: BFSVisitor

BFS Visitor Concept

This concept defines the visitor interface for breadth_first_search(). Users can define a class with the BFS Visitor interface and pass and object of the class to breadth_first_search(), thereby augmenting the actions taken during the graph search.

Refinement of

Copy Constructible (copying a visitor should be a lightweight operation).

Notation

V | A type that is a model of BFS Visitor.
vis | An object of type V.
G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
s,u | An object of type boost::graph_traits<G>::vertex_descriptor.

Associated Types

none

Valid Expressions

Name | Expression | Return Type | Description
Initialize Vertex | vis.initialize_vertex(s, g) | void | This is invoked on every vertex of the graph before the start of the graph search.
Discover Vertex | vis.discover_vertex(u, g) | void | This is invoked when a vertex is encountered for the first time.
Examine Vertex | vis.examine_vertex(u, g) | void | This is invoked on a vertex as it is popped from the queue. This happens immediately before examine_edge() is invoked on each of the out-edges of vertex u.
Examine Edge | vis.examine_edge(e, g) | void | This is invoked on every out-edge of each vertex after it is discovered.
Tree Edge | vis.tree_edge(e, g) | void | This is invoked on each edge as it becomes a member of the edges that form the search tree.
Non-Tree Edge | vis.non_tree_edge(e, g) | void | This is invoked on back or cross edges for directed graphs and cross edges for undirected graphs.
Gray Target | vis.gray_target(e, g) | void | This is invoked on the subset of non-tree edges who's target vertex is colored gray at the time of examination. The color gray indicates that the vertex is currently in the queue.
Black Target | vis.black_target(e, g) | void | This is invoked on the subset of non-tree edges who's target vertex is colored black at the time of examination. The color black indicates that the vertex has been removed from the queue.
Finish Vertex | vis.finish_vertex(u, g) | void | This invoked on a vertex after all of its out edges have been added to the search tree and all of the adjacent vertices have been discovered (but before the out-edges of the adjacent vertices have been examined).

Models

  • bfs_visitor

Python

To implement a model of the BFSVisitor concept in Python, create a new class that derives from the BFSVisitor type of the graph, which will be named GraphType.BFSVisitor. The events and syntax are the same as with visitors in C++. Here is an example for the Python bgl.Graph graph type:
class count_tree_edges_bfs_visitor(bgl.Graph.BFSVisitor):
  def __init__(self, name_map):
    bgl.Graph.BFSVisitor.__init__(self)
    self.name_map = name_map

  def tree_edge(self, e, g):
    (u, v) = (g.source(e), g.target(e))
    print "Tree edge ",
    print self.name_map[u],
    print " -> ",
    print self.name_map[v]

See also

Visitor concepts
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Named Parameters

bgl named params

bgl_named_params<Param, Type, Rest>

Many of the Boost.Graph algorithms have a long list of parameters, most of which have default values. This causes several problems. First, C++ does not provide a mechanism for handling default parameters of template functions. However, this can be overcome by creating multiply version of an algorithm with different numbers of parameters with each version providing defaults for some subset of the parameters. This is the approach used in previous versions of Boost.Graph. This solution is still unsatisfactory for several reasons:

  • The defaults for parameters can only been used in a particular order. If the ordering of the defaults does not fit the users situation he or she has to resort to providing all the parameters.
  • Since the list of parameters is long, it is easy to forget the ordering.

A better solution is provided by bgl_named_params. This class allows users to provide parameters is any order, and matches arguments to parameters based on parameter names.

The following code shows an example of calling bellman_ford_shortest_paths using the named parameter technique. Each of the arguments is passed to a function whose name indicates which parameter the argument is for. Each of the named parameters is separated by a period, not a comma.

  bool r = boost::bellman_ford_shortest_paths(g, int(N),
     boost::weight_map(weight).
     distance_map(&distance[0]).
     predecessor_map(&paren     predecessor_map(&parent[0]));

The order in which the arguments are provided does not matter as long as they are matched with the correct parameter function. Here is an call to bellman_ford_shortest_paths that is equivalent to the one above.

  bool r = boost::bellman_ford_shortest_paths(g, int(N),
     boost::predecessor_map(&parent[0]).
     distance_map(&distance[0]).
     weight_map(weight));

Typically the user never needs to deal with the bgl_named_params class directly, since there are functions like boost::weight_map that create an instance of bgl_named_params.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Bibliography

Bibliography

1

A. V. Aho, J. E. Hopcroft, and J. D. Ullman.
Data Structures and Algorithms.
Addison-Wesley, 1983.

2

M. H. Austern.
Generic Programming and the STL.
Professional computing series. Addison-Wesley, 1999.

3

G. Baumgartner and V. F. Russo.
Signatures: A language extension for improving type abstraction and subtype polymorphism in C++.
Software-Practice and Experience, 25(8):863-889, August 1995.

4

R. Bellman.
On a routing problem.
Quarterly of Applied Mathematics, 16(1):87-90, 1958.

5

K. B. Bruce, L. Cardelli, G. Castagna, the Hopkins Objects Group, G. T. Leavens, and B. Pierce.
On binary methods.
Theory and Practice of Object Systems, 1:221-242, 1995.

6

T. F. Coleman, B. S. Garbow, and J. J. Mor'e.
Algorithm 649: Fortran subroutines for estimating sparse hessian matrices.
ACM Transactions on Mathematical Software, 11(4):378, December 1985.

7

T. F. Coleman and J. J. Mor'e.
Estimation of sparse jacobian matrices and graph coloring problems.
SIAM Journal on Numerical Analysis, 20:187-209,, 1984.

8

T. Cormen, C. Leiserson, and R. Rivest.
Introduction to Algorithms.
McGraw-Hill, 1990.

9

A. Curtis, M. Powell, and J. Reid.
On the estimation of sparse jacobian matrices.
Journal of the Institute of Mathematics and its Applications, 13:117-119, 1974.

10

E. Dijkstra.
A note on two problems in connexion with graphs.
Numerische Mathematik, 1:269-271, 1959.

11

L. R. Ford and D. R. Fulkerson.
Flows in networks.
Princeton University Press, 1962.

12

E. Gamma, R. Helm, R. Johnson, and J. Vlissides.
Design Patterns: Elements of Reusable Object-Oriented Software.
Professional Computing. Addison-Welsey, 1995.

13

A. George, J. R. Gilbert, and J. W. Liu, editors.
Graph Theory and Sparse Matrix Computation.
Springer-Verlag New York, Inc, 1993.

14

A. George and J. W.-H. Liu.
Computer Solution of Large Sparse Positive Definite Systems.
Computational Mathematics. Prentice-Hall, 1981.

15

R. Graham and P. Hell.
On the history of the minimum spanning tree problem.
Annals of the History of Computing, 7(1):43-57, 1985.

16

P. E. Hart, N. J. Nilsson, and B. Raphael.
A formal basis for the heuristic determination of minimum cost paths.
IEEE Transactions on Systems Science and Cybernetics, 4(2):100-107, 1968.

18

J. B. Kruskal.
On the shortest spanning subtree of a graph and the traveling salesman problem.
In Proceedings of the American Mathematical Sofiety, volume 7, pages 48-50, 1956.

19

D. Kühl.
Design patterns for the implementation of graph algorithms.
Master's thesis, Technische Universität Berlin, July 1996.

20

E. L. Lawler.
Combinatorial Opimization: Networks and Matroids.
Holt, Rinehart, and Winston, 1976.

21

J. W. H. Liu.
Modification of the minimum-degree algorithm by multiple elimination.
ACM Transaction on Mathematical Software, 11(2):141-153, 1985.

22

K. Mehlhorn and S. Näher.
The LEDA Platform of Combinatorial and Geometric Computing.
Cambridge University Press, 1999.

23

B. Meyer.
Object-oriented Software Construction.
Prentice Hall International Series in Computer Science. Prentice Hall, 1988.

24

N. C. Myers.
Traits: a new and useful template technique.
C++ Report, June 1995.

25

R. Prim.
Shortest connection networks and some generalizations.
Bell System Technical Journal, 36:1389-1401, 1957.

26

Y. Saad.
Iterative Methods for Sparse Minear System.
PWS Publishing Company, 1996.

27

R. E. Tarjan.
Data Structures and Network Algorithms.
Society for Industrial and Applied Mathematics, 1983.

28

Seymour Parter.
The use of linear graphs in Gauss elimination.
SIAM Review, 1961 3:119-130.

29

D. Matula, G. Marble, and J. Isaacson
Graph coloring algorithms in Graph Theory and Computing.
Academic Press, pp.104-122, 1972.

30

M.R. Garey and D.S. Johnson
Computers and Intractibility: A Guide to the Theory of NP-Completeness
W.H. Freeman, New York, 1979.

31

D. Welsch and M. B. Powel
An upper bound for the chromatic number of a graph and its application to timetabling problems Computer Journal, 10:85-86, 1967.

32

D. Br'elaz
New methods to color the vertices of a graph
Communications of the ACM, vol. 22, 1979, pp. 251-256.

33

G. Heber, R. Biswas, G.R. Gao
Self-Avoiding Walks over Adaptive Unstructured Grids
Parallel and Distributed Processing, LNCS 1586, Springer-Verlag, 1999, pp. 968-977

34

Esmond G. Ng amd Padma Raghavan
Performance of greedy ordering heuristics for sparse {C}holesky factorization
SIAM Journal on Matrix Analysis and Applications (To appear)

35

Alan George and Joseph W. H. Liu
The Evolution of the Minimum Degree Ordering Algorithm
SIAM Review, March 1989, vol. 31, num. 1, pp. 1-19.

36

L. R. Ford and D. R. Fulkerson
Maximal flow through a network.
Can. Journal of Mathematics 1956 pp.399-404

37

A. V. Goldberg
A New Max-Flow Algorithm.
MIT Tehnical report MIT/LCS/TM-291, 1985.

38

A. V. Karzanov
Determining the maximal flow in a network by the method of preflows.
Sov. Math. Dokl. 1974

39

Ravindra K. Ahuja and Thomas L. Magnanti and James B. Orlin
Network Flows: Theory, Algorithms, and Applications.
Prentice Hall, 1993.

40

Jack Edmonds and Richard M. Karp
Theoretical improvements in the algorithmic efficiency for network flow problems.
Journal of the ACM, 1972 19:248-264

41

Robert E. Tarjan
Depth first search and linear graph algorithms.
SIAM Journal on Computing, 1(2):146-160, 1972

42

David Eppstein, Zvi Galil, and Giuseppe F. Italiano
Dynamic Graph Algorithms.
Chapter 22, CRC Handbook of Algorithms and Theory of Computation, 1997.

43

E. Cuthill and J. McKee
Reducing the bandwidth of sparse symmetric matrices.
Proceedings of the 24th National Conference of the ACM, 1969.

44

J. Liu and A. Sherman
Comparative analysis of the Cuthill-Mckee and the reverse Cuthill-Mckee ordering algorithms for sparse matrices.
SIAM Journal of Numerical Analysis. 13 (1975), pp. 198-213.

45

Alan George
Computer implementation of the finite element method
Technical Report STAN-CS-208, Stanford University (1971).

46

Scott Fortin
The Graph Isomorphism Problem
TR 96-20, Dept. of Computer Science, University of Alberta (1996)

47

Brendan D. McKay
Practical Graph Isomorphism
Congressus Numerantium (1981)

48

Reingold, Nievergelt, and Deo
Combinatorial Algorithms: Theory and Practice
Prentice Hall (1977)

49

Edward Moore
The shortest path through a maze
International Symposium on the Theory of Switching (1959)
Harvard University Press

50

E. Nuutila
Efficient transitive closure computation in large digraphs
PhD Thesis, Helsinki University of Technology, 1995.
Acta Polytechnica Scandinavica, Mathematics and Computing in Engineering Series, No. 74.

51

A. Goralcikova and V. Koubek
A reduct and closure algorithm for graphs
In Mathematical Foundations of Computer Science,
volume 74 of Lecture Notes in Computer Science, pages 301-307.
Springer-Verlag, 1979

52

Klaus Simon
An Improved Algorithm for Transitive Closure on Acyclic Digraphs
Theoretical Computer Science 58
Automata, Languages and Programming, 376-386, 1986

53

P. Purdom
A Transitive Closure Algorithm
BIT, 10, 1970, pp. 76-94.

54

Ulrik Brandes
A Faster Algorithm for Betweenness Centrality
Journal of Mathematical Sociology 25 (2):163-177, 2001.

55

Lindon C. Freeman
A Set of Measures of Centrality Based on Betweenness
Sociometry 40, pp. 35-41, 1977.

56

J.M. Anthonisse
The rush in a directed graph.
Technical Report BN9/71, Stichting Mahtematisch Centrum, Amsterdam, 1971.

57

T. Kamada and S. Kawai
An algorithm for drawing general undirected graphs.
Information Processing Letters, 31, pp. 7-15, 1989.

58

T. Fruchterman and E. Reingold
Graph drawing by force-directed placement.
Software--Practice & Experience, 21 (11), pp. 1129-1164, 1991.

59

Thomas F. Coleman and Jorge J. More
Estimation of sparse Jacobian matrices and graph coloring problems.
Journal of Numerical Analasis V20, pp. 187-209, 1983.

60

Attila Gürsoy and Murat Atun
Neighborhood Preserving Load Balancing: A Self-Organizing Approach
Euro-Par Parallel Processing, LNCS 1900, pp. 324-41, 2000.

61

James R. Driscoll, Harold N. Gabow, Ruth Shrairman, and Robert E. Tarjan
Relaxed Heaps: An alternative for Fibonacci Heaps with applications to parallel computation.
Communications of the ACM, 31 (11), pp. 1343-1354, November 1988.

62

King, I. P.
An automatic reordering scheme for simultaneous equations derived from network analysis.
Int. J. Numer. Methods Engrg. 2, pp. 523-533, 1970.

63

C. Palmer and J. Steffan
Generating Network Topologies That Obey Power Laws
Proceedings of GLOBECOM. November, 2000.

64

J. Edmonds
Paths, trees, and flowers
Canadian Journal of Mathematics 17 (1965), pp. 449-467.

65

Thomas Lengauer and Robert Endre Tarjan
A fast algorithm for finding dominators in a flowgraph
ACM Transactions on Programming Language and Systems, 1(1):121-141, 1979.

66

Steven S. Muchnick
Advanced Compiler Design and Implementation
Morgan Kaufmann Publishers, San Fransisco, 1997.

67

Andrew W. Appel
Modern Compiler Implementation in JAVA
Cambridge University Press, 1998.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Biconnected Components and Articulation Points

biconnected_components and articulation_points

// named parameter version
template <typename Graph, typename ComponentMap, typename OutputIterator,
          typename P, typename T, typename R>
std::pair<std::size_t, OutputIterator>
biconnected_components(const Graph& g, ComponentMap comp, OutputIterator out,
                       const bgl_named_params<P, T, R>& params)

template <typename Graph, typename ComponentMap,
          typename P, typename T, typename R>
std::size_t
biconnected_components(const Graph& g, ComponentMap comp,
                       const bgl_named_params<P, T, R>& params)

template <typename Graph, typename OutputIterator,
          typename P, typename T, typename R>
OutputIterator articulation_points(const Graph& g, OutputIterator out,
                                   const bgl_named_params<P, T, R>& params)

// non-named parameter version
template <typename Graph, typename ComponentMap, typename OutputIterator,
          typename DiscoverTimeMap, typename LowPointMap>
std::pair<std::size_t, OutputIterator>
biconnected_components(const Graph& g, ComponentMap comp, OutputIterator out,
                       DiscoverTimeMap discover_time, LowPointMap lowpt);

template <typename Graph, typename ComponentMap, typename OutputIterator>
std::pair<std::size_t, OutputIterator>
biconnected_components(const Graph& g, ComponentMap comp, OutputIterator out);

template <typename Graph, typename ComponentMap>
std::size_t biconnected_components(const Graph& g, ComponentMap comp);

template <typename Graph, typename OutputIterator>
OutputIterator articulation_points(const Graph& g, OutputIterator out);

A connected graph is biconnected if the removal of any single vertex (and all edges incident on that vertex) can not disconnect the graph. More generally, the biconnected components of a graph are the maximal subsets of vertices such that the removal of a vertex from a particular component will not disconnect the component. Unlike connected components, vertices may belong to multiple biconnected components: those vertices that belong to more than one biconnected component are called articulation points or, equivalently, cut vertices. Articulation points are vertices whose removal would increase the number of connected components in the graph. Thus, a graph without articulation points is biconnected. The following figure illustrates the articulation points and biconnected components of a small graph:

Vertices can be present in multiple biconnected components, but each edge can only be contained in a single biconnected component. The output of the biconnected_components algorithm records the biconnected component number of each edge in the property map comp. Articulation points will be emitted to the (optional) output iterator argument to biconnected_components, or can be computed without the use of a biconnected component number map via articulation_points. These functions return the number of biconnected components, the articulation point output iterator, or a pair of these quantities, depending on what information was recorded.

The algorithm implemented is due to Tarjan [41].

Where Defined

boost/graph/biconnected_components.hpp

Parameters

IN: const Graph& g

An undirected graph. The graph type must be a model of Vertex List Graph and Incidence Graph.
Python: The parameter is named graph.

OUT: ComponentMap c

The algorithm computes how many biconnected components are in the graph, and assigning each component an integer label. The algorithm then records which component each edge in the graph belongs to by recording the component number in the component property map. The ComponentMap type must be a model of Writable Property Map. The value type shouch be an integer type, preferably the same as the edges_size_type of the graph. The key type must be the graph's edge descriptor type.
Default: dummy_property_map.
Python: Must be an edge_int_map for the graph.
Python default: graph.get_edge_int_map("bicomponent")

OUT: OutputIterator out

The algorithm writes articulation points via this output iterator and returns the resulting iterator.
Default: a dummy iterator that ignores values written to it.
Python: this parameter is not used in Python. Instead, both algorithms return a Python list containing the articulation points.

Named Parameters

IN: vertex_index_map(VertexIndexMap i_map)

This maps each vertex to an integer in the range [0, num_vertices(g)). The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g)
Python: Unsupported parameter.

UTIL/OUT: discover_time_map(DiscoverTimeMap discover_time)

The discovery time of each vertex in the depth-first search. The type DiscoverTimeMap must be a model of Read/Write Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: an iterator_property_map created from a std::vector of vertices_size_type of size num_vertices(g) and using get(vertex_index, g) for the index map.
Python: Unsupported parameter.

UTIL/OUT: lowpoint_map(LowPointMap lowpt)

The low point of each vertex in the depth-first search, which is the smallest vertex reachable from a given vertex with at most one back edge. The type LowPointMap must be a model of Read/Write Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: an iterator_property_map created from a std::vector of vertices_size_type of size num_vertices(g) and using get(vertex_index, g) for the index map.
Python: Unsupported parameter.

UTIL/OUT: predecessor_map(PredecessorMap p_map)

The predecessor map records the depth first search tree. The PredecessorMap type must be a Read/Write Property Map whose key and value types are the same as the vertex descriptor type of the graph.
Default: an iterator_property_map created from a std::vector of vertex_descriptor of size num_vertices(g) and using get(vertex_index, g) for the index map.
Python: Must be a vertex_vertex_map for the graph.

IN: visitor(DFSVisitor vis)

A visitor object, invoked inside the algorithm at the event-points specified by the DFS Visitor concept. The visitor object is passed by value.
Default: dfs_visitor<null_visitor>
Python: The parameter should be an object that derives from the DFSVisitor type of the graph.

Complexity

The time complexity for the biconnected components and articulation points algorithms O(V + E).

Example

The file examples/biconnected_components.cpp contains an example of calculating the biconnected components and articulation points of an undirected graph.

Copyright © 2000-2004
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Doug Gregor, Indiana University


Bidirectional

BidirectionalGraph

The BidirectionalGraph concept refines IncidenceGraph and adds the requirement for efficient access to the in-edges of each vertex. This concept is separated from IncidenceGraph because for directed graphs efficient access to in-edges typically requires more storage space, and many algorithms do not require access to in-edges. For undirected graphs this is not an issue, since the in_edges() and out_edges() functions are the same, they both return the edges incident to the vertex.

Refinement of

IncidenceGraph

Notation

G | A type that is a model of Graph.
g | An object of type G.
v | An object of type boost::graph_traits<G>::vertex_descriptor.

Associated Types

boost::graph_traits<G>::traversal_category

This tag type must be convertible to bidirectional_graph_tag.

boost::graph_traits<G>::in_edge_iterator

An in-edge iterator for a vertex v provides access to the in-edges of v. As such, the value type of an in-edge iterator is the edge descriptor type of its graph. An in-edge iterator must meet the requirements of MultiPassInputIterator.

Valid Expressions

in_edges(v, g) | Returns an iterator-range providing access to the in-edges (for directed graphs) or incident edges (for undirected graphs) of vertex v in graph g. For both directed and undirected graphs, the target of an out-edge is required to be vertex v and the source is required to be a vertex that is adjacent to v. Return type: std::pair<in_edge_iterator, in_edge_iterator>
in_degree(v, g) | Returns the number of in-edges (for directed graphs) or the number of incident edges (for undirected graphs) of vertex v in graph g. Return type: degree_size_type
degree(v, g) | Returns the number of in-edges plus out-edges (for directed graphs) or the number of incident edges (for undirected graphs) of vertex v in graph g. Return type: degree_size_type

Models

  • adjacency_list with Directed=bidirectionalS

  • adjacency_list with Directed=undirectedS

Complexity guarantees

The in_edges() function is required to be constant time. The in_degree() and degree() functions must be linear in the number of in-edges (for directed graphs) or incident edges (for undirected graphs).

See Also

Graph concepts

Concept Checking Class

  template <class G>
  struct BidirectionalGraphConcept
  {
    typedef typename boost::graph_traits<G>::in_edge_iterator
      in_edge_iterator;
    void constraints() {
      function_requires< IncidenceGraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<in_edge_iterator> >();

      p = in_edges(v, g);
      e = *p.first;
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = in_edges(v, g);
      e = *p.first;
    }
    std::pair<in_edge_iterator, in_edge_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor v;
    typename boost::graph_traits<G>::edge_descriptor e;
    G g;
  };

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Name

Bidirectional Graph: Refines IncidenceGraph and adds the requirement for efficient access to the in-edges of each vertex.

Synopsis

template <class G>
  struct BidirectionalGraphConcept
  {
    typedef typename boost::graph_traits<G>::in_edge_iterator
      in_edge_iterator;
    void constraints() {
      function_requires< IncidenceGraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<in_edge_iterator> >();

      p = in_edges(v, g);
      e = *p.first;
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = in_edges(v, g);
      e = *p.first;
    }
    std::pair<in_edge_iterator, in_edge_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor v;
    typename boost::graph_traits<G>::edge_descriptor e;
    G g;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia


Boost Graph Library: Breadth-First Search

breadth first search

breadth_first_search

// named parameter version

template <class Graph, class P, class T, class R>
void breadth_first_search(Graph& G,
  typename graph_traits<Graph>::vertex_descriptor s,
  const bgl_named_params<P, T, R>& params);

// non-named parameter version

template <class Graph, class Buffer, class BFSVisitor,
      class ColorMap>
void breadth_first_search(const Graph& g,
   typename graph_traits<Graph>::vertex_descriptor s,
   Buffer& Q, BFSVisitor vis, ColorMap color);

The breadth_first_search() function performs a breadth-first traversal of a directed or undirected graph. A breadth-first traversal visits vertices that are closer to the source before visiting vertices that are further away. In this context ``distance'' is defined as the number of edges in the shortest path from the source vertex. The breadth_first_search() function can be used to compute the shortest path from the source to all reachable vertices and the resulting shortest-path distances. For more definitions related to BFS see section Breadth-First Search.

BFS uses two data structures to to implement the traversal: a color marker for each vertex and a queue. White vertices are undiscovered while gray vertices are discovered but have undiscovered adjacent vertices. Black vertices are discovered and are adjacent to only other black or gray vertices. The algorithm proceeds by removing a vertex u from the queue and examining each out-edge (u,v). If an adjacent vertex v is not already discovered, it is colored gray and placed in the queue. After all of the out-edges are examined, vertex u is colored black and the process is repeated. Pseudo-code for the BFS algorithm is a listed below.

BFS(G, s)
  for each vertex u in V[G]
    color[u] := WHITE
    d[u] := infinity
    p[u] := u
  end for
  color[s] := GRAY
  d[s] := 0
  ENQUEUE(Q, s)
  while (Q != Ø)
    u := DEQUEUE(Q)
    for each vertex v in Adj[u]
      if (color[v] = WHITE)
        color[v] := GRAY
        d[v] := d[u] + 1
        p[v] := u
        ENQUEUE(Q, v)
      else
        if (color[v] = GRAY)
          ...
        else
          ...
    end for
    color[u] := BLACK
  end while
  return (d, p)

initialize vertex u

discover vertex s

examine vertex u
examine edge (u,v)
(u,v) is a tree edge

discover vertex v
(u,v) is a non-tree edge

(u,v) has a gray target

(u,v) has a black target

finish vertex u

The breadth_first_search() function can be extended with user-defined actions that will be called a certain event points. The actions must be provided in the form of a visitor object, that is, an object who's type meets the requirements for a BFS Visitor. In the above pseudo-code, the event points are the labels on the right. Also a description of each event point is given below. By default, the breadth_first_search() function does not carry out any actions, not even recording distances or predecessors. However these can be easily added using the distance_recorder and predecessor_recorder event visitors.

Where Defined

boost/graph/breadth_first_search.hpp

Parameters

IN: Graph& g
A directed or undirected graph. The graph type must be a model of Vertex List Graph and Incidence Graph.
Python: The parameter is named graph.
IN: vertex_descriptor s

The source vertex where the search is started.
Python: The parameter is named root_vertex.

Named Parameters

IN: visitor(BFSVisitor vis)
A visitor object that is invoked inside the algorithm at the event-points specified by the BFS Visitor concept. The visitor object is passed by value [1].
Default: bfs_visitor<null_visitor>
Python: The parameter should be an object that derives from the BFSVisitor type of the graph.
UTIL/OUT: color_map(ColorMap color)
This is used by the algorithm to keep track of its progress through the graph. The user need not initialize the color map before calling breadth_first_search() since the algorithm initializes the color of every vertex to white at the start of the algorihtm.To perform multiple breadth-first searches on a graph (for example, if there are some disconnected components) then use the breadth_first_visit() function and do own color initialization.

The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.

IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)]. This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g). Note: if this is used as default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=list does not have an internal vertex_index property.
Python: Unsupported parameter.
UTIL: buffer(Buffer& Q)
The queue used to determine the order in which vertices will be discovered. If a FIFO queue is used, then the traversal will be according to the usual BFS ordering. Other types of queues can be used, but the traversal order will be different. For example Dijkstra's algorithm can be implemented using a priority queue. The type Buffer must be a model of Buffer.
The value_type of the buffer must be the vertex_descriptor type for the graph.
Default: boost::queue
Python: The buffer must derive from the Buffer type for the graph.

Complexity

The time complexity is O(E + V).

Visitor Event Points

  • vis.initialize_vertex(v, g) is invoked on every vertex before the start of the search.
  • vis.examine_vertex(u, g)r is invoked in each vertex as it is removed from the queue.
  • vis.examine_edge(e, g) is invoked on every out-edge of each vertex immediately after the vertex is removed from the queue.
  • vis.tree_edge(e, g) is invoked (in addition to examine_edge()) if the edge is a tree edge. The target vertex of edge e is discovered at this time.
  • vis.discover_vertex(u, g) is invoked the first time the algorithm encounters vertex u. All vertices closer to the source vertex have been discovered, and vertices further from the source have not yet been discovered.
  • vis.non_tree_edge(e, g) is invoked (in addition to examine_edge()) if the edge is not a tree edge.
  • vis.gray_target(e, g) is invoked (in addition to non_tree_edge()) if the target vertex is colored gray at the time of examination. The color gray indicates that the vertex is currently in the queue.
  • vis.black_target(e, g) is invoked (in addition to non_tree_edge()) if the target vertex is colored black at the time of examination. The color black indicates that the vertex is no longer in the queue.
  • vis.finish_vertex(u, g) is invoked after all of the out edges of u have been examined and all of the adjacent vertices have been discovered.

Example

The example in example/bfs-example.cpp demonstrates using the BGL Breadth-first search algorithm on the graph from Figure 5. The file example/bfs-example2.cpp contains the same example, except that the adacency_list class used has VertexList and EdgeList set to lists.

See Also

bfs_visitor and depth_first_search()

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore the user may want the visitor to hold this state by pointer or reference.

Copyright © 2000-2001
 Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Boost Graph Library: Breadth-First Visit

breadth_first_visit

template <class IncidenceGraph, class P, class T, class R>
  void breadth_first_visit(IncidenceGraph& G,
    typename graph_traits<IncidenceGraph>::vertex_descriptor s,
    const bgl_named_params<P, T, R>& params);

  template <class IncidenceGraph, class Buffer, class BFSVisitor, class ColorMap>
  void breadth_first_visit
    (const IncidenceGraph& g,
     typename graph_traits<IncidenceGraph>::vertex_descriptor s,
     Buffer& Q, BFSVisitor vis, ColorMap color)
This function is basically the same as breadth_first_search() except that the color markers are not initialized in the algorithm. The user is responsible for making sure the color for every vertex is white before calling the algorithm. With this difference, the graph type is only required to be an Incidence Graph instead of a Vertex List Graph. Also, this difference allows for more flexibility in the color property map. For example, one could use a map that only implements a partial function on the vertices, which could be more space efficient when the search only reaches a small portion of the graph.

Where Defined

boost/graph/breadth_first_search.hpp

Parameters

IN: IncidenceGraph& g
A directed or undirected graph. The graph type must be a model of Incidence Graph.
Python: The parameter is named graph/tt>..
IN: vertex_descriptors
The source vertex where the search is started.
Python: The parameter is named root_vertex.

Named Parameters

IN: visitor(BFSVisitor vis)
A visitor object that is invoked inside the algorithm at the event-points specified by the BFS Visitor concept. The visitor object is passed by value [1].
Default: bfs_visitor<null_visitor>
Python: The parameter should be an object that derives from the BFSVisitor type of the graph.
UTIL/OUT: color_map(ColorMap color)
This is used by the algorithm to keep track of its progress through the graph. The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue.
Default: get(vertex_color, g)
Python: The color map must be a vertex_color_map for the graph.
UTIL: buffer(Buffer& Q)
The queue used to determine the order in which vertices will be discovered. If a FIFO queue is used, then the traversal will be according to the usual BFS ordering. Other types of queues can be used, but the traversal order will be different. For example Dijkstra's algorithm can be implemented using a priority queue. The type Buffer must be a model of Buffer.
Default: boost::queue
Python: The buffer must derive from the Buffer type for the graph.

Complexity

The time complexity is O(E).

Visitor Event Points

  • vis.initialize_vertex(v, g) is invoked on every vertex before the start of the search.
  • vis.examine_vertex(u, g)r is invoked in each vertex as it is removed from the queue.
  • vis.examine_edge(e, g) is invoked on every out-edge of each vertex immediately after the vertex is removed from the queue.
  • vis.tree_edge(e, g) is invoked (in addition to examine_edge()) if the edge is a tree edge. The target vertex of edge e is discovered at this time.
  • vis.discover_vertex(u, g) is invoked the first time the algorithm encounters vertex u. All vertices closer to the source vertex have been discovered, and vertices further from the source have not yet been discovered.
  • vis.non_tree_edge(e, g) is invoked (in addition to examine_edge()) if the edge is not a tree edge.
  • vis.gray_target(e, g) is invoked (in addition to non_tree_edge()) if the target vertex is colored gray at the time of examination. The color gray indicates that the vertex is currently in the queue.
  • vis.black_target(e, g) is invoked (in addition to non_tree_edge()) if the target vertex is colored black at the time of examination. The color black indicates that the vertex is no longer in the queue.
  • vis.finish_vertex(u, g) is invoked after all of the out edges of u have been examined and all of the adjacent vertices have been discovered.

See Also

breadth_first_search(), bfs_visitor, and depth_first_search()

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore the user may want the visitor to hold this state by pointer or reference.

CCopyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu/A>)) 		Top


Buffer

Buffer Concept

A Buffer is something in which items can be put and removed. The Buffer concept has very few requirements. It does not require any particular ordering of how the items are stored or in what order they will appear when removed, however, there is typically some sort of ordering policy.

Notation

B | is a type that models Buffer.
T | is the value type of B.
t | is an object of type T.

Members

For a type to model the Buffer concept it must have the following members.

Member | Description
value_type | The type of object stored in the Buffer. The value type must be Assignable.
size_type | An unsigned integral type for representing the number of objects in the Buffer.
void push(const T& t) | Inserts t into the Buffer. size() will be incremented by one.
void pop() | Removes an object from the Buffer. size() will be decremented by one. Precondition: empty() is false.
T& top() | Returns a mutable reference to some object in the Buffer. Precondition: empty() is false.
const T& top() const | Returns a const reference to some object in the Buffer. Precondition: empty() is false.
size_type size() const | Returns the number of objects in the Buffer. Invariant: size() >= 0.
bool empty() const | Equivalent to b.size() == 0.

Complexity Guarantees

  • push(), pop(), and size() must be at most linear time complexity in the size of the Generalized Queue.
  • top() and empty() must be amortized constant time.

Models

  • std::stack
  • boost::mutable_queue
Copyright © 2000-2001
Jeremy Siek, Indiana University and C++ Library & Compiler Group/SGI (jsiek@engr.sgi.com)


Bundled Properties

Class templates adjacency_list and adjacency_matrix support the introduction of named properties via internal properties. However, this method is cumbersome in many uses, where it would be more intuitive to just specify a structure or class that contains internal properties for edges or vertices. Bundled properties allow one to use adjacency_list and adjacency_matrix in this manner, providing a simple way to introduce and access any number of internal properties for vertices and edges.

One can introduce bundled properties into an either graph type by providing a user-defined class type for the VertexProperties or EdgeProperties template arguments. The user-defined class may alternatively be placed at the end of a property list, replacing the (implicit) boost::no_property argument.

Example: Route planning

Consider the implementation of a simple route planner that should find the shortest directions from one city to another via a set of highways. The vertices of the graph are cities, and the user may wish to store several bits of information about the city within each vertex:

struct City
{
  string name;
  int population;
  vector<int> zipcodes;
};

The edges in the graph represent highways, which also has several attributes:

struct Highway
{
  string name;
  double miles;
  int speed_limit;
  int lanes;
  bool divided;
};

Without bundled properties, translating this example directly into an instantiation of adjacency_list would involve several custom properties and would result in a type like this:

typedef boost::adjacency_list<
    boost::listS, boost::vecS, boost::bidirectionalS,
    // Vertex properties
    boost::property<boost::vertex_name_t, std::string,
    boost::property<population_t, int,
    boost::property<zipcodes_t, std::vector<int> > > >,
    // Edge properties
    boost::property<boost::edge_name_t, std::string,
    boost::property<boost::edge_length_t, double,
    boost::property<edge_speed_limit_t, int,
    boost::property<edge_lanes_t, int,
    boost::property<edge_divided, bool> > > > > >
  Map;

With bundled properties, directly use the City and Highway structures:

typedef boost::adjacency_list<
    boost::listS, boost::vecS, boost::bidirectionalS,
    City, Highway> Map;

Accessing bundled properties

To access a bundled property for a particular edge or vertex, subscript the graph with the descriptor of the edge or vertex whose bundled property you wish to access. For instance:

Map map; // load the map
Map::vertex_descriptor v = *vertices(map).first;
map[v].name = "Troy";
map[v].population = 49170;
map[v].zipcodes.push_back(12180);
Map::edge_descriptor e = *out_edges(v, map).first;
map[e].name = "I-87";
map[e].miles = 10;
map[e].speed_limit = 65;
map[e].lanes = 4;
map[e].divided = true;

Properties map from bundled properties

Often one needs to create a property map from an internal property for use in a generic algorithm. For instance, using the graph without bundled properties invokes Dijkstra's shortest paths algorithm as follows:

vector<double> distances(num_vertices(map));
dijkstra_shortest_paths(map, from,
      weight_map(get(edge_length, map))
      .distance_map(make_iterator_property_map(distances.begin(),
                                               get(vertex_index, map))));

With bundled properties, just pass a member pointer as the property for get. The equivalent example using bundled properties is:

vector<double> distances(num_vertices(map));
dijkstra_shortest_paths(map, from,
      weight_map(get(&Highway::miles, map))
      .distance_map(make_iterator_property_map(distances.begin(),
                                               get(vertex_index, map))));

The type of the returned property map is property_map<Map, int Highway::*>::type or property_map<Map, int Highway::*>::const_type, depending on whether the graph map is non-constant or constant.

Access the entire vertex or edge bundle as a property map using the vertex_bundle or edge_bundle properties, respectively. For instance, the property map returned by get(vertex_bundle, map) is an Lvalue Property Map providing access to the City values stored in each vertex.

Getting the type of bundled properties

To get the type of the vertex or edge bundle for a given graph type Graph, use the trait classes vertex_bundle_type and edge_bundle_type. The type vertex_bundle_type<Graph>::type will be the type bundled with vertices (or no_vertex_bundle if the graph supports bundles but no vertex bundle exists). Likewise, edge_bundle_type<Graph>::type will be the type bundled with edges (or no_edge_bundle if no edge bundle exists).

Compatibility

Bundled properties works properly on compilers that support class template partial specialization.

Copyright © 2004 Doug Gregor.


Challenge

Boost Graph Library Challenge and To-Do Items

  • Dynamic graph algorithms such as described in Dynamic Graph Algorithms and A Software Library of Dynamic Graph Algorithms.

  • Polish up code/docs for pending items and champion the formal review. The pending items are:

    • container_traits.hpp (this should also include the work done by Matt Austern on this topic)

    • The queues and heaps: queue.hpp, mutable_queue.hpp, fibonacci_heap.hpp. Somehow merge implementation with Dietmer's heaps and queues.

    • disjoint_sets

  • Construct a set of planar graph algorithms.

    • Is the graph planar?

    • "Walk around the block" and similar open and closed neighborhood traversals. Note that edge traversals need to resolve to particular ends and sides (see below), not just to the edge as a whole.

    • Given a point, find the nearest vertex, edge, or bounded polygon. Again, edges are viewed as having left and right sides.

    • Given a line segment, find intersecting vertices, edges, or bounded polygons.

    • Given a polygon, find intersecting whatever...

    • Various minimum bounding rectangle and clipping problems.

    • Construct a planar embedding of a planar graph.

    • Find a balanced separator of a graph.

    • Modify adjacency_list so that the out-edges can be ordered according to a user defined comparison object.

  • Rewrite the Qhull algorithm using the Boost Graph Library (this is high difficulty challenge). Or, for a more manageable challenge, write an interface for Qhull with the BGL. Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram, and halfspace intersection about a point. Qhull runs in 2-d, 3-d, 4-d, and higher dimensions. Qhull is used for collision detection, animation, plate tectonics, 3-d modeling, robot motion planning, and other applications. It is currently difficult to use from a C++ program.

  • Explore the use of Algorithm Objects as an alternative to the current approach with visitors.

  • Analyze the algorithms that do not yet have visitors, and come up with visitor interfaces for them.

  • Add a check in the adjacency_list class to make sure all the vertex property template arguments have kind=vertex_property_tag and all edge property template arguments have kind=edge_property_tag.

  • Clean up the output functions in graph_utility.hpp to use streams, and document all the utility functions. Replace the random number stuff with calls to the boost random number generator.

  • Modularize the tests in test/graph.cpp to apply to particular concepts. Make sure there are run-time tests for every BGL concept.

  • Write tests for the BGL algorithms. There are a few, but more are needed. The example provide a sanity check but do not provide full coverage.

  • Write up the examples from Knuth's Stanford GraphBase using the BGL. The file examples/miles_span.cpp is a start.

  • Further testing of the subgraph class and add more features.

  • Implement a minimum-cost maximum-flow algorithm.

  • Make the type of all (internal) property maps convertible to the const_type of the property maps.

  • Add static functions to adjacency_list to return the per-vertex, per-edge, and per-graph overhead.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Function template circle_graph_layout

boost::circle_graph_layout — Layout the graph with the vertices at the points of a regular n-polygon.

Synopsis

template<typename VertexListGraph, typename PositionMap, typename Radius>
  void circle_graph_layout(const VertexListGraph & g, PositionMap position,
                           Radius radius);

Where Defined

boost/graph/circle_layout.hpp

Description

The distance from the center of the polygon to each point is determined by the radius parameter. The position parameter must be an Lvalue Property Map whose value type is a class type containing x and y members that will be set to the x and y coordinates.

Parameters

IN: const VertexListGraph& g

The graph object on which the algorithm will be applied. The type VertexListGraph must be a model of Vertex List Graph.
Python: The parameter is named graph.

OUT: PositionMap position

This property map stores the position of each vertex. The type PositionMap must be a model of Writable Property Map, with the graph's edge descriptor type as its key type. The value type of this property map should be assignable from the type Radius.
Python: The position map must be a vertex_point2d_map for the graph.
Python default: graph.get_vertex_point2d_map("position")

IN: Radius radius

Lays out the points on the circle with this radius. It must be compatible with double.

Copyright © 2004
Douglas Gregor, Indiana University (dgregor -at- cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums -at- osl.iu.edu)


Boost Graph Library: ColorValue Concept

ColorValue

This concept describes the requirements for the type used for color values, as in for coloring a graph during a breath-first search to mark which vertices have been visited.

Refinement of

EqualityComparable and DefaultConstructible

Notation

T | A type that is a model of ColorValue.
cv | An object of type T.

Valid Expressions

Name | Expression | Return Type | Description
Get Color White | color_traits<T>::white() | T | Returns an object that represents the color white.
Get Color Gray | color_traits<T>::gray() | T | Returns an object that represents the color gray.
Get Color Black | color_traits<T>::black() | T | Returns an object that represents the color black.

Models

  • default_color_type (in boost/graph/properties.hpp)
Copyright © 2000-2001
 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Compressed Sparse Row Graph

The class template compressed_sparse_row_graph is a graph class that uses the compact Compressed Sparse Row (CSR) format to store directed graphs. While CSR graphs have much less overhead than many other graph formats (e.g., adjacency_list), they do not provide any mutability that is, one cannot add or remove vertices or edges from a CSR graph. Use this format in high-performance applications or for very large graphs that need no change.

The CSR format stores vertices and edges in separate arrays, with the indices into these arrays corresponding to the identifier for the vertex or edge, respectively. The edge array is sorted by the source of each edge, but contains only the targets for the edges. The vertex array stores offsets into the edge array, providing the offset of the first edge outgoing from each vertex. Iteration over the out-edges for the ith vertex in the graph is achieved by visiting edge_array[vertex_array[i]], edge_array[vertex_array[i]+1], ..., edge_array[vertex_array[i+1]]. This format minimizes memory use to O(n + m), where n and m are the number of vertices and edges, respectively. The constants multiplied by n and m are based on the size of the integers needed to represent indices into the edge and vertex arrays, respectively, which can be controlled using the template parameters.

  • Synopsis

  • Where Defined

  • Models

  • Template parameters

  • Properties

  • Member functions

    • Constructors

    • Mutators

    • Property access

  • Non-member functions

    • Vertex access

    • Edge access

    • Property map accessors

    • Incremental construction functions

  • Example

Synopsis

namespace boost {{

template<typename Directed = directedS, typename VertexProperty = void,
         typename EdgeProperty = void, typename GraphProperty = no_property,
         typename Vertex = std::size_t, typename EdgeIndex = Vertex>
class compressed_sparse_row_graph
{
public:
  // Graph constructors
  compressed_sparse_row_graph();

  template<typename InputIterator>
  compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
                              vertices_size_type numverts,
                              edges_size_type numedges = 0,
                              const GraphProperty& prop = GraphProperty());

  template<typename InputIterator, typename EdgePropertyIterator>
  compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numverts,
                              edges_size_type numedges = 0,
                              const GraphProperty& prop = GraphProperty());

  template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi,
                              vertices_size_type numverts,
                              edges_size_type numedges);

  template<typename Graph, typename VertexIndexMap>
   compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi);

  template<typename Graph>
  explicit compressed_sparse_row_graph(const Graph& g);

  // Graph mutators
  template<typename Graph, typename VertexIndexMap>
  void assign(const Graph& g, const VertexIndexMap& vi,
              vertices_size_type numverts, edges_size_type numedges);

  template<typename Graph, typename VertexIndexMap>
  void assign(const Graph& g, const VertexIndexMap& vi);

  template<typename Graph>
  void assign(const Graph& g);

  // Property Access
  VertexProperty& operator[](vertex_descriptor v);
  const VertexProperty& operator[](vertex_descriptor v) const;
  EdgeProperty& operator[](edge_descriptor v);
  const EdgeProperty& operator[](edge_descriptor v) const;
};

// Incidence Graph requirements
vertex_descriptor source(edge_descriptor, const compressed_sparse_row_graph&);
vertex_descriptor target(edge_descriptor, const compressed_sparse_row_graph&);
std::pair<out_edge_iterator, out_edge_iterator>
  out_edges(vertex_descriptor, const compressed_sparse_row_graph&);
degree_size_type out_degree(vertex_descriptor v, const compressed_sparse_row_graph&);

// Adjacency Graph requirements
std::pair<adjacency_iterator, adjacency_iterator>
  adjacent_vertices(vertex_descriptor, const compressed_sparse_row_graph&);

// Vertex List Graph requirements
std::pair<vertex_iterator, vertex_iterator> vertices(const compressed_sparse_row_graph&);
vertices_size_type num_vertices(const compressed_sparse_row_graph&);

// Edge List Graph requirements
std::pair<edge_iterator, edge_iterator> edges(const compressed_sparse_row_graph&);
edges_size_type num_edges(const compressed_sparse_row_graph&);

// Vertex access
vertex_descriptor vertex(vertices_size_type i, const compressed_sparse_row_graph&);

// Edge access
std::pair<out_edge_iterator, out_edge_iterator>
  edge_range(vertex_descriptor u, vertex_descriptor v, const compressed_sparse_row_graph&);
std::pair<edge_descriptor, bool>
  edge(vertex_descriptor u, vertex_descriptor v, const compressed_sparse_row_graph&);
edge_descriptor edge_from_index(edges_size_type i, const compressed_sparse_row_graph&);

// Property map accessors
template<typename PropertyTag>
property_map<compressed_sparse_row_graph, PropertyTag>::type
get(PropertyTag, compressed_sparse_row_graph& g)

template<typename PropertyTag>
property_map<compressed_sparse_row_graph, Tag>::const_type
get(PropertyTag, const compressed_sparse_row_graph& g)

template<typename PropertyTag, class X>
typename property_traits<property_map<compressed_sparse_row_graph, PropertyTag>::const_type>::value_type
get(PropertyTag, const compressed_sparse_row_graph& g, X x)

template<typename PropertyTag, class X, class Value>
void put(PropertyTag, const compressed_sparse_row_graph& g, X x, const Value& value);

template<typename GraphPropertyTag>
typename graph_property<compressed_sparse_row_graph, GraphPropertyTag>::type&
get_property(compressed_sparse_row_graph& g, GraphPropertyTag);

template<typename GraphPropertyTag>
typename graph_property<compressed_sparse_row_graph, GraphPropertyTag>::type const &
get_property(const compressed_sparse_row_graph& g, GraphPropertyTag);

template<typename GraphPropertyTag>
void set_property(const compressed_sparse_row_graph& g, GraphPropertyTag,
                  const typename graph_property<compressed_sparse_row_graph, GraphPropertyTag>::type& value);

// Incremental construction functions
template<typename Graph>
vertex_descriptor add_vertex(compressed_sparse_row_graph& g);

template<typename Graph>
vertex_descriptor add_vertices(vertices_size_type count, compressed_sparse_row_graph& g);

template<typename Graph>
edge_descriptor add_vertices(vertex_descriptor src, vertex_descriptor tgt, compressed_sparse_row_graph& g);

} // end namespace boost

Where Defined

<boost/graph/compressed_sparse_row_graph.hpp>

Models

The compressed_sparse_row_graph class template models (i.e., implements the requirements of) many of the BGL graph concepts, allowing it to be used with most of the BGL algorithms. In particular, it models the following specific graph concepts:

  • Graph

  • IncidenceGraph

  • AdjacencyGraph

  • VertexListGraph

  • EdgeListGraph

  • PropertyGraph

Template Parameters

The compressed_sparse_row_graph class has several template parameters that can customize the layout in memory and what properties are attached to the graph itself. All parameters have defaults, so users interested only in the structure of a graph can use the type compressed_sparse_row_graph<> and ignore the parameters.

Parameters

Directed

A selector that determines whether the graph will be directed, bidirectional or undirected. At this time, the CSR graph type only supports directed graphs, so this value must be boost::directedS.
Default: boost::directedS

VertexProperty

A class type, attached to each vertex in the graph. If this value is void, attaches no properties to the vertices of the graph.
Default: void

EdgeProperty

A class type, attached to each edge in the graph. If this value is void, no properties will be attached to the edges of the graph.
Default: void

GraphProperty

A nested set of property templates that describe the properties of the graph itself. If this value is no_property, attaches no properties to the graph.
Default: no_property

Vertex

An unsigned integral type, used as both the index into the array of vertices and as the vertex descriptor itself. Larger types permit the CSR graph to store more vertices; smaller types reduce the storage required per vertex.
Default: std::size_t

EdgeIndex

An unsigned integral type, used as the index into the array of edges. As with the Vertex parameter, larger types permit more edges whereas smaller types reduce the amount of storage needed per edge. The EdgeIndex type must not be smaller than the Vertex type, but it may be larger. For instance, Vertex may be a 16-bit integer (allowing 32,767 vertices in the graph) whereas EdgeIndex could then be a 32-bit integer to allow a complete graph to be stored in the CSR format.
Default: Vertex

Interior Properties

The compressed_sparse_row_graph allows properties to be attached to its vertices, edges, or to the graph itself by way of its template parameters. Access these properties via the member and non-member property access functions, using the bundled properties scheme.

The CSR graph provides two kinds of built-in properties: vertex_index, which maps from vertices to values in [0, n) and edge_index, which maps from edges to values in [0, m), where n and m are the number of vertices and edges in the graph, respectively.

Member Functions

Constructors

compressed_sparse_row_graph();

Constructs a graph with no vertices or edges.

  template<typename InputIterator>
  compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
                              vertices_size_type numverts,
                              edges_size_type numedges = 0,
                              const GraphProperty& prop = GraphProperty());

Constructs a graph with numverts vertices whose edges are specified by the iterator range [edge_begin, edge_end). The InputIterator must be a model of InputIterator whose value_type is an std::pair of integer values. These integer values are the source and target vertices for the edges, and must fall within the range [0, numverts). The edges in [edge_begin, edge_end) must be sorted so that all edges originating from vertex i precede any edges originating from all vertices j where j > i.

The value numedges, if provided, tells how many edges are in the range [edge_begin, edge_end) and will be used to pre-allocate data structures to save both memory and time during construction.

The value prop will be used to initialize the graph property.

  template<typename InputIterator, typename EdgePropertyIterator>
  compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numverts,
                              edges_size_type numedges = 0,
                              const GraphProperty& prop = GraphProperty());

This constructor, constructs a graph with numverts vertices and the edges provided in the iterator range [edge_begin, edge_end). Its semantics are identical to the edge range constructor, except that edge properties are also initialized. The type EdgePropertyIterator must be a model of the InputIterator concept whose value_type is convertible to EdgeProperty. Use the iterator range [ep_iter, ep_ter + m) to initialize the properties on the edges of the graph, where m is distance from edge_begin to edge_end.

template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi,
                              vertices_size_type numverts,
                              edges_size_type numedges);

  template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi);

  template<typename Graph>
  explicit compressed_sparse_row_graph(const Graph& g);

Calls the assign function with all of the arguments it is given.

Mutators

  template<typename Graph, typename VertexIndexMap>
  void assign(const Graph& g, const VertexIndexMap& vi,
              vertices_size_type numverts, edges_size_type numedges);

  template<typename Graph, typename VertexIndexMap>
  void assign(const Graph& g, const VertexIndexMap& vi);

  template<typename Graph>
  void assign(const Graph& g);

Clears the CSR graph and builds a CSR graph in place from the structure of another graph. The graph type Graph must be a model of IncidenceGraph and have a vertex(i, g) function that retrieves the ith vertex in the graph.

Parameters

  • g: The incoming graph.

  • vi: A map from vertices to indices. If not provided, get(vertex_index, g) will be used.
  • numverts: The number of vertices in the graph g. If not provided, Graph must be a model of VertexListGraph.
  • numedges: The number of edges in the graph g. If not provided, Graph must be a model of EdgeListGraph.

Property Access

  VertexProperty& operator[](vertex_descriptor v);
  const VertexProperty& operator[](vertex_descriptor v) const;

Retrieves the property value associated with vertex v. Only valid when VertexProperty is a class type that is not no_property.

  EdgeProperty& operator[](edge_descriptor v);
  const EdgeProperty& operator[](edge_descriptor v) const;

Retrieves the property value associated with edge v. Only valid when EdgeProperty is a class type that is not no_property.

Non-member Functions

Vertex access

  vertex_descriptor vertex(vertices_size_type i, const compressed_sparse_row_graph&);

Retrieves the ith vertex in the graph in constant time.

Edge access

  std::pair<out_edge_iterator, out_edge_iterator>
    edge_range(vertex_descriptor u, vertex_descriptor v, const compressed_sparse_row_graph&);

Returns all edges from u to v. Requires time linear in the number of edges outgoing from u.

  std::pair<edge_descriptor, bool>
    edge(vertex_descriptor u, vertex_descriptor v, const compressed_sparse_row_graph&);

If there exists an edge (u, v) in the graph, returns the descriptor for that edge and true; otherwise, the second value in the pair will be false. If multiple edges exist from u to v, the first edge will be returned; use edge_range to retrieve all edges.

  edge_descriptor edge_from_index(edges_size_type i, const compressed_sparse_row_graph&);

Returns the ith edge in the graph. This operation requires logarithmic time in the number of vertices.

Property Map Accessors

template<typename PropertyTag>
property_map<compressed_sparse_row_graph, PropertyTag>::type
get(PropertyTag, compressed_sparse_row_graph& g)

template<typename PropertyTag>
property_map<compressed_sparse_row_graph, Tag>::const_type
get(PropertyTag, const compressed_sparse_row_graph& g)

Returns the property map object for the vertex property specified by PropertyTag. The PropertyTag must be a member pointer to access one of the fields in VertexProperty or EdgeProperty.

template<typename PropertyTag, class X>
typename property_traits<property_map<compressed_sparse_row_graph, PropertyTag>::const_type>::value_type
get(PropertyTag, const compressed_sparse_row_graph& g, X x)

This returns the property value for x, where x is either a vertex or edge descriptor.

template<typename PropertyTag, class X, class Value>
void put(PropertyTag, const compressed_sparse_row_graph& g, X x, const Value& value);

This sets the property value for x to value. x is either a vertex or edge descriptor. Value must be convertible to typename property_traits<property_map<compressed_sparse_row_graph, PropertyTag>::type>::value_type

template<typename GraphPropertyTag>
typename graph_property<compressed_sparse_row_graph, GraphPropertyTag>::type&
get_property(compressed_sparse_row_graph& g, GraphPropertyTag);

template<typename GraphPropertyTag>
typename graph_property<compressed_sparse_row_graph, GraphPropertyTag>::type const &
get_property(const compressed_sparse_row_graph& g, GraphPropertyTag);

Return the property specified by GraphPropertyTag that is attached to the graph object g.

template<typename GraphPropertyTag>
void set_property(const compressed_sparse_row_graph& g, GraphPropertyTag,
                  const typename graph_property<compressed_sparse_row_graph, GraphPropertyTag>::type& value);

Set the property specified by GraphPropertyTag that is attached to the graph object g.

Incremental construction functions

vertex_descriptor add_vertex(compressed_sparse_row_graph& g)

Add a new vertex to the end of the graph g, and return a descriptor for that vertex. The new vertex will be greater than any of the previous vertices in g.

vertex_descriptor add_vertices(vertices_size_type count, compressed_sparse_row_graph& g)

Add count new vertices to the end of the graph g, and return a descriptor for the smallest new vertex. The new vertices will be greater than any of the previous vertices in g.

edge_descriptor add_edge(vertex_descriptor src, vertex_descriptor tgt, compressed_sparse_row_graph& g)

Add a new edge from src to tgt in the graph g, and return a descriptor for it. There must not be an edge in g whose source vertex is greater than src. |
If the vertex src has out edges before this operation is called, there must be none whose target is larger than tgt.

Example

[libs/graph/example/csr-example.cpp]

Use the compressed_sparse_row_graph graph class to store a simple Web graph. In this web graph the vertices represent web pages and the edges represent links from one web page to another. To associate each web page with a URL, initially create a WebPage class that stores the URL. Then, create the graph type by providing WebPage as a parameter to the compressed_sparse_row_graph class template.

class WebPage
{
 public:
  std::string url;
};

// ...

typedef compressed_sparse_row_graph<directedS, WebPage> WebGraph;
WebGraph g(&the_edges[0], &the_edges[0] + sizeof(the_edges)/sizeof(E), 6);

Then, set the properties on the vertices of the graph using the bundled properties syntax, and display the edges for the user.

// Set the URLs of each vertex
int index = 0;
BGL_FORALL_VERTICES(v, g, WebGraph)
  g[v].url = urls[index++];

// Output each of the links
std::cout << "The web graph:" << std::endl;
BGL_FORALL_EDGES(e, g, WebGraph)
  std::cout << "  " << g[source(e, g)].url << " -> " << g[target(e, g)].url
            << std::endl;

See the complete example source for other operations one can perform with a compressed_sparse_row_graph.

Copyright © 2005
Doug Gregor, Indiana University ()
Jeremiah Willcock, Indiana University ()
Andrew Lumsdaine, Indiana University ()


Boost Graph Library: Connected Components

connected_components

// named parameter version
template <class VertexListGraph, class ComponentMap, class P, class T, class R>
typename property_traits<ComponentMap>::value_type
connected_components(VertexListGraph& G, ComponentMap comp,
    const bgl_named_params<P, T, R>& params = all defaults);

// there is not a non-named parameter version of this function

The connected_components() functions compute the connected components of an undirected graph using a DFS-based approach. A connected component of an undirected graph is a set of vertices that are all reachable from each other. If the connected components need to be maintained while a graph is growing the disjoint-set based approach of function incremental_components() is faster. For ``static'' graphs this DFS-based approach is faster [8].

The output of the algorithm is recorded in the component property map comp, which will contain numbers giving the component number assigned to each vertex. The total number of components is the return value of the function.

Where Defined

boost/graph/connected_components.hpp

Parameters

IN: const Graph& g

An undirected graph. The graph type must be a model of Vertex List Graph and Incidence Graph.
Python: The parameter is named graph.

OUT: ComponentMap c

The algorithm computes how many connected components are in the graph, and assigns each component an integer label. The algorithm then records which component each vertex in the graph belongs to by recording the component number in the component property map. The ComponentMap type must be a model of Writable Property Map. The value type should be an integer type, preferably the same as the vertices_size_type of the graph. The key type must be the graph's vertex descriptor type.
Python: Must be an vertex_int_map for the graph.
Python default: graph.get_vertex_int_map("component")

Named Parameters

UTIL: color_map(ColorMap color)

This is used by the algorithm to keep track of its progress through the graph. The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue .
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.

IN: vertex_index_map(VertexIndexMap i_map)

This maps each vertex to an integer in the range [0, num_vertices(g)). This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g). Note: Use this default, only when the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity for the connected components algorithm is also O(V + E).

See Also

strong_components() and incremental_components()

Example

The file examples/connected_components.cpp contains an example of calculating the connected components of an undirected graph.

Copyright © 2000-2001Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Const Associative Property Map

const_associative_property_map<UniquePairAssociativeContainer>

This property map is an adaptor that converts any type that is a model of both Pair Associative Container and Unique Associative Container such as std::map into a constant Lvalue Property Map.

Note: The adaptor only retains a reference to the container, so the lifetime of the container must encompass the use of the adaptor.

Example

//  (C) Copyright Jeremy Siek 2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <map>
#include <string>
#include <boost/property_map.hpp>

template <typename AddressMap>
void foo(AddressMap address)
{
  typedef typename boost::property_traits<AddressMap>::value_type value_type;
  typedef typename boost::property_traits<AddressMap>::key_type key_type;

  value_type old_address, new_address;
  key_type fred = "Fred";
  old_address = get(address, fred);
  new_address = "384 Fitzpatrick Street";
  put(address, fred, new_address);

  key_type joe = "Joe";
  value_type& joes_address = address[joe];
  joes_address = "325 Cushing Avenue";
}

int
main()
{
  std::map<std::string, std::string> name2address;
  boost::associative_property_map< std::map<std::string, std::string> >
    address_map(name2address);

  name2address.insert(make_pair(std::string("Fred"),
                                std::string("710 West 13th Street")));
  name2address.insert(make_pair(std::string("Joe"),
                                std::string("710 West 13th Street")));

  foo(address_map);

  for (std::map<std::string, std::string>::iterator i = name2address.begin();
       i != name2address.end(); ++i)
    std::cout << i->first << ": " << i->second << "\n";

  return EXIT_SUCCESS;
}

#include <iostream>
#include <map>
#include <string>
#include <boost/property_map.hpp>

template <typename ConstAddressMap>
void display(ConstAddressMap address)
{
  typedef typename boost::property_traits<ConstAddressMap>::value_type
    value_type;
  typedef typename boost::property_traits<ConstAddressMap>::key_type key_type;

  key_type fred = "Fred";
  key_type joe = "Joe";

  value_type freds_address = get(address, fred);
  value_type joes_address = get(address, joe);

  std::cout << fred << ": " << freds_address << "\n"
        << joe  << ": " << joes_address  << "\n";
}

int main()
{
  std::map<std::string, std::string> name2address;
  boost::const_associative_property_map< std::map<std::string, std::string> >
    address_map(name2address);

  name2address.insert(make_pair(std::string("Fred"),
                std::string("710 West 13th Street")));
  name2address.insert(make_pair(std::string("Joe"),
                std::string("710 West 13th Street")));

  display(address_map);

  return EXIT_SUCCESS;
}

Where Defined

boost/property_map.hpp

Model Of

Lvalue Property Map

Template Parameters

Parameter | Description | Default
UniquePairAssociativeContainer | Must be a model of both Pair Associative Container and Unique Associative Container . |

Members

In addition to the methods and functions required by Lvalue Property Map, this class has the following members.

property_traits<const_associative_property_map>::value_type

This is the same type as UniquePairAssociativeContainer::data_type.

const_associative_property_map()

Default Constructor.

const_associative_property_map(const UniquePairAssociativeContainer& c)

Constructor.

const data_type& operator[](const key_type& k) const

The operator bracket for property access. The key_type and data_type types are from the typedefs inside of UniquePairAssociativeContainer.

Non-Member functions

  template <typename UniquePairAssociativeContainer>
  const_associative_property_map<UniquePairAssociativeContainer>
  make_assoc_property_map(const UniquePairAssociativeContainer& c);

A function for conveniently creating an associative property map.

Copyright © 2002
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee1@osl.iu.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Constructing Graph Algorithms

Constructing graph algorithms with BGL

The main goal of BGL is not to provide a nice graph class, or to provide a comprehensive set of reusable graph algorithms (though these are goals). The main goal of BGL is to encourage others to write maximally reusable graph algorithms. Generic programming is a methodology for making algorithms maximally reusable, and this section discusses how to apply generic programming to constructing graph algorithms.

This section steps through the construction of a graph coloring algorithm to illustrate the generic programming process. The graph coloring problem (or more specifically, the vertex coloring problem) is to label each vertex in a graph G with a color such that no two adjacent vertices are labeled with the same color and such that the minimum number of colors are used. In general, the graph coloring problem is NP-complete, and therefore it is impossible to find an optimal solution in a reasonable amount of time. However, there are many algorithms that use heuristics to find colorings that are close to the minimum.

The particular algorithm presented here is based on the linear time SEQ subroutine that is used in the estimation of sparse Jacobian and Hessian matrices [9,7,6]. This algorithm visits all of the vertices in the graph according to the order defined by the input order. At each vertex the algorithm marks the colors of the adjacent vertices, and then chooses the smallest unmarked color for the color of the current vertex. If all of the colors are already marked, a new color is created. A color is considered marked if its mark number is equal to the current vertex number. This saves the trouble of having to reset the marks for each vertex. The effectiveness of this algorithm is highly dependent on the input vertex order. There are several ordering algorithms, including the largest-first [31], smallest-last [29], and incidence degree [32] algorithms, which improve the effectiveness of this coloring algorithm.

The first decision to make when constructing a generic graph algorithm is to decide what graph operations are necessary for implementing the algorithm, and which graph concepts the operations map to. In this algorithm the user must traverse through all of the vertices to intialize the vertex colors and access the adjacent vertices. Choose the VertexListGraph concept because it is the minimum concept that includes these operations. The graph type will be parameterized in the template function for this algorithm. Do not restrict the graph type to a particular graph class, such as the BGL adjacency_list, for this would drastically limit the reusability of the algorithm (as most algorithms written to date are). Do restrict the graph type to those types that model VertexListGraph. This is enforced by the use of those graph operations in the algorithm, and furthermore by our explicit requirement added as a concept check with function_requires() (see Section Concept Checking for more details about concept checking).

Next, think about what vertex or edge properties will be used in the algorithm. In this case, the only property is vertex color. The most flexible way to specify access to vertex color is to use the propery map interface. This gives the user of the algorithm the ability to decide how they want to store the properties. Since the user has to both read and write the colors specify the requirements as ReadWritePropertyMap. The key_type of the color map must be the vertex_descriptor from the graph, and the value_type must be some kind of integer. Also, specify the interface for the order parameter as a property map, in this case a ReadablePropertyMap. For order, the key_type is an integer offset and the value_type is a vertex_descriptor. Again enforce these requirements with concept checks. The return value of this algorithm is the number of colors that were needed to color the graph, hence the return type of the function is the graph's vertices_size_type. The following code shows the interface for our graph algorithm as a template function, the concept checks, and some typedefs. The implementation is straightforward, the only step not discussed above is the color initialization step, where the user sets the color of all the vertices to ``uncolored''.

namespace boost {{
  template <class VertexListGraph, class Order, class Color>
  typename graph_traits<VertexListGraph>::vertices_size_type
  sequential_vertex_color_ting(const VertexListGraph& G,
    Order order, Color color)
  {
    typedef graph_traits<VertexListGraph> GraphTraits;
    typedef typename GraphTraits::vertex_descriptor vertex_descriptor;
    typedef typename GraphTraits::vertices_size_type size_type;
    typedef typename property_traits<Color>::value_type ColorType;
    typedef typename property_traits<Order>::value_type OrderType;

    function_requires< VertexListGraphConcept<VertexListGraph> >();
    function_requires< ReadWritePropertyMapConcept<Color, vertex_descriptor> >();
    function_requires< IntegerConcept<ColorType> >();
    function_requires< size_type, ReadablePropertyMapConcept<Order> >();
    typedef typename same_type<OrderType, vertex_descriptor>::type req_same;

    size_type max_color = 0;
    const size_type V = num_vertices(G);
    std::vector<size_type>
      mark(V, numeric_limits_max(max_color));

    typename GraphTraits::vertex_iterator v, vend;
    for (tie(v, vend) = vertices(G); v != vend; ++v)
      color[*v] = V - 1; // which means "not colored"

    for (size_type i = 0; i < V; i++) {
      vertex_descriptor current = order[i];

      // mark all the colors of the adjacent vertices
      typename GraphTraits::adjacency_iterator ai, aend;
      for (tie(ai, aend) = adjacent_vertices(current, G); ai != aend; ++ai)
        mark[color[*ai]] = i;

      // find the smallest color unused by the adjacent vertices
      size_type smallest_color = 0;
      while (smallest_color < max_color && mark[smallest_color] == i)
        ++smallest_color;

      // if all the colors are used up, increase the number of colors
      if (smallest_color == max_color)
        ++max_color;

      color[current] = smallest_color;
    }
    return max_color;
  }
} // namespace boost

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Copy Graph

copy_graph

template <class VertexListGraph, class MutableGraph>
void copy_graph(const VertexListGraph& G, MutableGraph& G_copy,
    const bgl_named_params<P, T, R>& params = all defaults)

This function copies all of the vertices and edges from graph G into G_copy. Also, it copies the vertex and edge properties, either by using the vertex_all and edge_all property maps, or by user-supplied copy functions.

Where Defined

boost/graph/copy.hpp

Parameters

IN: const VertexListGraph& G

A directed or undirected graph. The graph type must be a model of Vertex List Graph.

OUT: MutableGraph& G_copy

The resulting copy of the graph. The graph type must be a model of Mutable Graph.

Named Parameters

IN: vertex_copy(VertexCopier vc)

This is a Binary Function that copies the properties of a vertex in the original graph into the corresponding vertex in the copy.
Default: vertex_copier<VertexListGraph, MutableGraph> which uses the property tag vertex_all to access a property map from the graph.

IN: edge_copy(EdgeCopier ec)

This is a Binary Function that copies the properties of an edge in the original graph into the corresponding edge in the copy.
Default: edge_copier<VertexListGraph, MutableGraph> which uses the property tag edge_all to access a property map from the graph.

IN: vertex_index_map(VertexIndexMap i_map)

The vertex index map type must be a model of Readable Property Map and must map the vertex descriptors of G to the integers in the half-open range [0,num_vertices(G)).
Default: get(vertex_index, G). Note: Use this default, only if the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.

UTIL/OUT: orig_to_copy(Orig2CopyMap c)

This maps vertices in the original graph to vertices in the copy.
Default: an iterator_property_map created from a std::vector of the output graph's vertex descriptor type of size num_vertices(g) and using the i_map for the index map.

Complexity

The time complexity is O(V + E).

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Copy Constructible

A type is Copy Constructible if it is possible to copy objects of that type.

Notation

T | is type that is a model of Copy Constructible
t | is an object of type T
u | is an object of type const T

Valid expressions

Name Expression Return type Semantics
Copy constructor T(t) T

t is equivalent to T(t)

Copy constructor
T(u)
T u is equivalent to T(u)
Destructor
t.~T()
T
Address Operator
&t
T*

denotes the address of t

Address Operator
&u
T*

denotes the address of u

Models

  • int

  • std::pair

Concept Checking Class

  template <class T>
  struct CopyConstructibleConcept
  {
    void constraints() {
      T a(b);            // require copy constructor
      T* ptr = &a;       // require address of operator
      const_constraints(a);
      ignore_unused_variable_warning(ptr);
    }
    void const_constraints(const T& a) {
      T c(a);            // require const copy constructor
      const T* ptr = &a; // require const address of operator
      ignore_unused_variable_warning(c);
      ignore_unused_variable_warning(ptr);
    }
    T b;
  };

See also

Default Constructible and Assignable

Copyright © 2000
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)
Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at www.boost.org/LICENSE_1_0.txt)


Boost Graph Library: Cuthill-Mckee Ordering

cuthill_mckee_ordering

Graphs: | undirected
Properties: | color, degree
Complexity: | time: O(m log(m)|V|) where m = max { degree(v) | v in V }
  (1)
  template <class IncidenceGraph, class OutputIterator,
            class ColorMap, class DegreeMap>
  OutputIterator
  cuthill_mckee_ordering(const IncidenceGraph& g,
                         typename graph_traits<Graph>::vertex_descriptor s,
                         OutputIterator inverse_permutation,
                         ColorMap color, DegreeMap degree)

  (2)
  template <class VertexListGraph, class OutputIterator>
  OutputIterator
  cuthill_mckee_ordering(const VertexIndexMap& G, OutputIterator inverse_permutation);

  template <class VertexListGraph, class OutputIterator, class VertexIndexMap>
  OutputIterator
  cuthill_mckee_ordering(const VertexIndexMap& G, OutputIterator inverse_permutation,
                         VertexIndexMap index_map);

  template <class VertexListGraph, class OutputIterator,
            class ColorMap, class DegreeMap>
  OutputIterator
  cuthill_mckee_ordering(const VertexListGraph& G, OutputIterator inverse_permutation,
                         ColorMap color, DegreeMap degree)

  (3)
  template <class IncidenceGraph, class OutputIterator,
            class ColorMap, class DegreeMap>
  OutputIterator
  cuthill_mckee_ordering(const IncidenceGraph& g,
                 std::deque< typename
             graph_traits<Graph>::vertex_descriptor > vertex_queue,
                         OutputIterator inverse_permutation,
                         ColorMap color, DegreeMap degree)

The goal of the Cuthill-Mckee (and reverse Cuthill-Mckee) ordering algorithm[14, 43, 44, 45 ] is to reduce the bandwidth of a graph by reordering the indices assigned to each vertex. The Cuthill-Mckee ordering algorithm works by a local minimization of the i-th bandwidths. The vertices are basically assigned a breadth-first search order, except that at each step, the adjacent vertices are placed in the queue in order of increasing degree.

Version 1 of the algorithm lets the user choose the ``starting vertex'', version 2 finds a good starting vertex using the pseudo-peripheral pair heuristic (among each component), while version 3 contains the starting nodes for each vertex in the deque. The choice of the ``starting vertex'' can have a significant effect on the quality of the ordering. Versions 2 and 3, call find_starting_vertex for each component in the graph, increasing the run time significantly.

The output of the algorithm are the vertices in the new ordering. Depending on the kind of output iterator used, the user gets either the Cuthill-Mckee ordering or the reverse Cuthill-McKee ordering. For example, if the user stores the output into a vector using the vector's reverse iterator, then the user gets the reverse Cuthill-McKee ordering.

  std::vector<vertex_descriptor> inv_perm(num_vertices(G));
  cuthill_mckee_ordering(G, inv_perm.rbegin(), ...);

Either way, storing the output into a vector gives the user the permutation from the new ordering to the old ordering.

  inv_perm[new_index[u]] == u

Often times, the user wants the opposite permutation (the permutation from the old index to the new index). Compute this as follows:

  for (size_type i = 0; i != inv_perm.size(); ++i)
    perm[old_index[inv_perm[i]]] = i;

Parameters

For version 1:

  • IncidenceGraph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
    Python: The parameter is named graph.

  • vertex_descriptor s  (IN)
    The starting vertex.
    Python: Unsupported parameter.

  • OutputIterator inverse_permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
    Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list.

  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
    Python: Unsupported parameter.

  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
    Python: Unsupported parameter.

For version 2:

  • VertexListGraph& g  (IN)
    An undirected graph. The graph's type must be a model of VertexListGraph.
    Python: The parameter is named graph.

  • OutputIterator inverse_permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
    Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list.

  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
    Python: Unsupported parameter.

  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
    Python: Unsupported parameter.

For version 3:

  • IncidenceGraph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
    Python: The parameter is named graph.

  • std::deque< typename graph_traits<Graph>::vertex_descriptor > vertex_queue  (IN)
    The deque containing the starting vertices for each component.
    Python: Unsupported parameter.

  • OutputIterator inverse_permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
    Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list.

  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
    Python: Unsupported parameter.

  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
    Python: Unsupported parameter.

Example

See example/cuthill_mckee_ordering.cpp.

See Also

bandwidth, and degree_property_map in boost/graph/properties.hpp.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Distributed under the Boost Software License, Version 1.0. -- (See accompanying file License.html or copy at -- http://www.boost.org/LICENSE_1_0.txt) Boost Graph Library: Cuthill-Mckee Ordering


Boost Graph Library: Directed Acyclic Graph Shortest Paths

dag_shortest_paths

// named paramter version
template <class VertexListGraph, class Param, class Tag, class Rest>
void dag_shortest_paths(const VertexListGraph& g,
   typename graph_traits<VertexListGraph>::vertex_descriptor s,
   const bgl_named_params<Param,Tag,Rest>& params)

// non-named parameter version
template <class VertexListGraph, class DijkstraVisitor,
      class DistanceMap, class WeightMap, class ColorMap,
      class PredecessorMap,
      class Compare, class Combine,
      class DistInf, class DistZero>
void dag_shortest_paths(const VertexListGraph& g,
   typename graph_traits<VertexListGraph>::vertex_descriptor s,
   DistanceMap distance, WeightMap weight, ColorMap color,
   PredecessorMap pred, DijkstraVisitor vis,
   Compare compare, Combine combine, DistInf inf, DistZero zero)

This algorithm [8] solves the single-source shortest-paths problem on a weighted, directed acyclic graph (DAG). This algorithm is more efficient for DAG's than either the Dijkstra or Bellman-Ford algorithm. Use breadth-first search instead of this algorithm when all edge weights are equal to one. For the definition of the shortest-path problem see Section Shortest-Paths Algorithms for some background to the shortest-path problem.

There are two main options for obtaining output from the dag_shortest_paths() function. If the user provides a distance property map through the distance_map() parameter then the shortest distance from the source vertex to every other vertex in the graph will be recorded in the distance map. Also, record the shortest paths tree in a predecessor map: for each vertex u in V, p[u] will be the predecessor of u in the shortest paths tree (unless p[u] = u, in which case u is either the source or a vertex unreachable from the source). In addition to these two options, the user can provide there own custom-made visitor that can takes actions during any of the algorithm's event points.

Where Defined

boost/graph/dag_shortest_paths.hpp

Parameters

IN: const VertexListGraph& g

The graph object on which the algorithm will be applied. The type VertexListGraph must be a model of \concept{VertexListGraph}.
Python: The parameter is named graph.

IN: vertex_descriptor s

The source vertex. All distance will be calculated from this vertex, and the shortest paths tree will be rooted at this vertex.
Python: The parameter is named root_vertex.

Named Parameters

IN: weight_map(WeightMap w_map)

The weight or ``length'' of each edge in the graph. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for the map must be Addable with the value type of the distance map.
Default: get(edge_weight, g)
Python: Must be an edge_double_map for the graph.
Python default: graph.get_edge_double_map("weight")

IN: vertex_index_map(VertexIndexMap i_map)

This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g). Note: Use this default, only if the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

OUT: predecessor_map(PredecessorMap p_map)

The predecessor map records the edges in the minimum spanning tree. Upon completion of the algorithm, the edges (p[u],u) for all u in V are in the minimum spanning tree. If p[u] = u then u is either the source vertex or a vertex that is not reachable from the source. The PredecessorMap type must be a Read/Write Property Map which key and vertex types the same as the vertex descriptor type of the graph.
Default: dummy_property_map
Python: Must be a vertex_vertex_map for the graph.

UTIL/OUT: distance_map(DistanceMap d_map)

The shortest path weight from the source vertex s to each vertex in the graph g is recorded in this property map. The shortest path weight is the sum of the edge weights along the shortest path. The type DistanceMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the distance map. The value type of the distance map is the element type of a Monoid formed with the combine function object and the zero object for the identity element. Also the distance value type must have a StrictWeakOrdering provided by the compare function object.
Default: iterator_property_map created from a std::vector of the WeightMap's value type of size num_vertices(g) and using the i_map for the index map.
Python: Must be a vertex_double_map for the graph.

IN: distance_compare(CompareFunction cmp)

Use this function to compare distances to determine which vertex is closer to the source vertex. The CompareFunction type must be a model of Binary Predicate and have argument types that match the value type of the DistanceMap property map.
Default: std::less<D> with D=typename property_traits<DistanceMap>::value_type
Python: Unsupported parameter.

IN: distance_combine(CombineFunction cmb)

Use this function to combine distances to compute the distance of a path. The CombineFunction type must be a model of Binary Function. The first argument type of the binary function must match the value type of the DistanceMap property map and the second argument type must match the value type of the WeightMap property map. The result type must be the same type as the distance value type.
Default: std::plus<D> with D=typename property_traits<DistanceMap>::value_type
Python: Unsupported parameter.

IN: distance_inf(D inf)

The inf object must be the greatest value of any D object. That is, compare(d, inf) == true for any d != inf. The type D is the value type of the DistanceMap.
Default: std::numeric_limits<D>::max()
Python: Unsupported parameter.

IN: distance_zero(D zero)

The zero value must be the identity element for the Monoid formed by the distance values and the combine function object. The type \code{D} is the value type of the \code{DistanceMap} Default: D()
Python: Unsupported parameter.

UTIL/OUT: color_map(ColorMap c_map)

Use this during the execution of the algorithm to mark the vertices. The vertices start out white and become gray when they are inserted in the queue. They then turn black when they are removed from the queue. At the end of the algorithm, vertices reachable from the source vertex will have been colored black. All other vertices will still be white. The type ColorMap must be a model of Read/Write Property Map. A vertex descriptor must be usable as the key type of the map, and the value type of the map must be a model of Color Value.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.

OUT: visitor(DijkstraVisitor v)

Use this to specify actions that you would like to happen during certain event points within the algorithm. The type DijkstraVisitor must be a model of the Dijkstra Visitor concept. The visitor object is passed by value [1].
Default: dijkstra_visitor<null_visitor>
Python: The parameter should be an object that derives from the DijkstraVisitor type of the graph.

Complexity

The time complexity is O(V + E).

Visitor Event Points

  • vis.initialize_vertex(u, g) is invoked on each vertex in the graph before the start of the algorithm.

  • vis.examine_vertex(u, g) is invoked on a vertex as it is added to set S. At this point we know that (p[u],u) is a shortest-paths tree edge so d[u] = delta(s,u) = d[p[u]] + w(p[u],u). Also, the distances of the examined vertices is monotonically increasing d[u1] <= d[u2] <= d[un].

  • vis.examine_edge(e, g) is invoked on each out-edge of a vertex immediately after it has been added to set S.

  • vis.edge_relaxed(e, g) is invoked on edge (u,v) if d[u] + w(u,v) < d[v]. The edge (u,v) that participated in the last relaxation for vertex v is an edge in the shortest paths tree.

  • vis.discover_vertex(v, g) is invoked on vertex v when the edge (u,v) is examined and v is WHITE. Since a vertex is colored GRAY when it is discovered, each reacable vertex is discovered exactly once.

  • vis.edge_not_relaxed(e, g) is invoked if the edge is not relaxed (see above).

  • vis.finish_vertex(u, g) is invoked on a vertex after all of its out edges have been examined.

Example

See example/dag_shortest_paths.cpp for an example of using this algorithm.

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore, the visitor must hold this state by pointer or reference.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Depth-First Search

depth_first_search

depth_first_search

// named parameter version
template <class Graph, class class P, class T, class R>
void depth_first_search(Graph& G,
  const bgl_named_params<P, T, R>& params);

// non-named parameter version
template <class Graph, class DFSVisitor, class ColorMap>
void depth_first_search(const Graph& g, DFSVisitor vis, ColorMap color)

template <class Graph, class DFSVisitor, class ColorMap>
void depth_first_search(const Graph& g, DFSVisitor vis, ColorMap color,
                        typename graph_traits<Graph>::vertex_descriptor start)

The depth_first_search() function performs a depth-first traversal of the vertices in a directed graph. When possible, a depth-first traversal chooses a vertex adjacent to the current vertex to visit next. If all adjacent vertices have already been discovered, or there are no adjacent vertices, then the algorithm backtracks to the last vertex that had undiscovered neighbors. Once all reachable vertices have been visited, the algorithm selects from any remaining undiscovered vertices and continues the traversal. The algorithm finishes when all vertices have been visited. Depth-first search is useful for categorizing edges in a graph, and for imposing an ordering on the vertices. Section Depth-First Search describes the various properties of DFS and walks through an example.

Similar to BFS, color markers are used to keep track of which vertices have been discovered. White marks vertices that have yet to be discovered, gray marks a vertex that is discovered but still has vertices adjacent to it that are undiscovered. A black vertex is discovered vertex that is not adjacent to any white vertices.

The depth_first_search() function invokes user-defined actions at certain event-points within the algorithm. This provides a mechanism for adapting the generic DFS algorithm to the many situations in which it can be used. In the pseudo-code below, the event points for DFS are indicated in by the triangles and labels on the right. The user-defined actions must be provided in the form of a visitor object, that is, an object whose type meets the requirements for a DFS Visitor. In the pseudo-code we show the algorithm computing predecessors p, discover time d and finish time t. By default, the depth_first_search() function does not compute these properties, however there are pre-defined visitors such as predecessor_recorder and time_stamper that can be used to do this.

DFS(G)
  for each vertex u in V
    color[u] := WHITE
    p[u] = u
  end for
  time := 0
  if there is a starting vertex s
    call DFS-VISIT(G, s)
  for each vertex u in V
    if color[u] = WHITE
      call DFS-VISIT(G, u)
  end for
  return (p,d_time,f_time)

DFS-VISIT(G, u)
  color[u] := GRAY
  d_time[u] := time := time + 1
  for each v in Adj[u]
    if (color[v] = WHITE)
      p[v] = u
      call DFS-VISIT(G, v)
    else if (color[v] = GRAY)
      ...
    else if (color[v] = BLACK)
      ...
  end for
  color[u] := BLACK
  f_time[u] := time := time + 1

--
-
initialize vertex u
-
-
-
-
start vertex s
-
-
start vertex u
-
-
-
-
discover vertex u
-
examine edge (u,v)
-
(u,v) is a tree edge
-
-
(u,v) is a back edge
-
(u,v) is a cross or forward edge
-
finish vertex u
-

Where Defined

boost/graph/depth_first_search.hpp

Parameters

IN: Graph& g
A directed graph. The graph type must be a model of Incidence Graph and Vertex List Graph.
Python: The parameter is named graph.

Named Parameters

IN: visitor(DFSVisitor vis)
A visitor object that is invoked inside the algorithm at the event-points specified by the DFS Visitor concept. The visitor object is passed by value [1].
Default: dfs_visitor<null_visitor>
Python: The parameter should be an object that derives from the DFSVisitor type of the graph.
UTIL/OUT: color_map(ColorMap color)
This is used by the algorithm to keep track of its progress through the graph. The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.
IN: root_vertex(typename graph_traits<VertexListGraph>::vertex_descriptor start)
This specifies the vertex that the depth-first search should originate from. The type is the type of a vertex descriptor for the given graph.
Default: *vertices(g).first
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)]. This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g). Note: if you use this default, make sure your graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity is O(E + V).

Visitor Event Points

  • vis.initialize_vertex(s, g) is invoked on every vertex of the graph before the start of the graph search.
  • vis.start_vertex(s, g) is invoked on the source vertex once before the start of the search.
  • vis.discover_vertex(u, g) is invoked when a vertex is encountered for the first time.
  • vis.examine_edge(e, g) is invoked on every out-edge of each vertex after it is discovered.
  • vis.tree_edge(e, g) is invoked on each edge as it becomes a member of the edges that form the search tree. If you wish to record predecessors, do so at this event point.
  • vis.back_edge(e, g) is invoked on the back edges in the graph.
  • vis.forward_or_cross_edge(e, g) is invoked on forward or cross edges in the graph. In an undirected graph this method is never called.
  • vis.finish_vertex(u, g) is invoked on a vertex after all of its out edges have been added to the search tree and all of the adjacent vertices have been discovered (but before their out-edges have been examined).

Example

The example in examples/dfs-example.cpp shows DFS applied to the graph in Figure 1.

See Also

depth_first_visit undirected_dfs

Notes

[1] Since the visitor parameter is passed by value, if your visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore you may want the visitor to hold this state by pointer or reference.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Depth-First Visit

depth_first_visit

template <class IncidenceGraph, class DFSVisitor, class ColorMap>
void depth_first_visit(IncidenceGraph& g,
  typename graph_traits<IncidenceGraph>::vertex_descriptor s,
  DFSVisitor& vis, ColorMap color)

template <class IncidenceGraph, class DFSVisitor, class ColorMap,
          class TerminatorFunc>
void depth_first_visit(IncidenceGraph& g,
  typename graph_traits<IncidenceGraph>::vertex_descriptor s,
  DFSVisitor& vis, ColorMap color, TerminatorFunc func = TerminatorFunc())

This function visits all of the vertices in the same connected component as the source vertex s, using the depth-first pattern. The main purpose of the function is for the implementation of depth_first_search() though sometimes it is useful on its own.

The DFSVisitor supplied by the user determines what actions are taken at each event-point within the algorithm.

The ColorMap is used by the algorithm to keep track of which vertices have been visited.

The second variant can be used, for example, to find all marked vertices reachable from a start vertex by a path which does not contain any another marked vertices.

Where Defined:

boost/graph/depth_first_search.hpp

Parameters

IN IncidenceGraph& g

A directed or undirected graph. The graph's type must be a model of Incidence Graph.
Python: The parameter is named graph.

IN: vertex_descriptor s

The source vertex from which to start the search.
Python: The parameter is named root_vertex.

IN: DFSVisitor visitor

A visitor object that is invoked inside the algorithm at the event-points specified by the DFS Visitor concept. The visitor object is passed by value [1].
Python: The parameter should be an object that derives from the DFSVisitor type of the graph.

UTIL: ColorMap color

This is used by the algorithm to keep track of its progress through the graph. The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map map must model Color Value.
Python: The color map must be a vertex_color_map for the graph.

IN: TerminatorFunc func

A function object callable with two parameters - the descriptor of vertex and the graph instance - and returning bool. The call is made immediately after call to 'discover_vertex' visitor event. If true is returned, the out edges of the vertex are not examined, as if they don't exist.
Python: Unsupported parameter.

Complexity

Time complexity is O(E).

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore the visitor must hold this state by pointer or reference.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: dfs_visitor

dfs_visitor

dfs_visitor<EventVisitorList>

This class is an adaptor that converts a list of EventVisitor's (constructed using std::pair) into a DFSVisitor.

Example

See the example for EventVisitor.

Model of

DFSVisitor

Template Parameters

Parameter | Description | Default
EventVisitorList | A list of EventVisitor's created with std::pair. | null_visitor

Where Defined

boost/graph/depth_first_search.hpp

Member Functions

This class implements all of the member functions required by DFSVisitor. In each function the appropriate event is dispatched to the EventVisitor's in the EventVisitorList.

Non-Member Functions

Function Description

template <class EventVisitorList>
dfs_visitor<EventVisitorList>
make_dfs_visitor(EventVisitorList ev_list);

Returns the event visitor list adapted to be a DFS visitor.

See Also

Visitor concepts

The following are event visitors: predecessor_recorder , distance_recorder , time_stamper, and property_writer .

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.eduu)


DFS Visitor

DFS Visitor Concept

This concept defines the visitor interface for depth_first_search(). Users can define a class with the DFS Visitor interface and pass an object of the class to depth_first_search(), thereby augmenting the actions taken during the graph search.

Refinement of

Copy Constructible (copying a visitor should be a lightweight operation).

Notation

V | A type that is a model of DFS Visitor.
vis | An object of type V.
G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
s,u | An object of type boost::graph_traits<G>::vertex_descriptor.

Associated Types

none

Valid Expressions

Name | Expression | Return Type | Description
Initialize Vertex | vis.initialize_vertex(s, g) | void | This is invoked on every vertex of the graph before the start of the graph search.
Start Vertex | vis.start_vertex(s, g) | void | This is invoked on the source vertex once before the start of the search.
Discover Vertex | vis.discover_vertex(u, g) | void | This is invoked when a vertex is encountered for the first time.
Examine Edge | vis.examine_edge(e, g) | void | This is invoked on every out-edge of each vertex after it is discovered.
Tree Edge | vis.tree_edge(e, g) | void | This is invoked on each edge as it becomes a member of the edges that form the search tree.
Back Edge | vis.back_edge(e, g) | void | This is invoked on the back edges in the graph. For an undirected graph there is some ambiguity between tree edges and back edges since the edge (u,v) and (v,u) are the same edge, but both the tree_edge() and back_edge() functions will be invoked. One way to resolve this ambiguity is to record the tree edges, and then disregard the back-edges that are already marked as tree edges. An easy way to record tree edges is to record predecessors at the tree_edge event point.
Forward or Cross Edge | vis.forward_or_cross_edge(e, g) | void | This is invoked on forward or cross edges in the graph. In an undirected graph this method is never called.
Finish Vertex | vis.finish_vertex(u, g) | void | This is invoked on vertex u after finish_vertex has been called for all the vertices in the DFS-tree rooted at vertex u. If vertex u is a leaf in the DFS-tree, then the finish_vertex function is call on u after all the out-edges of u have been examined.

Models

  • dfs_visitor

Python

To implement a model of the DFSVisitor concept in Python, create a new class that derives from the DFSVisitor type of the graph, which will be named GraphType.DFSVisitor. The events and syntax are the same as with visitors in C++. Here is an example for the Python bgl.Graph graph type:
class count_tree_edges_dfs_visitor(bgl.Graph.DFSVisitor):
  def __init__(self, name_map):
    bgl.Graph.DFSVisitor.__init__(self)
    self.name_map = name_map

  def tree_edge(self, e, g):
    (u, v) = (g.source(e), g.target(e))
    print "Tree edge ",
    print self.name_map[u],
    print " -> ",
    print self.name_map[v]
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Dijkstra's Shortest Paths

dijkstra_shortest_paths

// named parameter version
template <typename Graph, typename P, typename T, typename R>
void
dijkstra_shortest_paths(Graph& g,
  typename graph_traits<Graph>::vertex_descriptor s,
  const bgl_named_params<P, T, R>& params);

// non-named parameter version
template <typename Graph, typename DijkstraVisitor,
      typename PredecessorMap, typename DistanceMap,
      typename WeightMap, typename VertexIndexMap, typename CompareFunction, typename CombineFunction,
      typename DistInf, typename DistZero, typename ColorMap = default>
void dijkstra_shortest_paths
  (const Graph& g,
   typename graph_traits<Graph>::vertex_descriptor s,
   PredecessorMap predecessor, DistanceMap distance, WeightMap weight,
   VertexIndexMap index_map,
   CompareFunction compare, CombineFunction combine, DistInf inf, DistZero zero,
   DijkstraVisitor vis, ColorMap color = default)

This algorithm [10,8] solves the single-source shortest-paths problem on a weighted, directed or undirected graph for the case where all edge weights are nonnegative. Use the Bellman-Ford algorithm when some edge weights are negative. Use breadth-first search instead of Dijkstra's algorithm when all edge weights are equal to one. For the definition of the shortest-path problem see Section Shortest-Paths Algorithms for some background to the shortest-path problem.

There are two main options for obtaining output from the dijkstra_shortest_paths() function. If the user provides a distance property map through the distance_map() parameter then the shortest distance from the source vertex to every other vertex in the graph will be recorded in the distance map. Record the shortest paths tree in a predecessor map: for each vertex u in V, p[u] will be the predecessor of u in the shortest paths tree (unless p[u] = u, in which case u is either the source or a vertex unreachable from the source). In addition to these two options, the user provides own custom-made visitor that can takes actions during any of the algorithm's event points.

Dijkstra's algorithm finds all the shortest paths from the source vertex to every other vertex by iteratively ``growing'' the set of vertices S to which it knows the shortest path. At each step of the algorithm, the next vertex added to S is determined by a priority queue. The queue contains the vertices in V - S[1] prioritized by their distance label, which is the length of the shortest path seen so far for each vertex. The vertex u at the top of the priority queue is then added to S, and each of its out-edges is relaxed: if the distance to u plus the weight of the out-edge (u,v) is less than the distance label for v then the estimated distance for vertex v is reduced. The algorithm then loops back, processing the next vertex at the top of the priority queue. The algorithm finishes when the priority queue is empty.

The algorithm uses color markers (white, gray, and black) to keep track of which set each vertex is in. Vertices colored black are in S. Vertices colored white or gray are in V-S. White vertices have not yet been discovered and gray vertices are in the priority queue. By default, the algorithm will allocate an array to store a color marker for each vertex in the graph. Use the color_map() parameter to provide own storage and access to colors.

The following is the pseudo-code for Dijkstra's single-source shortest paths algorithm. w is the edge weight, d is the distance label, and p is the predecessor of each vertex which is used to encode the shortest paths tree. Q is a priority queue that supports the DECREASE-KEY

DIJKSTRA(G, s, w)
  for each vertex u in V
    d[u] := infinity
    p[u] := u
    color[u] := WHITE
  end for
  color[s] := GRAY
  d[s] := 0
  INSERT(Q, s)
  while (Q != Ø)
    u := EXTRACT-MIN(Q)
    S := S U { u }
    for each vertex v in Adj[u]
      if (w(u,v) + d[u] < d[v])
        d[v] := w(u,v) + d[u]
        p[v] := u
        if (color[v] = WHITE)
          color[v] := GRAY
          INSERT(Q, v)
        else if (color[v] = GRAY)
          DECREASE-KEY(Q, v)
      else
        ...
    end for
    color[u] := BLACK
  end while
  return (d, p)

initialize vertex u

discover vertex s

examine vertex u

examine edge (u,v)

edge (u,v) relaxed

discover vertex v

edge (u,v) not relaxed

finish vertex u

Where Defined

boost/graph/dijkstra_shortest_paths.hpp

Parameters

IN: const Graph& g

Applies the algorithm on this graph object. The type Graph must be a model of Vertex List Graph and Incidence Graph.
Python: The parameter is named graph.

IN: vertex_descriptor s

The source vertex. All distance will be calculated from this vertex, and the shortest paths tree will be rooted at this vertex.
Python: The parameter is named root_vertex.

Named Parameters

IN: weight_map(WeightMap w_map)

The weight or ``length'' of each edge in the graph. The weights must all be non-negative, and the algorithm will throw a negative_edge exception is one of the edges is negative. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for this map must be the same as the value type of the distance map.
Default: get(edge_weight, g)
Python: Must be an edge_double_map for the graph.
Python default: graph.get_edge_double_map("weight")

IN: vertex_index_map(VertexIndexMap i_map)

This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure [61] when an edge is relaxed. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g). Note: Use this default, only if the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

OUT: predecessor_map(PredecessorMap p_map)

The predecessor map records the edges in the minimum spanning tree. Upon completion of the algorithm, the edges (p[u],u) for all u in V are in the minimum spanning tree. If p[u] = u then u is either the source vertex or a vertex that is not reachable from the source. The PredecessorMap type must be a Read/Write Property Map whose key and value types are the same as the vertex descriptor type of the graph.
Default: dummy_property_map
Python: Must be a vertex_vertex_map for the graph.

UTIL/OUT: distance_map(DistanceMap d_map)

The shortest path weight from the source vertex s to each vertex in the graph g is recorded in this property map. The shortest path weight is the sum of the edge weights along the shortest path. The type DistanceMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the distance map. The value type of the distance map is the element type of a Monoid formed with the combine function object and the zero object for the identity element. Also the distance value type must have a StrictWeakOrdering provided by the compare function object.
Default: iterator_property_map created from a std::vector of the WeightMap's value type of size num_vertices(g) and using the i_map for the index map.
Python: Must be a vertex_double_map for the graph.

IN: distance_compare(CompareFunction cmp)

This function is use to compare distances to determine which vertex is closer to the source vertex. The CompareFunction type must be a model of Binary Predicate and have argument types that match the value type of the DistanceMap property map.
Default: std::less<D> with D=typename property_traits<DistanceMap>::value_type
Python: Unsupported parameter.

IN: distance_combine(CombineFunction cmb)

This function is used to combine distances to compute the distance of a path. The CombineFunction type must be a model of Binary Function. The first argument type of the binary function must match the value type of the DistanceMap property map and the second argument type must match the value type of the WeightMap property map. The result type must be the same type as the distance value type.
Default: std::plus<D> with D=typename property_traits<DistanceMap>::value_type
Python: Unsupported parameter.

IN: distance_inf(D inf)

The inf object must be the greatest value of any D object. That is, compare(d, inf) == true for any d != inf. The type D is the value type of the DistanceMap.
Default: std::numeric_limits<D>::max()
Python: Unsupported parameter.

IN: distance_zero(D zero)

The zero value must be the identity element for the Monoid formed by the distance values and the combine function object. The type D is the value type of the DistanceMap.
Default: D()with D=typename property_traits<DistanceMap>::value_type
Python: Unsupported parameter.

UTIL/OUT: color_map(ColorMap c_map)

This is used during the execution of the algorithm to mark the vertices. The vertices start out white and become gray when they are inserted in the queue. They then turn black when they are removed from the queue. At the end of the algorithm, vertices reachable from the source vertex will have been colored black. All other vertices will still be white. The type ColorMap must be a model of Read/Write Property Map. A vertex descriptor must be usable as the key type of the map, and the value type of the map must be a model of Color Value.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.

OUT: visitor(DijkstraVisitor v)

Use this to specify actions that you would like to happen during certain event points within the algorithm. The type DijkstraVisitor must be a model of the Dijkstra Visitor concept. The visitor object is passed by value [2].
Default: dijkstra_visitor<null_visitor>
Python: The parameter should be an object that derives from the DijkstraVisitor type of the graph.

Complexity

The time complexity is O(V log V).

Visitor Event Points

  • vis.initialize_vertex(u, g) is invoked on each vertex in the graph before the start of the algorithm.

  • vis.examine_vertex(u, g) is invoked on a vertex as it is removed from the priority queue and added to set S. At this point, (p[u],u) is a shortest-paths tree edge so d[u] = delta(s,u) = d[p[u]] + w(p[u],u). Also, the distances of the examined vertices is monotonically increasing d[u1] <= d[u2] <= d[un].

  • vis.examine_edge(e, g) is invoked on each out-edge of a vertex immediately after it has been added to set S.

  • vis.edge_relaxed(e, g) is invoked on edge (u,v) if d[u] + w(u,v) < d[v]. The edge (u,v) that participated in the last relaxation for vertex v is an edge in the shortest paths tree.

  • vis.discover_vertex(v, g) is invoked on vertex v when the edge (u,v) is examined and v is WHITE. Since a vertex is colored GRAY when it is discovered, each reachable vertex is discovered exactly once. This is also when the vertex is inserted into the priority queue.

  • vis.edge_not_relaxed(e, g) is invoked if the edge is not relaxed (see above).

  • vis.finish_vertex(u, g) is invoked on a vertex after all of its out edges have been examined.

Example

See example/dijkstra-example.cpp for an example of using Dijkstra's algorithm.

Notes

[1] The algorithm used here saves a little space by not putting all V - S vertices in the priority queue at once, but instead only those vertices in V - S that are discovered and therefore have a distance less than infinity.

[2] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore, make sure the visitor to hold this state by pointer or reference.

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: dijkstra_visitor

dijkstra_visitor

dijkstra_visitor<EventVisitorList>

This class is an adapter that converts a list of EventVisitor 's (constructed using std::pair) into a DijkstraVisitor .

Example

The code snippet below, is an excerpt from examples/dave.cpp. Use the copy_graph single-event visitor to create a copy the shortest-paths search-tree calculated by Dijkstra's algorithm.

boost::dijkstra_shortest_paths
  (G, vertex(a, G),
   distance_map(make_iterator_property_map(distance.begin(), vertex_id, distance[0])).
   predecessor_map(make_iterator_property_map(parent.begin(), vertex_id, parent[0])).
   visitor(make_dijkstra_visitor(copy_graph(G_copy, on_examine_edge()))));

Model of

Dijkstra Visitor

Template Parameters

Parameter | Description | Default
EventVisitorList | A list of EventVisitor's created with std::pair. | null_visitor

Where Defined

boost/graph/dijkstra_shortest_paths.hpp

Member Functions

This class implements all of the member functions required by DijkstraVisitor. In each function the appropriate event is dispatched to the EventVisitor's in the EventVisitorList.

Non-Member Functions

Function Description

template <class EventVisitorList>
dijkstra_visitor<EventVisitorList>
make_dijkstra_visitor(EventVisitorList ev_list);

Returns the event visitor list adapted to be a Dijkstra Visitor.

See Also

Visitor concepts

The following are event visitors: predecessor_recorder , distance_recorder, time_stamper , and property_writer .

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Dijkstra Visitor

Dijkstra Visitor Concept

This concept defines the visitor interface for dijkstra_shortest_paths() and related algorithms. The user can create a class that matches this interface, and then pass objects of the class into dijkstra_shortest_paths() to augment the actions taken during the search.

Refinement of

Copy Constructible (copying a visitor should be a lightweight operation).

Notation

V | A type that is a model of Dijkstra Visitor.
vis | An object of type V.
G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
s,u,v | An object of type boost::graph_traits<G>::vertex_descriptor.
DistanceMap | A type that is a model of Read/Write Property Map.
d | An object of type DistanceMap.
WeightMap | A type that is a model of Readable Property Map.
w | An object of type DistanceMap.

Associated Types

none

Valid Expressions

Name | Expression | Return Type | Description
Initialize Vertex | vis.initialize_vertex(u, g) | void | This is invoked one each vertex of the graph when it is initialized.
Examine Vertex | vis.examine_vertex(u, g) | void | This is invoked on a vertex as it is popped from the queue. This happens immediately before examine_edge() is invoked on each of the out-edges of vertex u.
Examine Edge | vis.examine_edge(e, g) | void | This is invoked on every out-edge of each vertex after it is discovered.
Discover Vertex | vis.discover_vertex(u, g) | void | This is invoked when a vertex is encountered for the first time.
Edge Relaxed vis.edge_relaxed(e, g) void Upon examination, if the following condition holds then the edge is relaxed (its distance is reduced), and this method is invoked.
tie(u,v) = incident(e, g);
D d_u = get(d, u), d_v = get(d, v);
W w_e = get(w, e);
assert(compare(combine(d_u, w_e), d_v));
Edge Not Relaxed | vis.edge_not_relaxed(e, g) | void | Upon examination, if the edge is not relaxed (see above) then this method is invoked.
Finish Vertex | vis.finish_vertex(u, g) | void | This invoked on a vertex after all of its out edges have been added to the search tree and all of the adjacent vertices have been discovered (but before their out-edges have been examined).

Models

  • dijkstra_visitor

Python

To implement a model of the DijkstraVisitor concept in Python, create a new class that derives from the DijkstraVisitor type of the graph, which will be named GraphType.DijkstraVisitor. The events and syntax are the same as with visitors in C++. Here is an example for the Python bgl.Graph graph type:

class count_tree_edges_dijkstra_visitor(bgl.Graph.DijkstraVisitor):
  def __init__(self, name_map):
    bgl.Graph.DijkstraVisitor.__init__(self)
    self.name_map = name_map

  def edge_relaxed(self, e, g):
    (u, v) = (g.source(e), g.target(e))
    print "Relaxed edge ",
    print self.name_map[u],
    print " -> ",
    print self.name_map[v]

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: distance_recorder

Distance Recorder

distance_recorder<DistanceMap, EventTag>

This is an EventVisitor that records the distance of a vertex (using a property map) from some source vertex during a graph search. When applied to edge e = (u,v), the distance of v is recorded to be one more than the distance of u. The distance recorder is typically used with the on_tree_edge or on_relax_edge events, and cannot be used with vertex events.

Use distance_recorder with graph algorithms by wrapping it with the algorithm specific adaptor, such as bfs_visitor and dfs_visitor . Also, combine this event visitor with other event visitors using std::pair to form an EventVisitorList.

Example

See the example for bfs_visitor .

Model of

EventVisitor

Where Defined

boost/graph/visitors.hpp

Template Parameters

Parameter | Description | Default
DistanceMap | A WritablePropertyMap , where the key type and the value type are the vertex descriptor type of the graph. |
EventTag | The tag to specify when the distance_recorder should be applied during the graph algorithm. EventTag must be an edge event. |

Associated Types

Type | Description
distance_recorder::event_filter | This will be the same type as the template parameter EventTag.

Member Functions

Member | Description
distance_recorder(DistanceMap pa); | Construct a distance recorder object with distance property map pa.
template <class Edge, class Graph>
void operator()(Edge e, const Graph& g); 		Given edge e = (u,v), this records the distance of v as one plus the distance of u.

Non-Member Functions

Function Description

template <class DistanceMap, class Tag>
distance_recorder<DistanceMap, Tag>
record_distances(DistanceMap pa, Tag);

A convenient way to create a distance_recorder.

See Also

Visitor concepts

The following are other event visitors: predecessor_recorder, time_stamper, and property_writer.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Dynamic Property Maps

Dynamic Property Maps

The dynamic property map interfaces provides access to a collection of property maps through a dynamically-typed interface. An algorithm can use it to manipulate property maps without knowing their key or value types at compile-time. Type-safe codes can use dynamic property maps to interface more easily and completely with scripting languages and other text-based representations of key-value data.

  • Introduction

    • "Fred's Info" Revisited

  • Reference

    • Member Functions

    • Free functions

    • Exceptions

Introduction

The Boost Property Map library specifies statically type-safe interfaces through which key-value pairs can be manipulated by generic algorithms. Typically, an algorithm that uses property maps is parameterized on, the types of the property maps it uses, and it manipulates them using the interfaces specified by the Boost Property Map Library.

The following generic function illustrates property map basics.

template <typename AgeMap, typename GPAMap>
void
manipulate_freds_info(AgeMap ages, GPAMap gpas) {

  typedef typename boost::property_traits<AgeMap>::key_type name_type;
  typedef typename boost::property_traits<AgeMap>::value_type age_type;
  typedef typename boost::property_traits<GPAMap>::value_type gpa_type;

  name_type fred = "Fred";

  age_type old_age = get(ages, fred);
  gpa_type old_gpa = get(gpas, fred);

  std::cout << "Fred's old age: " << old_age << "\n"
            << "Fred's old gpa: " << old_gpa << "\n";

  age_type new_age = 18;
  gpa_type new_gpa = 3.9;
  put(ages, fred, new_age);
  put(gpas, fred, new_gpa);
}

The function is parameterized on two property map types, AgeMap and GPAMap, and takes a value parameter for each of those types. The function uses the property_traits interface to ascertain, at compile-time, the value and key types of the property maps. The code then retrieves Fred's old information, using the get() function, and updates it using the put() function. The get() function is required by the Readable Property Map concept and both get()and put() are required by the Read/Write Property Map concept.

The above function not only requires the two type parameters to model property map concepts, but also makes some extra assumptions. AgeMap and GPAMap must have the same key type, and that type must be constructable from a string. Furthermore, AgeMap's value type must be constructable from an int. Although these requirements are not explicitly stated, they are statically checked during compilation and failure to meet them yields compile-time errors.

Although the static typing of property map interfaces usually provides desirable compile-time safety, some algorithms require a more dynamic interface to property maps. For example, the Boost Graph Library (BGL) provides functions that can initialize a graph by interpreting the contents of a textual graph description (i.e. a GraphML file). Such general-purpose graph description languages can specify an arbitrary number of edge and vertex properties, using strings to represent the key-value pairs. A graph reader function should capture these arbitrary properties, but since function templates can only be parameterized on a fixed number of property maps, the traditional techniques for handling property maps do not suffice to implement them.

Dynamic property maps specifically address the need for an interface to property maps whose checking is delayed to runtime. Several components combine to provide support for dynamic property maps. The dynamic_properties class collects a group of heterogeneous objects that model concepts from the Boost Property Map library. Each property map is assigned a string-based key when it is added to the collection, and it can be addressed using that key. Internally, dynamic_properties adapts each contained property map with the dynamic property map interface, which provides get() and put() functions that can be called using values of any type that meets a few requirements. Internally, the dynamic property map converts key and value pairs to meet the requirements of the underlying property map or signals a runtime exception if it cannot.

"Fred's Info" Revisited

Here's what the example above looks like using the dynamic_properties interface:

void manipulate_freds_info(boost::dynamic_properties& properties)
{
  using boost::get;
  std::string fred = "Fred";

  int old_age = get<int>("age", properties, fred);
  std::string old_gpa = get("gpa", properties, fred);

  std::cout << "Fred's old age: " << old_age << "\n"
            << "Fred's old gpa: " << old_gpa << "\n";

  std::string new_age = "18";
  double new_gpa = 3.9;
  put("age",properties,fred,new_age);
  put("gpa",properties,fred,new_gpa);
}

The new function is not a template parameterized on the property map types, but instead a concrete function that takes a dynamic_properties object. Furthermore, the code no longer makes reference to key or value types: keys and values are represented with strings. Nonetheless, the function still uses non-string types where they are useful. For instance, Fred's old age is represented using an int. It's value is retrieved by calling get with a type parameter, which determines its return type. Finally, the get and put functions are each supplied a string-based key that differs depending on the property of concern.

Here's an example of how the above function might be called.

int main()
{
  using boost::get;

  // build property maps using associative_property_map
  std::map<std::string, int> name2age;
  std::map<std::string, double> name2gpa;
  boost::associative_property_map< std::map<std::string, int> >
    age_map(name2age);
  boost::associative_property_map< std::map<std::string, double> >
    gpa_map(name2gpa);

  std::string fred("Fred");
  // add key-value information
  name2age.insert(make_pair(fred,17));
  name2gpa.insert(make_pair(fred,2.7));

  // build and populate dynamic interface
  boost::dynamic_properties properties;
  properties.property("age",age_map);
  properties.property("gpa",gpa_map);

  manipulate_freds_info(properties);

  std::cout << "Fred's age: " << get(age_map,fred) << "\n"
            << "Fred's gpa: " << get(gpa_map,fred) << "\n";
}

The code first creates two property maps using std::map and the associative_property_map adaptor. After initializing the property maps with key-value data, it constructs a dynamic_properties object and adds to it both property maps, keyed on the strings "age" and "gpa". Finally, manipulate_freds_info is passed the dynamic_properties object and the results of its changes are displayed.

As shown above, the dynamic_properties object provides, where needed, a dynamically-typed interface to property maps yet preserves the static typing of property map uses elsewhere in an application.

Reference

class dynamic_properties

The dynamic_properties class provides a dynamically-typed interface to a set of property maps. To use it, one must populate an object of this class with property maps using the property member function.

Member Functions

dynamic_properties()
dynamic_properties(
  const boost::function<
    std::auto_ptr<dynamic_property_map> (
      const std::string&, const boost::any&, const boost::any&)
    >& fn)

A dynamic_properties object can be constructed with a function object that, when called, creates a new property map. If an attempt is made to put a key-value pair to a nonexistent dynamic_properties key, then this function is called with the dynamic_properties key and the intended property key and value . If dynamic_properties is default-constructed, such a put attempt throws property_not_found.

template<typename PropertyMap>
dynamic_properties&
property(const std::string& name, PropertyMap property_map)

This member function adds a property map to the set of maps contained, using name as its key.

Requirements: PropertyMap must model Readable Property Map or Read/Write Property Map.

void insert(const std::string& name, std::auto_ptr<dynamic_property_map> pm)

This member function directly adds a dynamic_property_map to the collection, using name as its key.

iterator begin()
const_iterator begin() const

This member function returns an iterator over the set of property maps held by the dynamic_properties object.

iterator end()
const_iterator end() const

This member function returns a terminal iterator over the set of dynamic property maps held by the dynamic_properties object. It is used to terminate traversals over the set of dynamic property maps.

iterator lower_bound(const std::string& name)

This member function returns an iterator that points to the first property map whose dynamic_properties key is name. Bear in mind that multiple property maps may have the same dynamic_properties key, so long as their property map key types differ.

Invariant: The range [ lower_bound(name), end() ) contains every property map that has name for its dynamic_properties key.

Free functions

template<typename Key, typename Value>
bool put(const std::string& name, dynamic_properties& dp, const Key& key,
         const Value& value)

This function adds a key-value pair to the property map with the matching name and key type. If no matching property map is found and a generator function was supplied at construction, then that function is used to create a new property map; if the dynamic_properties object was default-constructed, then property_not_found is thrown. Furthermore, if the property map found does not support put, dynamic_const_put_error is thrown.

template<typename Value, typename Key>
Value get(const std::string& name, const dynamic_properties& dp,
          const Key& key)

This function gets the value from the property-map whose name is given and whose key type matches. If Value is std::string, then the property map's value type must either be std::string or model OutputStreamable. In the latter case, the get function converts the value to a string. If no matching property map is found, dynamic_get_failure is thrown.

class dynamic_property_map

This class describes the interface used by dynamic_properties to interact with a user's property maps polymorphically.

boost::any get(const any& key)

Given a representation of a key, returns the value associated with that key.

Requirement: 1) The object passed as the key must be convertible to a value of the map's key type. Details of that conversion are unspecified.
2) For this expression to be valid, the key must be associated with some value, otherwise the result is undefined.

std::string get_string(const any& key)

Given a representation of a key, returns the string representation of the value associated with that key.

Requirements: 1) The object passed as the key must be convertible to the property map's key type. Details of that conversion are unspecified.
2) For this expression to be valid, the key must be associated with some value, otherwise the result is undefined.
3) The value type of the property map must model Output Streamable.

void put(const any& key, const any& value)

Given a representation of a key and a representation of a value, the key and value are associated in the property map.

Requirements: 1) The object passed as the key must be convertible to the property map's key type. Details of that conversion are unspecified.
2) The object passed as the value must be convertible to the property map's value type. Details of that conversion are unspecified.
3) The property map need not support this member function, in which case an error will be signaled. This is the runtime analogue of the Readable Property Map concept.

const std::type_info& key() const

Returns a type_info object that represents the property map's key type.

const std::type_info& value() const

Returns a type_info object that represents the property map's value type.

Exceptions

struct dynamic_property_exception : public std::exception {
  virtual ~dynamic_property_exception() throw() {}
};

struct property_not_found : public std::exception {
  std::string property;
  property_not_found(const std::string& property);
  virtual ~property_not_found() throw();

  const char* what() const throw();
};

struct dynamic_get_failure : public std::exception {
  std::string property;
  dynamic_get_failure(const std::string& property);
  virtual ~dynamic_get_failure() throw();

  const char* what() const throw();
};

struct dynamic_const_put_error  : public std::exception {
  virtual ~dynamic_const_put_error() throw();

  const char* what() const throw();
};

Under certain circumstances, calls to dynamic_properties member functions will throw one of the above exceptions. The three concrete exceptions can all be caught using the general dynamic_property_exception moniker when greater precision is not needed. In addition, all of the above exceptions derive from the standard std::exception for even more generalized error handling. The specific circumstances that result in these exceptions are described above.


Boost Graph Library: Edge List Class

edge_list
edge_list<EdgeIterator, ValueType, DiffType>

The edge_list class is an adaptor that turns a pair of edge iterators into a class that models EdgeListGraph. The value_type of the edge iterator must be a std::pair (or at least have first and second members). The first_type and second_type of the pair must be the same and they will be used for the graph's vertex_descriptor. The ValueType and DiffType template parameters are only needed if your compiler does not support partial specialization. Otherwise they default to the correct types.

Example

Applying the Bellman-Ford shortest paths algorithm to an edge_list.

  enum { u, v, x, y, z, N };
  char name[] = { 'u', 'v', 'x', 'y', 'z' };

  typedef std::pair<int,int> E;
  E edges[] = { E(u,y), E(u,x), E(u,v),
                E(v,u),
                E(x,y), E(x,v),
                E(y,v), E(y,z),
                E(z,u), E(z,x) };

  int weight[] = { -4, 8, 5,
                   -2,
                   9, -3,
                   7, 2,
                   6, 7 };

  typedef boost::edge_list<E*> Graph;
  Graph g(edges, edges + sizeof(edges) / sizeof(E));

  std::vector<int> distance(N, std::numeric_limits<short>::max());
  std::vector<int> parent(N,-1);

  distance[z] = 0;
  parent[z] = z;
  bool r = boost::bellman_ford_shortest_paths(g, int(N), weight,
                                              distance.begin(),
                                              parent.begin());
  if (r)
    for (int i = 0; i < N; ++i)
      std::cout << name[i] << ": " << distance[i]
                << " " << name[parent[i]] << std::endl;
  else
    std::cout << "negative cycle" << std::endl;
The output is the distance from the root and the parent of each vertex in the shortest paths tree.
  u: 2 v
  v: 4 x
  x: 7 z
  y: -2 u
  z: 0 z

Where Defined

boost/graph/edge_list.hpp

Template Parameters

Parameter | Description
EdgeIterator | Must be model of InputIterator who's value_type must be a pair of vertex descriptors.
ValueType The value_type of the EdgeIterator.
Default: std::iterator_traits<EdgeIterator>::value_type
DiffType The difference_type of the EdgeIterator.
Default: std::iterator_traits<EdgeIterator>::difference_type

Model of

EdgeListGraph

Associated Types

boost::graph_traits<edge_list>::vertex_descriptor

The type for the vertex descriptors associated with the edge_list. This will be the same type as std::iterator_traits<EdgeIterator>::value_type::first_type.

boost::graph_traits<edge_list>::edge_descriptor

The type for the edge descriptors associated with the edge_list.

boost::graph_traits<edge_list>::edge_iterator

The type for the iterators returned by edges(). The iterator category of the edge_iterator will be the same as that of the EdgeIterator.

Member Functions

edge_list(EdgeIterator first, EdgeIterator last)

Creates a graph object with n vertices and with the edges specified in the edge list given by the range [first,last].

Non-Member Functions

std::pair<edge_iterator, edge_iterator>
edges(const edge_list& g)

Returns an iterator-range providing access to the edge set of graph g.
vertex_descriptor
source(edge_descriptor e, const edge_list& g)

Returns the source vertex of edge e.
vertex_descriptor
target(edge_descriptor e, const edge_list& g)

Returns the target vertex of edge e.
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


EdgeListGraph

The EdgeListGraph concept refines the Graph concept, and adds the requirement for efficient access to all the edges in the graph.

Notation

G | A type that is a model of EdgeListGraph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.

Associated Types

boost::graph_traits<G>::traversal_category

This tag type must be convertible to edge_list_graph_tag.

boost::graph_traits<G>::edge_iterator

An edge iterator (obtained via edges(g)) provides access to all of the edges in a graph. An edge iterator type must meet the requirements of MultiPassInputIterator. The value type of the edge iterator must be the same as the edge descriptor of the graph.

boost::graph_traits<G>::edges_size_type

The unsigned integer type used to represent the number of edges in the graph.

Valid Expressions

edges(g) | Returns an iterator-range providing access to all the edges in the graph g. Return type: std::pair<edge_iterator, edge_iterator>
num_edges(g) | Returns the number of edges in the graph g. Return type: edges_size_type
source(e, g) | Returns the vertex descriptor for u of the edge (u,v) represented by e. Return type: vertex_descriptor
target(e, g) | Returns the vertex descriptor for v of the edge (u,v) represented by e. Return type: vertex_descriptor

Models

  • adjacency_list

  • edge_list

Complexity guarantees

The
edges(),
source(),
and target()
functions must all return in constant time.

See Also

Graph concepts

Concept Checking Class

  template <class G>
  struct EdgeListGraphConcept
  {
    typedef typename boost::graph_traits<G>::edge_iterator
      edge_iterator;
    void constraints() {
      function_requires< GraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<edge_iterator> >();

      p = edges(g);
      E = num_edges(g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = edges(g);
      E = num_edges(g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
    }
    std::pair<edge_iterator,edge_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::edge_descriptor e;
    typename boost::graph_traits<G>::edges_size_type E;
    G g;
  };

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


EdgeListGraph

Name

EdgeListGraph: This concept refines the Graph concept, and adds the requirement for efficient access to all the edges in the graph.

Synopsis

template <class G>
  struct EdgeListGraphConcept
  {
    typedef typename boost::graph_traits<G>::edge_iterator
      edge_iterator;
    void constraints() {
      function_requires< GraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<edge_iterator> >();

      p = edges(g);
      E = num_edges(g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = edges(g);
      E = num_edges(g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
    }
    std::pair<edge_iterator,edge_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::edge_descriptor e;
    typename boost::graph_traits<G>::edges_size_type E;
    G g;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Edge Mutable Graph

The Edge Mutable Graph concept defines the interface for a graph that supports the addition and removal of edges.

Refinement of

Graph

Associated Types

No additional associated types.

Valid Expressions

  • add_edge(u, v, g) returns std::pair<edge_descriptor, bool>

    Semantics: Try to insert the edge (u,v) into the graph, returning the inserted edge or a parallel edge and a flag that specifies whether an edge was inserted. This operation must not invalidate vertex descriptors or vertex iterators of the graph, though it may invalidate edge descriptors or edge iterators.
    Preconditions: u and v are     vertices in the graph.
    Postconditions: (u,v) is in the edge set of the graph. The returned edge descriptor will have u in the source position and v in the target position. If the graph allows parallel edges, then the returned flag is always true. If the graph does not allow parallel edges, if (u,v) was already in the graph then the returned flag is false. If (u,v) was not in the graph then the returned flag is true.

  • remove_edge(u, v, g) returns void

    Semantics: Remove the edge (u,v) from the graph. If the graph allows parallel edges this removes all occurrences of (u,v).
    Precondition: (u,v) is in the edge set of the graph.
    Postcondition: (u,v) is no longer in the edge set of the graph.

  • remove_edge(e, g) returns void

    Semantics: Remove the edge e from the graph.
    Precondition: e is an edge in the graph.
    Postcondition: e is no longer in the edge set for g.

  • clear_vertex(u, g) returns void

    Semantics: Remove all edges to and from vertex u from the graph.
    Precondition: u is a valid vertex descriptor of g.
    Postconditions: u does not appear as a source or target of any edge in g.

Complexity guarantees

For further details refer http://www.boost.org/libs/graph/doc/EdgeMutableGraph.html .

See

Graph concepts
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


EdgeMutableGraph

Name

EdgeMutableGraph : This concept defines the interface for a graph that supports the addition and removal of edges.

Synopsis

NA

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Graph Library: Edmunds-Karp Maximum Flow

edmunds_karp_max_flow

// named paramter version
template <class Graph, class P, class T, class R>
typename detail::edge_capacity_value<Graph, P, T, R>::value_type
edmunds_karp_max_flow(Graph& g,
   typename graph_traits<Graph>::vertex_descriptor src,
   typename graph_traits<Graph>::vertex_descriptor sink,
   const bgl_named_params<P, T, R>& params = all defaults)

// non-named parameter version
template <class Graph,
      class CapacityEdgeMap, class ResidualCapacityEdgeMap,
      class ReverseEdgeMap, class ColorMap, class PredEdgeMap>
typename property_traits<CapacityEdgeMap>::value_type
edmunds_karp_max_flow(Graph& g,
   typename graph_traits<Graph>::vertex_descriptor src,
   typename graph_traits<Graph>::vertex_descriptor sink,
   CapacityEdgeMap cap, ResidualCapacityEdgeMap res, ReverseEdgeMap rev,
   ColorMap color, PredEdgeMap pred)

The edmunds_karp_max_flow() function calculates the maximum flow of a network. See Section Network Flow Algorithms for a description of maximum flow. The calculated maximum flow will be the return value of the function. The function also calculates the flow values f(u,v) for all (u,v) in E, which are returned in the form of the residual capacity r(u,v) = c(u,v) - f(u,v).

There are several special requirements on the input graph and property map parameters for this algorithm. First, the directed graph G=(V,E) that represents the network must be augmented to include the reverse edge for every edge in E. That is, the input graph should be Gin = (V,{E U ET}). The ReverseEdgeMap argument rev must map each edge in the original graph to its reverse edge, that is (u,v) -> (v,u) for all (u,v) in E. The CapacityEdgeMap argument cap must map each edge in E to a positive number, and each edge in ET to 0.

The algorithm is due to Edmonds and Karp, though we are using the variation called the ``labeling algorithm'' described in Network Flows.

This algorithm provides a very simple and easy to implement solution to the maximum flow problem. However, there are several reasons why this algorithm is not as good as the push_relabel_max_flow() algorithm.

  • In the non-integer capacity case, the time complexity is O(V E2) which is worse than the time complexity of the push-relabel algorithm O(V2E1/2) for all but the sparsest of graphs.

  • In the integer capacity case, if the capacity bound U is very large then the algorithm will take a long time.

Where Defined

boost/graph/edmunds_karp_max_flow.hpp

Parameters

IN: Graph& g

A directed graph. The graph's type must be a model of VertexListGraph and IncidenceGraph For each edge (u,v) in the graph, the reverse edge (v,u) must also be in the graph.

IN: vertex_descriptor src

The source vertex for the flow network graph.

IN: vertex_descriptor sink

The sink vertex for the flow network graph.

Named Parameters

IN: capacity_map(CapacityEdgeMap cap)

The edge capacity property map. The type must be a model of a constant Lvalue Property Map. The key type of the map must be the graph's edge descriptor type.
Default: get(edge_capacity, g)

OUT: residual_capacity_map(ResidualCapacityEdgeMap res)

This maps edges to their residual capacity. The type must be a model of a mutable Lvalue Property Map. The key type of the map must be the graph's edge descriptor type.
Default: get(edge_residual_capacity, g)

IN: reverse_edge_map(ReverseEdgeMap rev)

An edge property map that maps every edge (u,v) in the graph to the reverse edge (v,u). The map must be a model of constant Lvalue Property Map. The key type of the map must be the graph's edge descriptor type.
Default: get(edge_reverse, g)

UTIL: color_map(ColorMap color)

Used by the algorithm to keep track of progress during the breadth-first search stage. At the end of the algorithm, the white vertices define the minimum cut set. The map must be a model of mutable Lvalue Property Map. The key type of the map should be the graph's vertex descriptor type, and the value type must be a model of ColorValue.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.

UTIL: predecessor_map(PredEdgeMap pred)

Use by the algorithm to store augmenting paths. The map must be a model of mutable Lvalue Property Map. The key type must be the graph's vertex descriptor type and the value type must be the graph's edge descriptor type.
Default: an iterator_property_map created from a std::vector of edge descriptors of size num_vertices(g) and using the i_map for the index map.

IN: vertex_index_map(VertexIndexMap i_map)

Maps each vertex of the graph to a unique integer in the range [0, num_vertices(g)). This property map is only needed if the default for the color or predecessor map is used. The vertex index map must be a model of Readable Property Map. The key type of the map must be the graph's vertex descriptor type.
Default: get(vertex_index, g) Note: Use this default, only if the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.

Complexity

The time complexity is O(V E2) in the general case or O(V E U) if capacity values are integers bounded by some constant U.

Example

The program in example/edmunds-karp-eg.cpp reads an example maximum flow problem (a graph with edge capacities) from a file in the DIMACS format and computes the maximum flow.

See Also

push_relabel_max_flow().

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Erdös-Renyi Generator

erdos_renyi_iterator

etemplate<typename RandomGenerator, typename Graph>
class erdos_renyi_iterator
{
public:
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<vertices_size_type, vertices_size_type> value_type;
  typedef const value_type& reference;
  typedef const value_type* pointer;
  typedef void difference_type;

  erdos_renyi_iterator();
  erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
                       double fraction = 0.0, bool allow_self_loops = false);
  erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
                       edges_size_type m, bool allow_self_loops = false);

  // Iterator operations
  reference operator*() const;
  pointer operator->() const;
  erdos_renyi_iterator& operator++();
  erdos_renyi_iterator operator++(int);
  bool operator==(const erdos_renyi_iterator& other) const;
  bool operator!=(const erdos_renyi_iterator& other) const;
};

This class template implements a generator for Erdös-Renyi graphs, suitable for initializing an adjacency_list or other graph structure with iterator-based initialization. An Erdös-Renyi graph G = (n, p) is a graph with n vertices that. The probability of having an edge (u, v) in G is p for any vertices u and v. Typically, there are no self-loops, but the generator can optionally introduce self-loops with probability p. This generator will not produce any parallel edges and will produce edges in sorted order (as required by, e.g., the compressed_sparse_row_graph).

Erdös-Renyi graphs typically exhibit very little structure. For this reason, they are rarely useful in modeling real-world problems. However, they are often used when determining the theoretical complexity of complex graph algorithms.

If you want the possibility of generating parallel edges and don't care about sorted order, use the erdos_renyi_iterator.

Where Defined

boost/graph/erdos_renyi_generator.hpp

Constructors

erdos_renyi_iterator();

Constructs a past-the-end iterator.

erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
                     double fraction = 0.0, bool allow_self_loops = false);

Constructs an Erdös-Renyi generator iterator that creates a graph with n vertices and a given fraction of the total number of edges that a simple graph may have. probability. Random vertices and edges are selected using the random number generator gen. Self-loops are permitted only when allow_self_loops is true.

erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
                     edges_size_type m, bool allow_self_loops = false);

Constructs an Erdös-Renyi generator iterator that creates a graph with n vertices and m edges. probability. Random vertices and edges are selected using the random number generator gen. Self-loops are permitted only when allow_self_loops is true.

Example

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/erdos_renyi_generator.hpp>
#include <boost/random/linear_congruential.hpp>

typedef boost::adjacency_list<> Graph;
typedef boost::erdos_renyi_iterator<boost::minstd_rand, Graph> ERGen;

int main()
{
  boost::minstd_rand gen;
  // Create graph with 100 nodes and edges with probability 0.05
  Graph g(ERGen(gen, 100, 0.05), ERGen(), 100);
  return 0;
}


Boost Graph Library: EventVisitor

EventVisitor Concept

This concept defines the interface for single-event visitors. An EventVisitor has an apply member function (operator()) which is invoked within the graph algorithm at the event-point specified by the event_filter typedef within the EventVisitor. EventVisitor's can be combined into an EventVistorList .

Invoke the event tags given in the following list in BGL algorithms. Each tag corresponds to a member function of the visitor for an algorithm. For example, the BFSVisitor of breadth_first_search() has a cycle_edge() member function. The corresponding tag is on_cycle_edge. The first argument is the event visitor's operator(), this must be either an edge or vertex descriptor depending on the event tag.

namespace boost {
  struct on_initialize_vertex { };
  struct on_start_vertex { };
  struct on_discover_vertex { };
  struct on_examine_edge { };
  struct on_tree_edge { };
  struct on_cycle_edge { };
  struct on_finish_vertex { };
  struct on_forward_or_cross_edge { };
  struct on_back_edge { };
  struct on_edge_relaxed { };
  struct on_edge_not_relaxed { };
  struct on_edge_minimized { };
  struct on_edge_not_minimized { };
} // namespace boost

Refinement of

Copy Constructible (copying a visitor should be a lightweight operation).

Notation

G | A type that is a model of Graph.
g | An object of type G.
V | A type that is a model of EventVisitor.
vis | An object of type V.
x | An object of type boost::graph_traits<G>::vertex_descriptor or boost::graph_traits<G>::edge_descriptor.

Associated Types

Event Filter V::event_filter

A tag struct to specify on which event the visitor should be invoked.

Valid Expressions

Name | Expression | Return Type | Description
Apply Visitor | vis(x, g) | void | Invokes the visitor operation on object x, which is either a vertex or edge descriptor of the graph.

Models

  • predecessor_recorder
  • distance_recorder
  • time_stamper
  • property_writer
  • null_visitor

See Also

EventVisitorList , Visitor concepts

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: EventVisitorList

EventVisitorList Concept

The EventVisitorList is either an EventVisitor , or a list of EventVisitor's combined using std::pair. Each graph algorithm defines visitor adaptors that convert an EventVisitorList into the particular kind of visitor needed by the algorithm. The following example shows how to combine event visitors into a list using std::pair and how to use an algorithm's visitor adaptor class.

For example, to print out the parenthesis structure of the discover/finish times of vertices in a depth-first search . To accomplish this, use the BGL algorithm depth_first_search() and two event visitors. The complete source code for the following example is in examples/dfs_parenthesis.cpp. First, define the two event visitors. Use on_discover_vertex and on_finish_vertex as the event points, selected from the list of event points specified in DFSVisitor.

struct open_paren : public base_visitor<open_paren> {
  typedef on_discover_vertex event_filter;
  template <class Vertex, class Graph>
  void operator()(Vertex v, Graph& G) {
    std::cout << "(" << v;
  }
};
struct close_paren : public base_visitor<close_paren> {
  typedef on_finish_vertex event_filter;
  template <class Vertex, class Graph>
  void operator()(Vertex v, Graph& G) {
    std::cout << v << ")";
  }
};

Next, create two event visitor objects and make an EventVisitorList out of them using a std::pair; created by std::make_pair.

std::make_pair(open_paren(), close_paren()))

Then, pass this list into depth_first_search(), but depth_first_search() is expecting a DFSVisitor, not a EventVisitorList. Hence use the dfs_visitor adaptor which turns an EventVisitor list into a DFSVisitor. Like all of the visitor adaptors, dfs_visitor has a creation function called make_dfs_visitor().

make_dfs_visitor(std::make_pair(open_paren(), close_paren()))

Now pass the resulting visitor object into depth_first_search() as follows.

  // graph object G is created ...

  std::vector<default_color_type> color(num_vertices(G));

  depth_first_search(G, make_dfs_visitor(std::make_pair(open_paren(), close_paren())),
                     color.begin());

For creating a list of more than two event visitors, nest calls to std::make_pair in the following way:

std::make_pair(visitor1,
  std::make_pair(visitor2,
    ...
    std::make_pair(visitorN-1, visitorN)...));

See Also

EventVisitor, Visitor concepts

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edudu)


Boost Graph Library: Exceptions

boost/graph/exception.hpp

The BGL defines several exception classes for reporting errors from BGL algorithms. Many of the BGL algorithms place certain requirements on the input graph. If these requirements are not met then the algorithm can not successfully complete, and instead throws the appropriate exception.

Synopsis

  struct bad_graph : public invalid_argument {
    bad_graph(const string& what_arg);
  };
  struct not_a_dag : public bad_graph {
    not_a_dag();
  };
  struct negative_edge : public bad_graph {
    negative_edge();
  };
  struct negative_cycle : public bad_graph {
    negative_cycle();
  };
  struct not_connected : public bad_graph {
    not_connected();
  };


Boost Graph Library: FAQ

Frequently Asked Questions

  1. How do I perform an early exit from an algorithm such as BFS?

    Create a visitor that throws an exception when there is need to cut off the search, then put the call to     breadth_first_search inside of an appropriate try/catch block. This strikes many programmers as a misuse of exceptions, however, much thought was put into the decision to have exceptions has the preferred way to exit early. See boost email discussions for more details.
  2. Why is the visitor parameter passed by value rather than reference in the various BGL algorithms?

    One of the usage scenarios that we wanted to support with the algorithms was creating visitor objects on the fly, within the argument list of the call to the graph algorithm. In this situation, the visitor object is a temporary object. Now there is a truly unfortunate rule in the C++ standard that says a temporary cannot be bound to a non-const reference parameter. So we had to decide whether we wanted to support this kind of usage and call-by-value, or not and call-by-reference. We chose call-by-value, following in the footsteps of the STL (which passes functors by value). The disadvantage of this decision is that if the visitor contains state and changes that state during the algorithm, the change will be made to a copy of the visitor object, not the visitor object passed in. Therefore the user may want the visitor to hold this state by pointer or reference.

  3. Why does the BGL interface use friend functions (or free functions) instead of member functions?

    For the most part, the differences between member functions and free functions are syntactic, and not very important, though people can get religious about them. However, we had one technical reason for favoring free functions. A programmer can overload a free function for a type coming from a 3rd party without modifying the source code/definition of that type. There are several uses of this in the BGL. For example, Stanford GraphBase and LEDA graphs can both be used in BGL algorithms because of overloads in stanford_graph.hpp and leda_graph.hpp. One can even use std::vector<std::list> as a graph due to the overloads in vector_as_graph.hpp.

    Of course, there is a way to adapt 3rd party classes into an interface with member functions. Create an adaptor class. However, the disadvantage of an adaptor class (compared to overloaded functions) is that one has to physically wrap and unwrap the objects as they go into/out of BGL algorithms. So the overloaded function route is more convenient. Granted, this is not a huge difference, but since there weren't other strong reasons, it was enough for us to choose free functions.

    Our religious reason for choosing free functions is to send the message that BGL is a generic library, and not a traditional object-oriented library. OO was hip in the 80s and 90s, but its time we moved beyond!

  4. How do I create a graph where the edges are sorted/ordered?

    The example ordered_out_edges.cpp shows how to do this.

  5. Why does the algorithm X work with adjacency_list where VertexList=vecS but not when VertexList=listS?

    Often the reason is that the algorithm expects to find the vertex_index property stored in the graph. When VertexList=vecS, the adjacency_list automatically has a vertex_index property. However, when VertexList=listS this is not the case, and the vertex_index property must be explicitly added, and initialized. For example,
      // specify the graph type
  typedef adjacency_list<listS, listS, undirectedS,
                         property<vertex_index_t, std::size_t>,
                         no_property
                        > graph_t;

  // construct a graph object
  graph_t G(num_nodes);
  // obtain a property map for the vertex_index property
  property_map<graph_t, vertex_index_t>::type
    index = get(vertex_index, G);
  // initialize the vertex_index property values
  graph_traits<graph_t>::vertex_iterator vi, vend;
  graph_traits<graph_t>::vertices_size_type cnt = 0;
  for(tie(vi,vend) = vertices(G); vi != vend; ++vi)
    put(index, *vi, cnt++);
  6. When using algorithm X, why do I get an error about a property not being found, such as:
    ../../../boost/concept_check.hpp:209: no match for
`boost::detail::error_property_not_found & ==
 boost::detail::error_property_not_found &'
    or a message such as:
    ../../..\boost/graph/depth_first_search.hpp(78) : error C2664: 'white'
: cannot convert parameter 1 from
 'struct boost::detail::error_property_not_found'
 to 'enum boost::default_color_type'

The reason is that the algorithm expected to find some property (like color or
weight) attached to the vertices or edges of the graph, but didn't
find it.
Either add an interior property to the graph, or
create an exterior property map for the property and pass it as an
argument to the algorithm.


File Dependency Example

One of the most common uses of the graph abstraction in computer science is to track dependencies. An example of dependency tracking that is dealt with, on a day to day basis is the compilation dependencies for files in programs that is written. These dependencies are used inside programs such as make or in an IDE such as Visual C++ to minimize the number of files that must be recompiled after some changes have been made. Figure 1 shows a graph that has a vertex for each source file, object file, and library that is used in the killerapp program. The edges in the graph show which files are used in creating other files. The choice of which direction to point the arrows is somewhat arbitrary. As long as the user is consistent in remembering that the arrows mean ``used by'' then things will work out. The opposite direction would mean ``depends on''.
Figure 1: A graph representing file dependencies.
A compilation system such as make has to be able to answer a number of questions:
  1. In order to compile (or recompile) all of the files, what order should that be done it?
  2. What files can be compiled in parallel?
  3. If a file is changed, which files must be recompiled?
  4. Are there any cycles in the dependencies? (which means the user has made a mistake and an error should be emitted)
In the following examples formulate each of these questions in terms of the dependency graph, and then find a graph algorithm to provide the solution. The graph in Figure 1 will be used in all of the following examples. The source code for this example can be found in the file examples/file_dependencies.cpp.

Graph Setup

To show the construction of the graph. First, these are the required header files:
#include <;iostream> // std::cout
#include <utility> // std::pair
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
For simplicity construct the graph "by-hand". A compilation system such as make would instead parse a Makefile to get the list of files and to set-up the dependencies. Use the adjacency_list class to represent the graph. The vecS selector means that a std::vector will be used to represent each edge-list, which provides efficient traversal. The bidirectionalS selector means the user wants a directed graph with access to both the edges outgoing from each vertex and the edges incoming to each vertex, and the color_property attaches a color property to each vertex of the graph. The color property will be used in several of the algorithms in the following sections.
  enum files_e { dax_h, yow_h, boz_h, zow_h, foo_cpp,
                 foo_o, bar_cpp, bar_o, libfoobar_a,
                 zig_cpp, zig_o, zag_cpp, zag_o,
                 libzigzag_a, killerapp, N };
  const char* name[] = { "dax.h", "yow.h", "boz.h", "zow.h", "foo.cpp",
                         "foo.o", "bar.cpp", "bar.o", "libfoobar.a",
                         "zig.cpp", "zig.o", "zag.cpp", "zag.o",
                         "libzigzag.a", "killerapp" };

  typedef std::pair<int, int> Edge;
  Edge used_by[] = {
    Edge(dax_h, foo_cpp), Edge(dax_h, bar_cpp), Edge(dax_h, yow_h),
    Edge(yow_h, bar_cpp), Edge(yow_h, zag_cpp),
    Edge(boz_h, bar_cpp), Edge(boz_h, zig_cpp), Edge(boz_h, zag_cpp),
    Edge(zow_h, foo_cpp),
    Edge(foo_cpp, foo_o),
    Edge(foo_o, libfoobar_a),
    Edge(bar_cpp, bar_o),
    Edge(bar_o, libfoobar_a),
    Edge(libfoobar_a, libzigzag_a),
    Edge(zig_cpp, zig_o),
    Edge(zig_o, libzigzag_a),
    Edge(zag_cpp, zag_o),
    Edge(zag_o, libzigzag_a),
    Edge(libzigzag_a, killerapp)
  };

  using namespace boost;
  typedef adjacency_list<vecS, vecS, bidirectionalS,
      property<vertex_color_t, default_color_type>
    > Graph;
  Graph g(used_by, used_by + sizeof(used_by) / sizeof(Edge), N);
  typedef graph_traits<Graph>::vertex_descriptor Vertex;

Compilation Order (All Files)

On the first invocation of make for a particular project, all of the files must be compiled. Given the dependencies between the various files, what is the correct order in which to compile and link them? First formulate this in terms of a graph. Finding a compilation order is the same as ordering the vertices in the graph. The constraint on the ordering is the file dependencies which is represented as edges. So if there is an edge (u,v) in the graph then v better not come before u in the ordering. It turns out that this kind of constrained ordering is called a topological sort. Therefore, answering the question of compilation order is as easy as calling the BGL algorithm topological_sort() . The traditional form of the output for topological sort is a linked-list of the sorted vertices. The BGL algorithm instead puts the sorted vertices into any OutputIterator, which allows for much more flexibility. Here, use the std::front_insert_iterator to create an output iterator that inserts the vertices on the front of a linked list. Other possible options are writing the output to a file or inserting into a different STL or custom-made container.
  typedef std::list<Vertex> MakeOrder;
  MakeOrder make_order;
  boost::topological_sort(g, std::front_inserter(make_order));

  std::cout << "make ordering: ";
  for (  std::cout << std::endl;
The output of this is:
  make ordering: zow.h boz.h zig.cpp zig.o dax.h yow.h zag.cpp \
  zag.o bar.cpp bar.o foo.cpp foo.o libfoobar.a libzi std::cout << std::endl;
The output of this is:
  make ordering:

Another question the compilation system might need to answer is: what  files can be compiled simultaneously? This would allow the system to spawn threads and
utilize multiplParallel Compilation . Another question the compilation system might need to answer is: what files can be compiled simultaneously? This would allow the
system to spawn threads and utilize multiple processors to speed up the build.This question can also be put in a slightly different way: what is the earliest time that a file
can be built assuming that an unlimited number of files can be built at the same time? The main criteria for when a file can be built is that all of the files it depends on must
already be built. To simplify things for this example, assume that each file takes 1 time unit to build (even header files). For parallel compilation ,it is possible to build all of
the files corresponding to vertices with no dependencies, e.g., those that have an in-degree of 0, in the first step. For all other files, the main observation for determining
the ``time slot'' for a file is that the time slot must be one more than the maximum time-slot of the files it depends on.

Start by creating a vector time that will store the time step at which each file can be built. Initialize every value with time step zero.
  std::vector<int> time(N, 0);

To visit the vertices against in topological
order, from those files that need to be built first until those that need to be
built last. However, instead of printing out the order immediately, compute the
time step in which each file should be built based on the time steps of the
files it depends on. Consider those files whose in-degree is greater than zero.

    for (i = make_order.begin(); i != make_order.end(); ++i) {

      if (in_degree (*i, g) > 0) {

        Graph::in_edge_iterator j, j_end;

        int maxdist = 0;

        for (tie(j, j_end) = in_edges(*i, g); j != j_end; ++j)

          maxdist = std::max(time[source(*j, g)], maxdist);

        time[*i]=maxdist+1;

      }ame    }

Last,output the time-slot that is calculated for each vertex.

  std::cout << "parallel make ordering, " << std::endl
       << "  vertices with same group number" << td::endl
       << "  can be made in parallel" << std::endl << std::endl;
  for (boost::tie(i, iend) = vertices(g); i != iend; ++i)
     std::cout << "time_slot[" << name[*i] << "] = " << time[*i] << std::endl;

The output is:

  parallel make ordering, = 2
  time_slot[bar.cpp] = 2
  time_slot[bar.o] = 3
  time_slot[lib  time_slot[dax.h] = 0
  time_slot[yow.h] = 1
  time_slot[boz.h] = 0
  time_slot[zow.h] = 0
  time_slot[foo.cpp] = 1
  time_slot[foo.o] = 2
  time_slot[bar.cpp] = 2
  time_slot[bar.o] = 3
  time_slot[libfoobar.a] = 4
  time_slot[zig.cpp] = 1
  time_slot[zig.o] = 2
  time_slot[zag.cpp] = 2
  time_slot[zag.o] = 3
  time_slot[libzigzag.a] = 5
  time_slot[killerapp] = 6

    Cyclic Dependencies

   Another question the
    compilation system needs to be able to answer is whether there are any
    cycles in the dependencies. If there are cycles, the system will need to
    report an error to the user so that the cycles can be removed. One easy way
    to detect a cycle is to run a depth-first search, and if the search runs
    into an already discovered vertex (of the current search tree), then there
    is a cycle. The BGL graph search algorithms (which includes
    depth_first_search()) are all extensible via the visitor mechanism. A
    visitor is similar to a function object, but it has several methods instead
    of just the one operator(). The visitor's methods are called at certain
    points within the algorithm, thereby giving the user a way to extend the
    functionality of the graph search algorithms. See Section Visitor Concepts
    for a detailed description of visitors.

    Create a visitor class and fill in the back_edge() method, which is the
    DFSVisitor method that is called when DFS explores an edge to an already
    discovered vertex. A call to this method indicates the existence of a cycle.
    Inheriting from dfs_visitor<> provides the visitor with empty versions of
    the other visitor methods. Once the visitor is created, construct and object
    and pass it to the BGL algorithm. Visitor objects are passed by value inside
    of the BGL algorithms, so the has_cycle flag is stored reference in this
    visitor.

    struct cycle_detector : public dfs_visitor<>

    {

    cycle_detector( bool& has_cycle)

    : _has_cycle(has_cycle) { }

    template <class Edge, class Graph>

    void back_edge(Edge, Graph&) {

    _has_cycle = true;

    }

    protected:

    bool& _has_cycle;

    };

    Now, invoke the BGL depth_first_search() algorithm and pass in the cycle
    detector visitor.

    bool has_cycle = false;

    cycle_detector vis(has_cycle);

    boost::depth_first_search(g, visitor(vis));

    std::cout << "The graph has a cycle? " << has_cycle << std::endl;

    The graph in Figure 1 (ignoring the dotted line)
    did not have any cycles, but if a dependency between bar.cpp and dax.h is
    added there there will be. Such a dependency would be flagged as a user
    error.

    add_edge(bar_cpp, dax_h, g);

Copyright © 2000-20011

Jeremy Siek, Indiana University


Boost Graph Library: Filtered Graph

filtered_graph
filtered_graph<Graph, EdgePredicate, VertexPredicate>

The filtered_graph class template is an adaptor that creates a filtered view of a graph. The predicate function objects determine which edges and vertices of the original graph will show up in the filtered graph. If the edge predicate returns true for an edge then it shows up in the filtered graph, and if the predicate returns false then the edge does not appear in the filtered graph. Likewise for vertices. The filtered_graph class does not create a copy of the original graph, but uses a reference to the original graph. The lifetime of the original graph must extend past any use of the filtered graph. The filtered graph does not change the structure of the original graph, though vertex and edge properties of the original graph can be changed through property maps of the filtered graph. Vertex and edge descriptors of the filtered graph are the same as, and interchangeable with, the vertex and edge descriptors of the original graph.

The num_vertices and num_edges functions do not filter before returning results, so they return the number of vertices or edges in the underlying graph, unfiltered [2].

Example

In this example, the graph's edges is filtered based on edge weight. Keep all edges with positive edge weight. First, create a predicate function object.

template <typename EdgeWeightMap>
struct positive_edge_weight {
  positive_edge_weight() { }
  positive_edge_weight(EdgeWeightMap weight) : m_weight(weight) { }
  template <typename Edge>
  bool operator()(const Edge& e) const {
    return 0 < get(m_weight, e);
  }
  EdgeWeightMap m_weight;
};
Now create a graph and print out the filtered graph.
int main()
{
  using namespace boost;

  typedef adjacency_list<vecS, vecS, directedS,
    no_property, property<edge_weight_t, int> > Graph;
  typedef property_map<Graph, edge_weight_t>::type EdgeWeightMap;

  enum { A, B, C, D, E, N };
  const char* name = "ABCDE";
  Graph g(N);
  add_edge(A, B, 2, g);
  add_edge(A, C, 0, g);
  add_edge(C, D, 1, g);
  add_edge(C, E, 0, g);
  add_edge(D, B, 3, g);
  add_edge(E, C, 0, g);

  positive_edge_weight<EdgeWeightMap> filter(get(edge_weight, g));
  filtered_graph<Graph, positive_edge_weight<EdgeWeightMap> >
    fg(g, filter);

  std::cout << "filtered edge set: ";
  print_edges(fg, name);

  std::cout << "filtered out-edges:" << std::endl;
  print_graph(fg, name);

  return 0;
}
The output is:
filtered edge set: (A,B) (C,D) (D,B)
filtered out-edges:
A --> B
B -->
C --> D
D --> B
E -->

Template Parameters

Parameter | Description | Default
Graph | The underlying graph type. |
EdgePredicate | A function object that selects which edges from the original graph will appear in the filtered graph. The function object must model Predicate. The argument type for the function object must be the edge descriptor type of the graph. Also, the predicate must be Default Constructible [1]. |
VertexPredicate | A function object that selects which vertices from the original graph will appear in the filtered graph. The function object must model Predicate. The argument type for the function object must be the vertex descriptor type of the graph. Also, the predicate must be Default Constructible [1]. | keep_all

Model of

This depends on the underlying graph type. If the underlying Graph type models VertexAndEdgeListGraph and PropertyGraph then so does the filtered graph. If the underlying Graph type models fewer or smaller concepts than these, then so does the filtered graph.

Where Defined

boost/graph/filtered_graph.hpp

Associated Types

graph_traits<filtered_graph>::vertex_descriptor

The type for the vertex descriptors associated with the filtered_graph, which is the same type as the vertex_descriptor for the original Graph.
graph_traits<filtered_graph>::edge_descriptor

The type for the edge descriptors associated with the filtered_graph, which is the same type as the edge_descriptor for the original Graph.
graph_traits<filtered_graph>::vertex_iterator

The type for the iterators returned by v ertices(). The iterator is a model of MultiPassInputIterator.
graph_traits<filtered_graph>::edge_iterator

The type for the iterators returned by edges(). The iterator is a model of MultiPassInputIterator.
graph_traits<filtered_graph>::out_edge_iterator

The type for the iterators returned by out_edges(). The iterator is a model of MultiPassInputIterator.
graph_traits<filtered_graph>::adjacency_iterator

The type for the iterators returned by adjacent_vertices(). The adjacency_iterator models the same iterator concept as out_edge_iterator.
graph_traits<filtered_graph>::directed_category

Provides information about whether the graph is directed (directed_tag) or undirected (undirected_tag).
graph_traits<filtered_graph>::edge_parallel_category

This describes whether the graph class allows the insertion of parallel edges (edges with the same source and target). The two tags are allow_parallel_edge_tag and disallow_parallel_edge_tag.
graph_traits<filtered_graph>::vertices_size_type

The type used for dealing with the number of vertices in the graph.
graph_traits<filtered_graph>::edge_size_type

The type used for dealing with the number of edges in the graph.
graph_traits<filtered_graph>::degree_size_type

The type used for dealing with the number of edges incident to a vertex in the graph.
property_map<filtered_graph, Property>::type
and
property_map<filtered_graph, Property>::const_type

The property map type for vertex or edge properties in the graph. The same property maps from the adapted graph are available in the filtered graph.

Member Functions

filtered_graph(Graph& g, EdgePredicate ep, VertexPredicate vp)

Create a filtered graph based on the graph g and the edge filter ep and vertex filter vp.

filtered_graph(Graph& g, EdgePredicate ep)

Create a filtered graph based on the graph g and the edge filter ep. All vertices from the original graph are retained.
filtered_graph(const filtered_graph& x) This creates a filtered graph for the same underlying graph as x. Anotherwords, this is a shallow copy.

filtered_graph& operator=(const filtered_graph& x)

This creates a filtered graph for the same underlying graph as x. Anotherwords, this is a shallow copy.

Non-Member Functions

Structure Access

std::pair<vertex_iterator, vertex_iterator>
vertices(const filtered_graph& g)

Returns an iterator-range providing access to the vertex set of graph g.

std::pair<edge_iterator, edge_iterator>
edges(const filtered_graph& g)

Returns an iterator-range providing access to the edge set of graph g.

std::pair<adjacency_iterator, adjacency_iterator>
adjacent_vertices(vertex_descriptor u, const filtered_graph& g)

Returns an iterator-range providing access to the vertices adjacent to vertex u in graph gg

std::pair<out_edge_iterator, out_edge_iterator>
out_edges(vertex_descriptor u, const filtered_graph& g)

Returns an iterator-range providing access to the out-edges of vertex u in graph g. If the graph is undirected, this iterator-range provides access to all edges incident on vertex u. For both directed and undirected graphs, for an out-edge e, source(e, g) == u and target(e, g) == v where v is a vertex adjacent to uu

std::pair<in_edge_iterator, in_edge_iterator>
in_edges(vertex_descriptor v, const filtered_graph& g)

Returns an iterator-range providing access to the in-edges of vertex v in graph g. For an in-edge e, target(e, g) == v and source(e, g) == u for some vertex u that is adjacent to v, whether the graph is directed or undirected.

vertex_descriptor
source(edge_descriptor e, const filtered_graph& g)

Returns the source vertex of edge ee

vertex_descriptor
target(edge_descriptor e, const filtered_graph& g)

Returns the target vertex of edge ee

degree_size_type
out_degree(vertex_descriptor u, const filtered_graph& g)

Returns the number of edges leaving vertex u.

degree_size_type
in_degree(vertex_descriptor u, const filtered_graph& g)

Returns the number of edges entering vertex u.

vertices_size_type
num_vertices(const filtered_graph& g)

Returns the number of vertices in the underlying graph [2].

edges_size_type
num_edges(const filtered_graph& g)

Returns the number of edges in the underlying graph [2].

std::pair<edge_descriptor, bool>
edge(vertex_descriptor u, vertex_descriptor v,
const filtered_graph& g)

Returns the edge connecting vertex u to vertex v in graph gg

template <typename G, typename EP, typename VP>
std::pair<out_edge_iterator, out_edge_iterator>
edge_range(vertex_descriptor u, vertex_descriptor v,
const filtered_graph& g)

Returns a pair of out-edge iterators that give the range for all the parallel edges from u to v. This function only works when the underlying graph supports edge_range, which requires that it sorts its out edges according to target vertex and allows parallel edges. The adjacency_list class with OutEdgeList=multisetS is an example of such a graph.

Property Map Access

template <class PropertyTag>
property_map<filtered_graph, PropertyTag>::type
get(PropertyTag, filtered_graph& g)

template <class PropertyTag>
property_map<filtered_graph, Tag>::const_type
get(PropertyTag, const filtered_graph& g)

Returns the property map object for the vertex property specified by PropertyTag. The PropertyTag must match one of the properties specified in the graph's VertexProperty template argument.

PropertyTag, class X>
typename property_traits<property_map<filtered_graph, PropertyTag>::const_type>::value_type
get(PropertyTag, const filtered_graph& g, X x)

This returns the property value forr x, where x is either a vertex or edge descriptor.

PropertyTag, class X, class Value>
void
put(PropertyTag, const filtered_graph& g, X x, const Value& value)

This sets the property value for x to value. is either a vertex or edge descriptor. Value must be convertible to typename property_traits<property_map<filtered_graph, PropertyTag>::type>::value_type

See Also

property_map, graph_traits

Notes

[1] The reason for requiring Default Constructible in the EdgePredicate and VertexPredicate types is that these predicates are stored by-value (for performance reasons) in the filter iterator adaptor, and iterators are required to be Default Constructible by the C++ Standard.

[2] It would be nicer to return the number of vertices (or edges) remaining after the filter has been applied, but this has two problems. The first is that it would take longer to calculate, and the second is that it would interact badly with the underlying vertex/edge index mappings. The index mapping would no longer fall in the range [0,num_vertices(g)) (resp. [0, num_edges(g))) which is assumed in many of the algorithms.

Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.eduu) 		Top


Floyd-Warshall All Pairs Shortest Paths

floyd_warshall_all_pairs_shortest_paths

// Named parameters version
template <class VertexListGraph, class DistanceMatrix,
    class P, class T, class R>
bool floyd_warshall_initialized_all_pairs_shortest_paths(
    const VertexListGraph& g, DistanceMatrix& d,
    const bgl_named_params<P, T, R>& params)

template <class VertexAndEdgeListGraph, class DistanceMatrix,
    class P, class T, class R>
bool floyd_warshall_all_pairs_shortest_paths(
    const VertexAndEdgeListGraph& g, DistanceMatrix& d,
    const bgl_named_params<P, T, R>& params)

// Positional parameter versions
\begin{verbatim}
template <typename VertexListGraph, typename DistanceMatrix,
    typename BinaryPredicate, typename BinaryFunction,
    typename Infinity, typename Zero>
bool floyd_warshall_initialized_all_pairs_shortest_paths(
    const VertexListGraph& g, DistanceMatrix& d,
    const BinaryPredicate& compare, const BinaryFunction& combine,
    const Infinity& inf, const Zero& zero)

template <typename VertexAndEdgeListGraph, typename DistanceMatrix,
    typename WeightMap, typename BinaryPredicate,
    typename BinaryFunction, typename Infinity, typename Zero>
bool floyd_warshall_all_pairs_shortest_paths(
    const VertexAndEdgeListGraph& g, DistanceMatrix& d,
    const WeightMap& w, const BinaryPredicate& compare,
    const BinaryFunction& combine,
    const Infinity& inf, const Zero& zero)

These algorithms find the shortest distance between every pair of vertices in the graph. The algorithms return false if there is a negative weight cycle in the graph, true otherwise. The shortest distance between each pair of vertices is stored in the distance matrix d. The difference between the two algorithms is in whether the distance matrix is assumed to be initialized or not, as discussed below under the OUT parameter description.

This algorithm should be used to compute shortest paths between every pair of vertices for dense graphs. For sparse graphs, use johnson_all_pairs_shortest_paths.

Where Defined

boost/graph/floyd_warshall_shortest.hpp

Parameters

IN: Graph& g
A directed or undirected graph. The graph must be a model of Vertex List Graph for calls to floyd_warshall_initialized_all_pairs_shortest_paths, and Vertex And Edge List Graph for calls to floyd_warshall_all_pairs_shortest_paths.
OUT: DistanceMatrix& d
The length of the shortest path between each pair of vertices u,v are stored in the matrix at location D[u][v]. The DistanceMatrix must be of type {M, I, V} where I is of type vertex_descriptor and V i s the type of the weight_map. The set of types must be a model of BasicMatrix, with the exceptions that it isn't required to run in constant time, and it must be mutable. The matrix must be properly initialized when it is passed to the function floyd_warshall_initialized_all_pairs_shortest_paths. If the function floyd_warshall_all_pairs_shortest_path s is used then the matrix will be initialized for the user.

Named Parameters

IN: weight_map(WeightMap w)
The weight of length of each edge in the graph. The WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value_type of the weight map must be the type of the DistanceMatrix, and must always either be part of the graph passed to the function, or passed in as a parameter.
Default: get(edge_weight, g)
IN: distance_compare(CompareFunction cmp)
The function used to compare distances to determine which target vertex is closer to the source vertex. The CompareFunction must be a model of BinaryPredicate. T he argument types must match the value type of the WeightMap.
Default: std::less<WM>with WM = typename property_traits<WeightMap>::value_type
IN: distance_combine(CombineFunction cmb)
The function used to combine distance to compute the distance of a path. The CombineFunction must be a model of BinaryFunction. The argument types must match the value type of the WeightMap. The result type must be the same as the distance value type.
Default: std::plus<WM> with WM = typename property_traits<WeightMap>::value_type
IN: distance_inf(WM inf)
The value used to initialize the distance for each vertex before starting the algorithm, and to represent the distance between vertices for which there is not path. Should be larger than any possible valid path length. The argument type must match the value type of the WeightMap.
Default: std::numeric_limits<WM>::max() with WM = typename property_traits<WeightMap>::value_type
IN: distance_zero(WM zero)
The value used to represent the distance from a vertex to itself, and to determine if a value is negative. The argument type must match the value type of the WeightMap.
 Default: 0

Complexity

The time complexity is O(V3).
Copyright © 2002-2004
Lauren Foutz, Rensselaer Polytechnic Institute
Scott Hill, Rensselaer Polytechnic Institute 		Top


Boost Graph Library: Fruchterman-Reingold Force-Directed Layout

fruchterman_reingold_force_directed_layout

// named parameter version
template<typename Graph, typename PositionMap, typename Dim, typename Param,
     typename Tag, typename Rest>
void
fruchterman_reingold_force_directed_layout
  (const Graph&    g,
   PositionMap     position,
   Dim             width,
   Dim             height,
   const bgl_named_params<Param, Tag, Rest>& params);

// non-named parameter version
template<typename Graph, typename PositionMap, typename Dim,
         typename AttractiveForce, typename RepulsiveForce,
         typename ForcePairs, typename DisplacementMap, typename Cooling>
void
fruchterman_reingold_force_directed_layout
 (const Graph&    g,
  PositionMap     position,
  Dim             width,
  Dim             height,
  AttractiveForce fa,
  RepulsiveForce  fr,
  ForcePairs      fp,
  Cooling         cool,
  DisplacementMap displacement);

template<typename Graph, typename PositionMap, typename Dim>
void
fruchterman_reingold_force_directed_layout(const Graph&    g,
                            PositionMap     position,
                            Dim             width,
                            Dim             height);

This algorithm [58] performs layout of unweighted, undirected graphs. Unlike the Kamada-Kawai layout algorithm, this algorithm directly supports the layout of disconnected graphs (but see the force_pairs named parameter). It is a force-directed algorithm, meaning that vertex layout is determined by the forces pulling vertices together and pushing them apart. Attractive forces occur between adjacent vertices only, whereas repulsive forces occur between every pair of vertices. Each iteration computes the sum of the forces on each vertex, then moves the vertices to their new positions. The movement of vertices is mitigated by the temperature of the system for that iteration: as the algorithm progresses through successive iterations, the temperature should decrease so that vertices settle in place. The cooling schedule, attractive forces, and repulsive forces can be provided by the user.

The vertices are often placed randomly prior to execution of this algorithm via random_graph_layout.

Where Defined

boost/graph/fruchterman_reingold.hpp

Parameters

IN: const Graph& g
The graph object on which the algorithm will be applied. The type Graph must be a model of Vertex And Edge List Graph.
Python: This parameter is named graph in Python.
IN/OUT: PositionMap position
The property map that stores the position of each vertex. It should typically be initialized with the vertices at random locations (use random_graph_layout ). The type PositionMap must be a model of Lvalue Property Map such that the vertex descriptor type of Graph is convertible to its key type. Its value type must be a structure with fields x and y, representing the coordinates of the vertex.
Python: The position map must be a vertex_point2d_map for the graph.
Python default: graph.get_vertex_point2d_map("position")
IN: Dim width
The width of the display area in which layout should occur. On termination of the algorithm, the x coordinates of all vertices will fall in [-width/2, width/2].
IN: Dim height
The height of the display area in which layout should occur. On termination of the algorithm, the y coordinates of all vertices will fall in [-height/2, height/2].

Named Parameters

IN: attractive_force(AttractiveForce fa)
Computes the magnitude of the attractive force between two adjacent vertices. The function object fa must accept four parameters: the edge descriptor, k, the distance between the vertices, and the graph. k is the square root of the ratio of the display area to the number of vertices.
Default: square_distance_attractive_force(), which computes the attractive force as dist2/k.
Python: Any callable Python object that matches the signature will suffice.
IN: repulsive_force(RepulsiveForce fr)
Computes the magnitude of the repulsive force between any two vertices. The function object fa must accept five parameters: the two vertex descriptors, k,
Default: square_distance_repsulsive_force(),, which computes the repulsive force as k2/dist Python: Any callable Python object that matches the signature will suffice.
IN: force_pairs(ForcePairs fp)
Enumerates the pairs of vertices on which the repulsive force should be applied. fp is a function object taking two parameters: the graph g and a binary function object that should be passed each pair of vertices to be considered. The basic formulation of the Fruchterman-Reingold algorithm computes repulsive forces between all pairs of vertices (pass all_force_pairs() for this parameter), which is functional for disconnected graphs but tends to push the connected components toward the edges of the display area. The grid variant of the algorithm places a grid over the display area and only computes repulsive forces among vertices within each rectangle in the grid. The grid variant can be more efficient than the basic formulation and tends to produce better layouts for disconnected graphs, but is not better overall: pass make_grid_force_pairs(width, height, position, g)
Default: make_grid_force_pairs(width, height, position, g)
Python: Unsupported parameter.
IN: cooling(Cooling cool)

Default: linear_cooling<double>(100)
Python: Any callable Python object that matches the signature will suffice.
UTIL: displacement_map(DisplacementMap displacement)
The displacement map is used to compute the amount by which each vertex will move in each step. The DisplacementMap type carries the same requirements as the PositionMap
Default: Ann iterator_property_map with a value type of simple_point<double>
Python: Unsupported parameter.
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This is only necessary when no displacement map is provided. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer typDefault: get(vertex_index, g) Note: if you use this default, make sure your graph has an internalal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.
Python IN: bool progressive
When false, performs a random layout of the graph before running the Fruchterman-Reingold algorithm. If true, the algorithm is executing starting with the vertex configuration in the position Default: False.

Complexity

The time complexity is O(|V|2 + |E|) for each iteration of the algorithm in the worse case. The average case for the grid variant is O(|V| + |E|). The number of iterations is determined by the cooling schedule.

Example

libs/graph/example/fr_layout.cpp
Copyright © 2004
Doug Gregor, Indiana University 		Top


Graph

The Graph concept contains a few requirements that are common to all the graph concepts. These include some associated types for vertex_descriptor, edge_descriptor, etc.

Note: A model of Graph is not required to be a model of Assignable, algorithms should pass graph objects by reference.

Notation

G | A type that is a model of Graph.
g | An object of type G.

Associated Types

boost::graph_traits<G>::vertex_descriptor

A vertex descriptor corresponds to a unique vertex in an abstract graph instance. A vertex descriptor must be Default Constructible, Assignable, and Equality Comparable.

boost::graph_traits<G>::edge_descriptor

An edge descriptor corresponds to a unique edge (u,v) in a graph. An edge descriptor must be Default Constructible, Assignable, and Equality Comparable.

boost::graph_traits<G>::directed_category

This type shall be convertible to directed_tag or undirected_tag.

boost::graph_traits<G>::edge_parallel_category

This describes whether the graph class allows the insertion of parallel edges (edges with the same source and target). The two tags are allow_parallel_edge_tag and disallow_parallel_edge_tag.

boost::graph_traits<G>::traversal_category

This describes the ways in which the vertices and edges of the graph can be visited. The choices are incidence_graph_tag, adjacency_graph_tag, bidirectional_graph_tag, vertex_list_graph_tag, edge_list_graph_tag, and adjacency_matrix_tag.

Valid Expressions

boost::graph_traits<G>::null_vertex()

Returns a special boost::graph_traits<G>::vertex_descriptor object which does not refer to any vertex of graph object which type is G.

Concept Checking Class

  template <class G>
  struct GraphConcept
  {
    typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    typedef typename boost::graph_traits<G>::directed_category directed_category;
    typedef typename boost::graph_traits<G>::edge_parallel_category edge_parallel_category;
    typedef typename boost::graph_traits<G>::traversal_category traversal_category;

    void constraints() {
      function_requires< DefaultConstructibleConcept<vertex_descriptor> >();
      function_requires< EqualityComparableConcept<vertex_descriptor> >();
      function_requires< AssignableConcept<vertex_descriptor> >();
      function_requires< DefaultConstructibleConcept<edge_descriptor> >();
      function_requires< EqualityComparableConcept<edge_descriptor> >();
      function_requires< AssignableConcept<edge_descriptor> >();
    }
    G g;
  };

See Also

Graph concepts

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Graph Coloring Example

Graph Coloring

The graph (or vertex) coloring problem, which involves assigning colors to vertices in a graph such that adjacent vertices have distinct colors, arises in a number of scientific and engineering applications such as scheduling , register allocation , optimization and parallel numerical computation.

Mathematically, a proper vertex coloring of an undirected graph G=(V,E) is a map c: V -> S such that c(u) != c(v) whenever there exists an edge (u,v) in G. The elements of set S are called the available colors. The problem is often to determine the minimum cardinality (the number of colors) of S for a given graph G or to ask whether it is able to color graph G with a certain number of colors. For example, how many color do we need to color the United States on a map in such a way that adjacent states have different color? A compiler needs to decide whether variables and temporaries could be allocated in fixed number of registers at some point. If a target machine has K registers, can a particular interference graph be colored with K colors? (Each vertex in the interference graph represents a temporary value; each edge indicates a pair of temporaries that cannot be assigned to the same register.)

Another example is in the estimation of sparse Jacobian matrix by differences in large scale nonlinear problems in optimization and differential equations. Given a nonlinear function F, the estimation of Jacobian matrix J can be obtained by estimating Jd for suitable choices of vector d. Curtis, Powell and Reid [9] observed that a group of columns of J can be determined by one evaluation of Jd if no two columns in this group have a nonzero in the same row position. Therefore, a question is emerged: what is the number of function evaluations need to compute approximate Jacobian matrix? As a matter of fact this question is the same as to compute the minimum numbers of colors for coloring a graph if graph is constructed in the following manner: A vertex represents a column of J and there is an edge if and only if the two column have a nonzero in the same row position.

However, coloring a general graph with the minimum number of colors (the cardinality of set S) is known to be an NP-complete problem [30]. One often relies on heuristics to find a solution. A widely-used general greedy based approach is starting from an ordered vertex enumeration v1, ..., vn of G, to assign vi to the smallest possible color for i from 1 to n. In section Constructing graph algorithms with BGL we have shown how to write this algorithm in the generic programming paradigm. However, the ordering of the vertices dramatically affects the coloring. The arbitrary order may perform very poorly while another ordering may produces an optimal coloring. Several ordering algorithms have been studied to help the greedy coloring heuristics including largest-first ordering, smallest-last ordering and incidence degree ordering.

In the BGL framework, the process of constructing/prototyping such a ordering is fairly easy because writing such a ordering follows the algorithm description closely. As an example, we present the smallest-last ordering algorithm.

The basic idea of the smallest-last ordering [29] is as follows: Assuming that the vertices vk+1, ..., vn have been selected, choose vk so that the degree of vk in the subgraph induced by V - {vk+1, ..., vn} is minimal.

The algorithm uses a bucket sorter for the vertices in the graph where bucket is the degree. Two vertex property maps, degree and marker, are used in the algorithm. The former is to store degree of every vertex while the latter is to mark whether a vertex has been ordered or processed during traversing adjacent vertices. The ordering is stored in the order. The algorithm is as follows:

  • put all vertices in the bucket sorter
  • find the vertex node with smallest degree in the bucket sorter
  • number node and traverse through its adjacent vertices to update its degree and the position in the bucket sorter.
  • go to the step 2 until all vertices are numbered.

namespace boost {
  template <class VertexListGraph, class Order, class Degree,
            class Marker, class BucketSorter>
  void
  smallest_last_ordering(const VertexListGraph& G, Order order,
                         Degree degree, Marker marker,
                         BucketSorter& degree_buckets) {
    typedef typename graph_traits<VertexListGraph> GraphTraits;

    typedef typename GraphTraits::vertex_descriptor Vertex;
    //typedef typename GraphTraits::size_type size_type;
    typedef size_t size_type;

    const size_type num = num_vertices(G);

    typename GraphTraits::vertex_iterator v, vend;
    for (tie(v, vend) = vertices(G); v != vend; ++v) {
      put(marker, *v, num);
      put(degree, *v, out_degree(*v, G));
      degree_buckets.push(*v);
    }

    size_type minimum_degree = 1;
    size_type current_order = num - 1;

    while ( 1 ) {
      typedef typename BucketSorter::stack MDStack;
      MDStack minimum_degree_stack = degree_buckets[minimum_degree];
      while (minimum_degree_stack.empty())
        minimum_degree_stack = degree_buckets[++minimum_degree];

      Vertex node = minimum_degree_stack.top();
      put(order, current_order, node);

      if ( current_order == 0 ) //find all vertices
        break;

      minimum_degree_stack.pop();
      put(marker, node, 0); //node has been ordered.

      typename GraphTraits::adjacency_iterator v, vend;
      for (tie(v,vend) = adjacent_vertices(node, G); v != vend; ++v)

        if ( get(marker, *v) > current_order ) { //*v is unordered vertex
          put(marker, *v, current_order);   //mark the columns adjacent to node

          //It is possible minimum degree goes down
          //Here we keep tracking it.
          put(degree, *v, get(degree, *v) - 1);
          minimum_degree = std::min(minimum_degree, get(degree, *v));

          //update the position of *v in the bucket sorter
          degree_buckets.update(*v);
        }

      current_order--;
    }
    //at this point, get(order, i) == vertex(i, g);
  }
} // namespace boost
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Concepts

Graph Concepts

The heart of the Boost Graph Library (BGL) is the interface, or concepts (in the parlance of generic programming), that define how a graph can be examined and manipulated in a data-structure neutral fashion. In fact, the BGL interface need not even be implemented using a data-structure, as for some problems it is easier or more efficient to define a graph implicitly based on some functions.

The BGL interface does not appear as a single graph concept. Instead it is factored into much smaller pieces. The reason for this is that the purpose of a concept is to summarize the requirements for particular algorithms. Any one algorithm does not need every kind of graph operation, typically only a small subset. Furthermore, there are many graph data-structures that can not provide efficient implementations of all the operations, but provide highly efficient implementations of the operations necessary for a particular algorithm . By factoring the graph interface into many smaller concepts we provide the graph algorithm writer with a good selection from which to choose the concept that is the closest match for their algorithm.

Graph Structure Concepts Overview

Figure 1 shows the refinements relations between the graph concepts. The reason for factoring the graph interface into so many concepts is to encourage algorithm interfaces to require and use only the minimum interface of a graph, thereby increasing the reusability of the algorithm.

Figure 1: The graph concepts and refinement relationships.

Table 1 gives a summary of the valid expressions and associated types for the graph concepts and provides links to the detailed descriptions of each of the concepts. The notation used in the table is as follows.

Notation

G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
e_iter | An object of type boost::graph_traits<G>::out_edge_iterator.
u,v | Are objects of type boost::graph_traits<G>::vertex_descriptor.
ep | is an object of type G::edge_property_type
vp | is an object of type G::vertex_property_type
Property | A type used to specify a vertex or edge property.
property | An object of type Property.

Table 1: Summary of the graph concepts.
Expression | Return Type or Description
Graph
boost::graph_traits<G>::vertex_descriptor | The type for vertex representative objects.
boost::graph_traits<G>::edge_descriptor | The type for edge representative objects.
boost::graph_traits<G>::directed_category | Directed or undirected?
boost::graph_traits<G>::edge_parallel_category | Allow parallel edges?
boost::graph_traits<G>::traversal_category | The ways in which the vertices and edges of the graph can be visited.
IncidenceGraph refines Graph
boost::graph_traits<G>::out_edge_iterator | Iterate through the out-edges.
boost::graph_traits<G>::degree_size_type | The integer type for vertex degee.
out_edges(v, g) | std::pair<out_edge_iterator, out_edge_iterator>
source(e, g) | vertex_descriptor
target(e, g) | vertex_descriptor
out_degree(v, g) | degree_size_type
BidirectionalGraph refines IncidenceGraph
boost::graph_traits<G>::in_edge_iterator | Iterate through the in-edges.
in_edges(v, g) | std::pair<in_edge_iterator, in_edge_iterator>
in_degree(v, g) | degree_size_type
degree(e, g) | degree_size_type
AdjacencyGraph refines Graph
boost::graph_traits<G>::adjacency_iterator | Iterate through adjacent vertices.
adjacent_vertices(v, g) | std::pair<adjacency_iterator, adjacency_iterator>
VertexListGraph refines IncidenceGraph and AdjacencyGraph
boost::graph_traits<G>::vertex_iterator | Iterate through the graph's vertex set.
boost::graph_traits<G>::vertices_size_type | The unsigned integer type for number of vertices in the graph.
vertices(g) | std::pair<vertex_iterator, vertex_iterator>
num_vertices(g) | vertices_size_type
EdgeListGraph refines Graph
boost::graph_traits<G>::edge_iterator | Iterate through the graph's edge set.
boost::graph_traits<G>::edges_size_type | The unsigned integer type for number of edges in the graph.
edges(g) | std::pair<edge_iterator, edge_iterator>
num_edges(g) | edges_size_type
source(e, g) | vertex_descriptor
target(e, g) | vertex_descriptor
AdjacencyMatrix refines Graph
edge(u, v, g) | std::pair<edge_descriptor, bool>
MutableGraph refines Graph
add_vertex(g) | vertex_descriptor
clear_vertex(v, g) | void
remove_vertex(v, g) | void
add_edge(u, v, g) | std::pair<edge_descriptor, bool>
remove_edge(u, v, g) | void
remove_edge(e, g) | void
remove_edge(e_iter, g) | void
MutablePropertyGraph refines Graph
add_vertex(vp, g) | vertex_descriptor
add_edge(u, v, ep, g) | std::pair<edge_descriptor, bool>
PropertyGraph refines Graph
boost::property_map<G, Property>::type | Type for a mutable property map.
boost::property_map<G, Property>::const_type | Type for a non-mutable property map.
get(property, g) | Function to get a property map.
get(property, g, x) | Get property value for vertex or edge x.
put(property, g, x, v) | Set property value for vertex or edge x to v.

Undirected Graphs

The interface that the BGL provides for accessing and manipulating an undirected graph is the same as the interface for directed graphs described in the following sections, however there are some differences in the behaviour and semantics. For example, in a directed graph we can talk about out-edges and in-edges of a vertex. In an undirected graph there is no ``in'' and ``out'', there are just edges incident to a vertex. Nevertheless, in the BGL we still use the out_edges() function (or in_edges()) to access the incident edges in an undirected graph. Similarly, an undirected edge has no ``source'' and ``target'' but merely an unordered pair of vertices, but in the BGL we still use source() and target() to access these vertices. The reason the BGL does not provide a separate interface for undirected graphs is that many algorithms on directed graphs also work on undirected graphs, and it would be inconvenient to have to duplicate the algorithms just because of an interface difference. When using undirected graphs just mentally disregard the directionality in the function names. The example below demonstrates using the out_edges(), source(), and target() with an undirected graph. The source code for this example and the following one can be found in examples/undirected.cpp.

  const int V = 2;
  typedef ... UndirectedGraph;
  UndirectedGraph undigraph(V);

  std::cout << "the edges incident to v: ";
  boost::graph_traits<UndirectedGraph>::out_edge_iterator e, e_end;
  boost::graph_traits<UndirectedGraph>::vertex_descriptor
    s = vertex(0, undigraph);
  for (tie(e, e_end) = out_edges(s, undigraph); e != e_end; ++e)
    std::cout << "(" << source(*e, undigraph)
              << "," << target(*e, undigraph) << ")" << endl;

Even though the interface is the same for undirected graphs, there are some behavioral differences because edge equality is defined differently. In a directed graph, edge (u,v) is never equal to edge (v,u), but in an undirected graph they may be equal. If the undirected graph is a multigraph then (u,v) and (v,u) might be parallel edges. If the graph is not a multigraph then (u,v) and (v,u) must be the same edge.

In the example below the edge equality test will return false for the directed graph and true for the undirected graph. The difference also affects the meaning of add_edge(). In the example below, if we had also written add_add(v, u, undigraph), this would have added a parallel edge between u and v (provided the graph type allows parallel edges). The difference in edge equality also affects the association of edge properties. In the directed graph, the edges (u,v) and (v,u) can have distinct weight values, whereas in the undirected graph the weight of (u,v) is the same as the weight of (v,u) since they are the same edge.

  typedef ... DirectedGraph;
  DirectedGraph digraph(V);
  {
    boost::graph_traits<DirectedGraph>::vertex_descriptor u, v;
    u = vertex(0, digraph);
    v = vertex(1, digraph);
    add_edge(digraph, u, v, Weight(1.2));
    add_edge(digraph, v, u, Weight(2.4));
    boost::graph_traits<DirectedGraph>::edge_descriptor e1, e2;
    bool found;
    tie(e1, found) = edge(u, v, digraph);
    tie(e2, found) = edge(v, u, digraph);
    std::cout << "in a directed graph is ";
    std::cout << "(u,v) == (v,u) ? " << (e1 == e2) << std::endl;

    property_map<DirectedGraph, edge_weight_t>::type
      weight = get(edge_weight, digraph);
    cout << "weight[(u,v)] = " << get(weight, e1) << endl;
    cout << "weight[(v,u)] = " << get(weight, e2) << endl;
  }
  {
    boost::graph_traits<UndirectedGraph>::vertex_descriptor u, v;
    u = vertex(0, undigraph);
    v = vertex(1, undigraph);
    add_edge(undigraph, u, v, Weight(3.1));
    boost::graph_traits<UndirectedGraph>::edge_descriptor e1, e2;
    bool found;
    tie(e1, found) = edge(u, v, undigraph);
    tie(e2, found) = edge(v, u, undigraph);
    std::cout << "in an undirected graph is ";
    std::cout << "(u,v) == (v,u) ? " << (e1 == e2) << std::endl;

    property_map<UndirectedGraph, edge_weight_t>::type
      weight = get(edge_weight, undigraph);
    cout << "weight[(u,v)] = " << get(weight, e1) << endl;
    cout << "weight[(v,u)] = " << get(weight, e2) << endl;
  }

The output is:

in a directed graph is (u,v) == (v,u) ? 0
weight[(u,v)] = 1.2
weight[(v,u)] = 2.4
in an undirected graph is (u,v) == (v,u) ? 1
weight[(u,v)] = 3.1
weight[(v,u)] = 3.1

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Graph

Name

Graph: The Graph concept contains a few requirements that are common to all the graph concepts. These include some associated types for vertex_descriptor, edge_descriptor, etc.

Synopsis

template <class G>
  struct GraphConcept
  {
    typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    typedef typename boost::graph_traits<G>::directed_category directed_category;
    typedef typename boost::graph_traits<G>::edge_parallel_category edge_parallel_category;
    typedef typename boost::graph_traits<G>::traversal_category traversal_category;

    void constraints() {
      function_requires< DefaultConstructibleConcept<vertex_descriptor> >();
      function_requires< EqualityComparableConcept<vertex_descriptor> >();
      function_requires< AssignableConcept<vertex_descriptor> >();
      function_requires< DefaultConstructibleConcept<edge_descriptor> >();
      function_requires< EqualityComparableConcept<edge_descriptor> >();
      function_requires< AssignableConcept<edge_descriptor> >();
    }
    G g;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Graph Library: Graph Theory Review

Review of Elementary Graph Theory

This chapter is meant as a refresher on elementary graph theory. If the reader has some previous acquaintance with graph algorithms, this chapter should be enough to get started. If the reader has no previous background in graph algorithms we suggest a more thorough introduction such as Introduction to Algorithms by Cormen, Leiserson, and Rivest.

The Graph Abstraction

A graph is a mathematical abstraction that is useful for solving many kinds of problems. Fundamentally, a graph consists of a set of vertices, and a set of edges, where an edge is something that connects two vertices in the graph. More precisely, a graph is a pair (V,E), where V is a finite set and E is a binary relation on V. V is called a vertex set whose elements are called vertices. E is a collection of edges, where an edge is a pair (u,v) with u,v in V. In a directed graph, edges are ordered pairs, connecting a source vertex to a target vertex. In an undirected graph edges are unordered pairs and connect the two vertices in both directions, hence in an undirected graph (u,v) and (v,u) are two ways of writing the same edge.

This definition of a graph is vague in certain respects; it does not say what a vertex or edge represents. They could be cities with connecting roads, or web-pages with hyperlinks. These details are left out of the definition of a graph for an important reason; they are not a necessary part of the graph abstraction. By leaving out the details we can construct a theory that is reusable, that can help us solve lots of different kinds of problems.

Back to the definition: a graph is a set of vertices and edges. For purposes of demonstration, let us consider a graph where we have labeled the vertices with letters, and we write an edge simply as a pair of letters. Now we can write down an example of a directed graph as follows:

V = {v, b, x, z, a, y }
E = { (b,y), (b,y), (y,v), (z,a), (x,x), (b,x), (x,v), (a,z) }
G = (V, E)

Figure 1 gives a pictorial view of this graph. The edge (x,x) is called a self-loop. Edges (b,y) and (b,y) are parallel edges, which are allowed in a multigraph (but are normally not allowed in a directed or undirected graph).

Figure 1: Example of a directed graph.

Next we have a similar graph, though this time it is undirected. Figure 2 gives the pictorial view. Self loops are not allowed in undirected graphs. This graph is the undirected version(b,y)), meaning it has the same vertices and the same edges with their directions removed. Also the self edge has been removed, and edges such as (a,z) and (z,a) are collapsed into one edge. One can go the other way, and make a directed version of an undirected graph be replacing each edge by two edges, one pointing in each direction.

V = {v, b, x, z, a, y }
E = { (b,y), (y,v), (z,a), (b,x), (x,v) }
G = (V, E)

Figure 2: Example of an undirected graph.

Now for some more graph terminology. If some edge (u,v) is in graph , then vertex v is adjacent to vertex u. In a directed graph, edge (u,v) is an out-edge of vertex u and an in-edge of vertex v. In an undirected graph edge (u,v) is incident on vertices u and v.

In Figure 1, vertex y is adjacent to vertex b (but b is not adjacent to y). The edge (b,y) is an out-edge of b and an in-edge of y. In Figure 2, y is adjacent to b and vice-versa. The edge (y,b) is incident on vertices y and b.

In a directed graph, the number of out-edges of a vertex is its out-degree and the number of in-edges is its in-degree. For an undirected graph, the number of edges incident to a vertex is its degree. In Figure 1, vertex b has an out-degree of 3 and an in-degree of zero. In Figure 2 , vertex b simply has a degree of 2.

Now a path is a sequence of edges in a graph such that the target vertex of each edge is the source vertex of the next edge in the sequence. If there is a path starting at vertex u and ending at vertex v we say that v is reachable from u. A path is simple if none of the vertices in the sequence are repeated. The path <(b,x), (x,v)> is simple, while the path <(a,z), (z,a)> is not. Also, the path <(a,z), (z,a)> is called a cycle because the first and last vertex in the path are the same. A graph with no cycles is acyclic.

A planar graph is a graph that can be drawn on a plane without any of the edges crossing over each other. Such a drawing is called a plane graph. A face of a plane graph is a connected region of the plane surrounded by edges. An important property of planar graphs is that the number of faces, edges, and vertices are related through Euler's formula: |F| - |E| + |V| = 2. This means that a simple planar graph has at most O(|V|) edges.

Graph Data Structures

The primary property of a graph to consider when deciding which data structure to use is sparsity, the number of edges relative to the number of vertices in the graph. A graph where E is close to V2 is a dense graph, whereas a graph where E = alpha V and alpha is much smaller than V is a sparse graph. For dense graphs, the adjacency-matrix representation is usually the best choice, whereas for sparse graphs the adjacency-list representation is a better choice. Also the edge-list representation is a space efficient choice for sparse graphs that is appropriate in some situations.

Adjacency Matrix Representation

An adjacency-matrix representation of a graph is a 2-dimensional V x V array. Each element in the array auv stores a Boolean value saying whether the edge (u,v) is in the graph. Figure 3 depicts an adjacency matrix for the graph in Figure 1 (minus the parallel edge (b,y)). The ammount of space required to store an adjacency-matrix is O(V2). Any edge can be accessed, added, or removed in O(1) time. To add or remove a vertex requires reallocating and copying the whole graph, an O(V2) operation. The adjacency_matrix class implements the BGL graph interface in terms of the adjacency-matrix data-structure.

Figure 3: The Adjacency Matrix Graph Representation.

Adjacency List Representation

An adjacency-list representation of a graph stores an out-edge sequence for each vertex. For sparse graphs this saves space since only O(V + E) memory is required. In addition, the out-edges for each vertex can be accessed more efficiently. Edge insertion is O(1), though accessing any given edge is O(alpha), where alpha is the sparsity factor of the matrix (which is equal to the maximum number of out-edges for any vertex in the graph). Figure 4 depicts an adjacency-list representation of the graph in Figure 1. The adjacency_list class is an implementation of the adjacency-list representation.

Figure 4: The Adjacency List Graph Representation.

Edge List Representation

An edge-list representation of a graph is simply a sequence of edges, where each edge is represented as a pair of vertex ID's. The memory required is only O(E). Edge insertion is typically O(1), though accessing a particular edge is O(E) (not efficient). Figure 5 shows an edge-list representation of the graph in Figure 1. The edge_list adaptor class can be used to create implementations of the edge-list representation.

Figure 5: The Edge List Graph Representation.

Graph Algorithms

Graph Search Algorithms

Tree edges are edges in the search tree (or forest) constructed (implicitly or explicitly) by running a graph search algorithm over a graph. An edge (u,v) is a tree edge if v was first discovered while exploring (corresponding to the visitor explore() method) edge (u,v). Back edges connect vertices to their ancestors in a search tree. So for edge (u,v) the vertex v must be the ancestor of vertex u. Self loops are considered to be back edges. Forward edges are non-tree edges (u,v) that connect a vertex u to a descendant v in a search tree. Cross edges are edges that do not fall into the above three categories.

Breadth-First Search

Breadth-first search is a traversal through a graph that touches all of the vertices reachable from a particular source vertex. In addition, the order of the traversal is such that the algorithm will explore all of the neighbors of a vertex before proceeding on to the neighbors of its neighbors. One way to think of breadth-first search is that it expands like a wave emanating from a stone dropped into a pool of water. Vertices in the same ``wave'' are the same distance from the source vertex. A vertex is discovered the first time it is encountered by the algorithm. A vertex is finished after all of its neighbors are explored. Here's an example to help make this clear. A graph is shown in Figure 6 and the BFS discovery and finish order for the vertices is shown below.

Figure 6: Breadth-first search spreading through a graph.

  order of discovery: s r w v t x u y
  order of finish: s r w v t x u y

We start at vertex , and first visit r and w (the two neighbors of ). Once both neighbors of are visited, we visit the neighbor of r (vertex v), then the neighbors of w (the discovery order between r and w does not matter) which are t and x. Finally we visit the neighbors of t and x, which are u and y.

For the algorithm to keep track of where it is in the graph, and which vertex to visit next, BFS needs to color the vertices (see the section on Property Maps for more details about attaching properties to graphs). The place to put the color can either be inside the graph, or it can be passed into the algorithm as an argument.

Depth-First Search

A depth first search (DFS) visits all the vertices in a graph. When choosing which edge to explore next, this algorithm always chooses to go "deeper'' into the graph. That is, it will pick the next adjacent unvisited vertex until reaching a vertex that has no unvisited adjacent vertices. The algorithm will then backtrack to the previous vertex and continue along any as-yet unexplored edges from that vertex. After DFS has visited all the reachable vertices from a particular source vertex, it chooses one of the remaining undiscovered vertices and continues the search. This process creates a set of depth-first trees which together form the depth-first forest. A depth-first search categorizes the edges in the graph into three categories: tree-edges, back-edges, and forward or cross-edges (it does not specify which). There are typically many valid depth-first forests for a given graph, and therefore many different (and equally valid) ways to categorize the edges.

One interesting property of depth-first search is that the discover and finish times for each vertex form a parenthesis structure. If we use an open-parenthesis when a vertex is discovered, and a close-parenthesis when a vertex is finished, then the result is a properly nested set of parenthesis. Figure 7 shows DFS applied to an undirected graph, with the edges labeled in the order they were explored. Below we list the vertices of the graph ordered by discover and finish time, as well as show the parenthesis structure. DFS is used as the kernel for several other graph algorithms, including topological sort and two of the connected component algorithms. It can also be used to detect cycles (see the Cylic Dependencies section of the File Dependency Example).

Figure 7: Depth-first search on an undirected graph.

  order of discovery: a b e d c f g h i
  order of finish: d f c e b a
  parenthesis: (a (b (e (d d) (c (f f) c) e) b) a) (g (h (i i) h) g)

Minimum Spanning Tree Problem

The minimum-spanning-tree problem is defined as follows: find an acyclic subset T of E that connects all of the vertices in the graph and whose total weight is minimized, where the total weight is given by

w(T) = sum of w(u,v) over all (u,v) in T, where w(u,v) is the weight on the edge (u,v)

T is called the spanning tree.

Shortest-Paths Algorithms

One of the classic problems in graph theory is to find the shortest path between two vertices in a graph. Formally, a path is a sequence of vertices <v0,v1,...,vk> in a graph G = (V, E) such that each vertex is connected to the next vertex in the sequence (the edges (vi,vi+1) for i=0,1,...,k-1 are in the edge set E). In the shortest-path problem, each edge is given a real-valued weight. We can therefore talk about the weight of a path.

w(p) = sum from i=1..k of w(vi-1,vi)

The shortest path weight from vertex u to v is then

delta (u,v) = min { w(p) : u --> v } if there is a path from u to v
delta (u,v) = infinity otherwise.

A shortest path is any path who's path weight is equal to the shortest path weight.

There are several variants of the shortest path problem. Above we defined the single-pair problem, but there is also the single-source problem (all shortest paths from one vertex to every other vertex in the graph), the equivalent single-destination problem, and the all-pairs problem. It turns out that there are no algorithms for solving the single-pair problem that are asymptotically faster than algorithms that solve the single-source problem.

A shortest-paths tree rooted at vertex in graph G=(V,E) is a directed subgraph where V' is a subset of V and E' is a subset of E, V' is the set of vertices reachable from , G' forms a rooted tree with root , and for all v in V' the unique simple path from to v in G' is a shortest path from to v in . The result of a single-source algorithm is a shortest-paths tree.

Network Flow Algorithms

A flow network is a directed graph G=(V,E) with a source vertex s and a sink vertex t. Each edge has a positive real valued capacity function c and there is a flow function f defined over every vertex pair. The flow function must satisfy three contraints:

f(u,v) <= c(u,v) for all (u,v) in V x V (Capacity constraint)
f(u,v) = - f(v,u) for all (u,v) in V x V (Skew symmetry)
sumv in V f(u,v) = 0 for all u in V - {s,t} (Flow conservation)

The flow of the network is the net flow entering the sink vertex t (which is equal to the net flow leaving the source vertex s).

|f| = sumu in V f(u,t) = sumv in V f(s,v)

The residual capacity of an edge is r(u,v) = c(u,v) - f(u,v). The edges with r(u,v) > 0 are residual edges Ef which induce the residual graph Gf = (V, Ef). An edge with r(u,v) = 0 is saturated.

The maximum flow problem is to determine the maximum possible value for |f| and the corresponding flow values for every vertex pair in the graph.

A flow network is shown in Figure 8. Vertex A is the source vertex and H is the target vertex.

Figure 8: A Maximum Flow Network.
Edges are labeled with the flow and capacity values.

There is a long history of algorithms for solving the maximum flow problem, with the first algorithm due to Ford and Fulkerson. The best general purpose algorithm to date is the push-relabel algorithm of Goldberg which is based on the notion of a preflow introduced by Karzanov.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Boost Graph Library: Graph Traits

graph_traits<Graph>
graph_traits<Graph>

Just like the iterators of STL, graphs have associated types. As stated in the various graph concepts, a graph has quite a few associated types: vertex_descriptor, edge_descriptor, out_edge_iterator, etc.. Any particular graph concepts will not require that all of the following associated types be defined. When implementing a graph class that fullfils one or more graph concepts, for associated types that are not required by the concepts, it is ok to use void as the type (when using nested typedefs inside the graph class), or to leave the typedef out of the graph_traits specialization for the graph class.
  template <typename Graph>
  struct graph_traits {
    typedef typename Graph::vertex_descriptor      vertex_descriptor;
    typedef typename Graph::edge_descriptor        edge_descriptor;
    typedef typename Graph::adjacency_iterator     adjacency_iterator;
    typedef typename Graph::out_edge_iterator      out_edge_iterator;
    typedef typename Graph::in_edge_iterator       in_edge_iterator;
    typedef typename Graph::vertex_iterator        vertex_iterator;
    typedef typename Graph::edge_iterator          edge_iterator;

    typedef typename Graph::directed_category      directed_category;
    typedef typename Graph::edge_parallel_category edge_parallel_category;
    typedef typename Graph::traversal_category     traversal_category;

    typedef typename Graph::vertices_size_type     vertices_size_type;
    typedef typename Graph::edges_size_type        edges_size_type;
    typedef typename Graph::degree_size_type       degree_size_type;
  };

Where Defined

boost/graph/graph_traits.hpp

Template Parameters

Parameter | Description
Graph | The graph type whose associated types are being accessed.

Model of

DefaultConstructible and Assignable

Type Requirements

  • Graph is a model of one of the graph concepts.

Members

Member | Description
vertex_descriptor | The type for the objects used to identity vertices in the graph.
edge_descriptor | The type for the objects used to identity edges in the graph.
adjacency_iterator | The type for the iterators that traverse the vertices adjacent to a vertex.
out_edge_iterator | The type for the iterators that traverse through the out-edges of a vertex.
in_edge_iterator | The type for the iterators that traverse through the in-edges of a vertex.
vertex_iterator | The type for the iterators that traverse through the complete vertex set of the graph.
edge_iterator | The type for the iterators that traverse through the complete edge set of the graph.
directed_category | This says whether the graph is undirected (undirected_tag) or directed (directed_tag).
edge_parallel_category | This says whether the graph allows parallel edges to be inserted (allow_parallel_edge_tag) or if it automatically removes parallel edges (disallow_parallel_edge_tag).
traversal_category | The ways in which the vertices in the graph can be traversed. The traversal category tags are: incidence_graph_tag, adjacency_graph_tag, bidirectional_graph_tag, vertex_list_graph_tag, edge_list_graph_tag, vertex_and_edge_list_graph_tag, adjacency_matrix_tag. The user can also create a own tag which should inherit from one of the above.
vertices_size_type | The unsigned integer type used for representing the number of vertices in the graph.
edge_size_type | The unsigned integer type used for representing the number of edge in the graph.
degree_size_type | The unsigned integer type used for representing the degree of vertices in the graph.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Gürsoy-Atun Layout

gursoy_atun_layout

gursoy_atun_layout

Synopsis

// Non-named parameter version
template<typename VertexListAndIncidenceGraph,  typename Topology,
     typename PositionMap, typename VertexIndexMap,
         typename EdgeWeightMap>
void
gursoy_atun_layout(const VertexListAndIncidenceGraph& g,
                   const Topology& space,
           PositionMap position,
           int nsteps = num_vertices(g),
           double diameter_initial = sqrt((double)num_vertices(g)),
           double diameter_final = 1,
           double learning_constant_initial = 0.8,
           double learning_constant_final = 0.2,
           VertexIndexMap vertex_index_map = get(vertex_index, g),
                   EdgeWeightMap weight = dummy_property_map());

// Named parameter version
template<typename VertexListAndIncidenceGraph, typename Topology,
         typename PositionMap, typename P, typename T, typename R>
void
gursoy_atun_layout(const VertexListAndIncidenceGraph& g,
                   const Topology& space,
                   PositionMap position,
                   const bgl_named_params<P,T,R>& params = all defaults);

// Topologies
template<std::size_t Dims> class convex_topology;
template<std::size_t Dims, typename RandomNumberGenerator = minstd_rand> class hypercube_topology;
template<typename RandomNumberGenerator = minstd_rand> class square_topology;
template<typename RandomNumberGenerator = minstd_rand> class cube_topology;
template<std::size_t Dims, typename RandomNumberGenerator = minstd_rand> class ball_topology;
template<typename RandomNumberGenerator = minstd_rand> class circle_topology;
template<typename RandomNumberGenerator = minstd_rand> class sphere_topology;
template<typename RandomNumberGenerator = minstd_rand> class heart_topology;

Description

This algorithm [60] performs layout of directed graphs, either weighted or unweighted. This algorithm is very different from the Kamada-Kawai and Fruchterman-Reingold algorithms, because it does not explicitly strive to layout graphs in a visually pleasing manner. Instead, it attempts to distribute the vertices uniformly within a topology (e.g., rectangle, sphere, heart shape), keeping vertices close to their neighbors. The algorithm itself is based on Self-Organizing Maps.

Various topologies are provided that produce different, interesting results. The square topology can be used for normal display of graphs or distributing vertices for parallel computation on a process array, for instance. Other topologies, such as the sphere topology (or N-dimensional ball topology) make sense for different problems, whereas the heart topology is just plain fun. One can also define a topology to suit other particular needs.

Where Defined

boost/graph/gursoy_atun_layout.hpp

Parameters

IN: const Graph& g
The graph object on which the algorithm will be applied. The type Graph must be a model of Vertex List Graph and Incidence Graph.
IN: const Topology& space
The topology on which the graph will be layed out. The type must model the Topology concept.
OUT: PositionMap position
The property map that stores the position of each vertex. The type PositionMap must be a model of Lvalue Property Map such that the vertex descriptor type of Graph is convertible to its key type. Its value type must be the type of a point in the topology.
IN: int nsteps
The number of iterations to perform.
Default: num_vertices(g)
IN: double diameter_initial
When a vertex is selected to be updated, all vertices that are reachable from that vertex within a certain diameter (in graph terms) will also be updated. This diameter begins at diameter_initial in the first iteration and ends at diameter_final in the last iteration, progressing exponentially. Generally the diameter decreases, in a manner similar to the cooling schedule in Fruchterman-Reingold. The diameter should typically decrease in later iterations, so this value should not be less than diameter_final.
Default: sqrt((double)num_vertices(g))
IN: double diameter_final
The final value of the diameter.
Default: 1.0
IN: double learning_constant_initial
The learning rate affects how far vertices can moved to rearrange themselves in a given iteration. The learning rate progresses linearly from the initial value to the final value, both of which should be between 0 and 1. The learning rate should typically decrease, so the initial value should not exceed the final value.
Default: 0.8
IN: double learning_constant_final
The final learning rate constant.
 Default: 0.2
IN: VertexIndexMap vertex_index_map
This maps each vertex to an integer in the range [0, num_vertices(g)). The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note: If this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
IN: EdgeWeightMap weight
This maps each edge to an weight. num_vertices(g)). This is only necessary when no displacement map is provided. The type EdgeWeightMap must be a model of Readable Property Map. The value type of the map must be an floating-point type compatible with double. The edge descriptor type of the graph needs to be usable as the key type of the map. When this map is a dummy_property_map, the algorithm assumes the graph is unweighted.
Default: dummy_property_map()

Named Parameters

IN: iterations(int n)
Executes the algorithm for n iterations.
 Default: num_vertices(g)
IN: diameter_range(std::pair range)
Range specifying the parameters (diameter_initial, diameter_final).
Default: std::make_pair(sqrt((double)num_vertices(g)), 1.0)
IN: learning_constant_range(std::pair range)
Range specifying the parameters (learning_constant_initial, learning_constant_final).
Default: std::make_pair(0.8, 0.2)
IN: edge_weight(EdgeWeightMap weight)
Equivalent to the non-named weight parameter.
Default: dummy_property_map()
IN: vertex_index_map(VertexIndexMap i_map)
Equivalent to the non-named vertex_index_map parameter.
Default: get(vertex_index, g) Note: If this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.

Topologies

 A topology is a description of a space on which layout can be performed. Some common two, three, and multidimensional topologies are provided, or the user may create the own so long as it meets the requirements of the Topology concept.

Topology Concept

 Let Topology be a model of the Topology concept and let space be an object of type Topology. p1 and p2 are objects of associated type point_type (see below). The following expressions must be valid:
Expression | Type | Description
Topology::point_type | type | The type of points in the space.
space.random_point() | point_type | Returns a random point (usually uniformly distributed) within the space.
space.distance(p1, p2) | double | Get a quantity representing the distance between p1 and p2 using a path going completely inside the space. This only needs to have the same < relation as actual distances, and does not need to satisfy the other properties of a norm in a Banach space.
space.move_position_toward(p1, fraction, p2) | point_type | Returns a point that is a fraction of the way from p1 to p2, moving along a "line" in the space according to the distance measure. fraction is a double between 0 and 1, inclusive.

Class template convex_topology

Class template convex_topology implements the basic distance and point movement functions for any convex topology in Dims dimensions. It is not itself a topology, but is intended as a base class that any convex topology can derive from. The derived topology need only provide a suitable random_point function that returns a random point within the space.

template<std::size_t Dims>
class convex_topology
{
  struct point
  {
    point() { }
    double& operator[](std::size_t i) {return values[i];}
    const double& operator[](std::size_t i) const {return values[i];}

  private:
    double values[Dims];
  };

 public:
  typedef point point_type;

  double distance(point a, point b) const;
  point move_position_toward(point a, double fraction, point b) const;
};

Class template hypercube_topology

Class template hypercube_topology implements a Dims-dimensional hypercube. It is a convex topology whose points are drawn from a random number generator of type RandomNumberGenerator. The hypercube_topology can be constructed with a given random number generator; if omitted, a new, default-constructed random number generator will be used. The resulting layout will be contained within the hypercube, whose sides measure 2*scaling long (points will fall in the range [-scaling, scaling] in each dimension).

template<std::size_t Dims, typename RandomNumberGenerator = minstd_rand>
class hypercube_topology : public convex_topology<Dims>
{
public:
  explicit hypercube_topology(double scaling = 1.0);
  hypercube_topology(RandomNumberGenerator& gen, double scaling = 1.0);
  point_type random_point() const;
};

Class template square_topology

Class template square_topology is a two-dimensional hypercube topology.

template<typename RandomNumberGenerator = minstd_rand>
class square_topology : public hypercube_topology<2, RandomNumberGenerator>
{
 public:
  explicit square_topology(double scaling = 1.0);
  square_topology(RandomNumberGenerator& gen, double scaling = 1.0);
};

Class template cube_topology

Class template cube_topology is a two-dimensional hypercube topology.

template<typename RandomNumberGenerator = minstd_rand>
class cube_topology : public hypercube_topology<3, RandomNumberGenerator>
{
 public:
  explicit cube_topology(double scaling = 1.0);
  cube_topology(RandomNumberGenerator& gen, double scaling = 1.0);
};

Class template ball_topology

Class template ball_topology implements a Dims-dimensional ball. It is a convex topology whose points are drawn from a random number generator of type RandomNumberGenerator but reside inside the ball. The ball_topology can be constructed with a given random number generator; if omitted, a new, default-constructed random number generator will be used. The resulting layout will be contained within the ball with the given radius.

template<std::size_t Dims, typename RandomNumberGenerator = minstd_rand>
class ball_topology : public convex_topology<Dims>
{
public:
  explicit ball_topology(double radius = 1.0);
  ball_topology(RandomNumberGenerator& gen, double radius = 1.0);
  point_type random_point() const;
};

Class template circle_topology

Class template circle_topology is a two-dimensional ball topology.

template<typename RandomNumberGenerator = minstd_rand>
class circle_topology : public ball_topology<2, RandomNumberGenerator>
{
 public:
  explicit circle_topology(double radius = 1.0);
  circle_topology(RandomNumberGenerator& gen, double radius = 1.0);
};

Class template sphere_topology

Class template sphere_topology is a two-dimensional ball topology.

template<typename RandomNumberGenerator = minstd_rand>
class sphere_topology : public ball_topology<3, RandomNumberGenerator>
{
 public:
  explicit sphere_topology(double radius = 1.0);
  sphere_topology(RandomNumberGenerator& gen, double radius = 1.0);
};

Class template heart_topology

Class template/font> heart_topology is topology in the shape of a heart. It serves as an example of a non-convex, nontrivial topology for layout.

template<typename RandomNumberGenerator = minstd_rand>
class heart_topology
{
 public:
  typedef unspecified point_type;

  heart_topology();
  heart_topology(RandomNumberGenerator& gen);
  point_type random_point() const;
  double distance(point_type a, point_type b) const;
  point_type move_position_toward(point_type a, double fraction, point_type b) const;
};
Copyright © 2004
Jeremiah Willcock, Indiana University ()
Doug Gregor, Indiana University ()
Andrew Lumsdaine, Indiana University () 		Top


Boost Graph Library: History

History of the Boost Graph Library

The Boost Graph Library began its life as the Generic Graph Component Library (GGCL), a software project at the Lab for Scientific Computing (LSC) at the University of Notre Dame, under the direction of Professor Andrew Lumsdaine. The Lab's research directions include numerical linear algebra, parallel computing, and software engineering (including generic programming).

Soon after the Standard Template Library was released, work began at the LSC to apply generic programming to scientific computing. The Matrix Template Library (Jeremy Siek's masters thesis) was one of the first projects. Many of the lessons learned during construction of the MTL were applied to the design and implementation of the GGCL.

Graph algorithms play an important role in sparse matrix computations, so the LSC had a need for a good graph library. However, none of the available graph libraries (LEDA, GTL, Stanford GraphBase) were written using the generic programming style of the STL, and hence did not fulfill the flexibility and high-performance requirements of the LSC. Others were also expressing interest in a generic C++ graph library. During a meeting with Bjarne Stroustrup we were introduced to several people at AT&T who needed such a library. There had also been earlier work in the area of generic graph algorithms, including some codes written by Alexander Stepanov, and Dietmar Kühl's masters thesis.

With this in mind, and motivated by homework assignments in his algorithms class, Jeremy began prototyping an interface and some graph classes in the spring on 1998. Lie-Quan Lee then developed the first version of GGCL, which became his masters thesis project.

The following year, Jeremy went to work for SGI with Alexander Stepanov and Matt Austern. During this time Alex's disjoint-sets based connected components algorithm was added to GGCL, and Jeremy began working on the concept documentation for GGCL similar to Matt's STL documentation.

While working at SGI, Jeremy heard about Boost and was excited to find a group of people interested in creating high-quality C++ libraries. At boost there were several people interested in generic graph algorithms, most notably Dietmar Kühl. Some discussions about generic interfaces for graph structures resulted in the a revision of GGCL which closely resembles the current Boost Graph Library interface.

On September 4, 2000 GGCL passed the Boost formal review and became the Boost Graph Library (BGL). The first release of BGL was September 27, 2000.

Changes by version

  • Version 1.34.1
    Bug Fixes
    • bellman_ford_shortest_paths: fixed a bug where certain negative cycles were not correctly detected.
  • Version 1.34.0
    New algorithms and components

    • edmonds_maximum_cardinality_matching, from Aaron Windsor.

    • lengauer_tarjan_dominator_tree, from JongSoo Park.

    • compressed_sparse_row_graph, from Jeremiah Willcock and Douglas Gregor of Indiana University.

    • sorted_erdos_renyi_iterator, from Jeremiah Willcock of Indiana University.

    Enhancements

    • Note: the name of the compiled library for GraphViz reading is now called boost_graph rather than bgl-viz.

    • biconnected_components now has a visitor parameter and supports named parameters, from Janusz Piwowarski.

    • adjacency_matrix now models the Bidirectional Graph concept.

    • adjacency_list is now Serializable, from Jeremy Siek of Rice University.

    • Added edges_size_type and vertices_size_type to adjacency_list_traits, from Jeremy Siek of Rice University.

    • Added operator< , etc. to the edge descriptor of adjacency_list, from Jeremy Siek of Rice University.

    Bug Fixes
    • Fixed a bug that causes the relaxed heap to fail on x86 Linux.
    • Bundled properties now work with adjacency list I/O.

    • floyd_warshall_all_pairs_shortest_paths now properly uses its compare, inf, and zero parameters.

    • johnson_all_pairs_shortest_paths now supports compare, combine, inf, and zero.

    • Fixed a bug in smallest_last_vertex_ordering.hpp which could cause a vertex to be moved to the wrong bucket during an BucketSorter update.

  • Version 1.33.1
    Bug Fixes

    • fruchterman_reingold_force_directed_layout: Fixed enumeration of grid-force pairs, which caused some odd graph formations along grid lines.

    • king_ordering and cuthill_mckee_ordering: Fixed bug that caused failures with the multi-component version of these algorithms.

  • Version 1.33.0
    New algorithms and components

    • Experimental Python bindings, from Doug Gregor and Indiana University.

    • floyd_warshall_all_pairs_shortest_paths, from Lauren Foutz and Scott Hill.

    • astar_search, from Kristopher Beevers and Jufeng Peng.

    • fruchterman_reingold_force_directed_layout, from Doug Gregor and Indiana University.

    • biconnected_components and articulation_points, from Indiana University.

    • gursoy_atun_layout, from Jeremiah Willcock and Doug Gregor of Indiana University.

    • king_ordering, from D. Kevin McGrath of Indiana University.

    • erdos_renyi_iterator

    • plod_iterator

    • small_world_iterator

    Enhancements

    • bellman_ford_shortest_paths now permits one to specify the starting vertex, so that it will perform its own initialization.

    • undirected_dfs is now data-recursive, resulting in improved performance in some cases, from Synge Todo.

    • dijkstra_shortest_paths now uses a relaxed heap [61] as its priority queue, improving its complexity to O(V log V) and improving real-world performance for larger graphs.

    • read_graphviz now has a new, Spirit-based parser that works for all graph types and supports arbitrary properties on the graph, from Ron Garcia. The old, Bison-based GraphViz reader has been deprecated and will be removed in a future Boost release.

    • write_graphviz now supports output of dynamic properties (as read in through the new read_graphviz).

    • cuthill_mckee_ordering has been recast as an invocation of breadth_first_search and now supports graphs with multiple components.

    • subgraph now supports bundled properties. get_property now refers to the subgraph property, not the root graph's property.

    • filtered_graph now supports bundled properties.

    • reverse_graph now supports bundlecon_graph_d properties, set_property, and get_property.

    Bug fixes

    • bellman_ford_shortest_paths now deals with unreachable vertices better.

    • adjacency_list: parallel edge removal with OutEdgeListS = listS has been fixed. Copying and swapping has been fixed.

    • Incremental connected components : fixed a bug in the incremental_components routine that may have caused incorrect results.

    • The remove_out_edge_if function for an undirected adjacency_list has been rewritten and should no longer dereference singular iterators.

    • write_graphviz now accepts a vertex_id parameter that is used to name the nodes.

    • read_graphviz now accepts empty attribute lists.

    • sequential_vertex_coloring has been updated, tested, and documented.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Identity Property Map

identity_property_map

This property map applies the identity function, that is, it just returns a copy of the key object that was input.

Where Defined

boost/property_map.hpp

Model of

Readable Property Map

Associated Types

Type | Description
boost::property_traits<identity_property_map>::value_type | This type is std::size_t; the map functions are templated.
boost::property_traits<identity_property_map>::key_type | This type is std::size_t; the map functions are templated.
boost::property_traits<identity_property_map>::category | This type is boost::readable_op_bracket_tag.

Member Functionss

Member | Description
identity_property_map() | Default constructor.
identity_property_map(const identity_property_map& x) | Copy constructor.
template <class T>
T operator[](T x) const 		Returns a copy of object x.

Non-Member Functions

Member Description
template <class T>
T get(const identity_property_map& pmap, T x) 		Returns a copy of object x.

Copyright © 2000
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)
Lie-Quan Lee, Univ.of Notre Dame (llee1@lsc.nd.edu)
Andrew Lumsdaine, Univ.of Notre Dame (lums@lsc.nd.edu)


IncidenceGraph

The IncidenceGraph concept provides an interface for efficient access to the out-edges of each vertex in the graph.

Refinement of

Graph

Notation

G | A type that is a model of IncidenceGraph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
u, v | Are objects of type boost::graph_traits<G>::vertex_descriptor.

Associated Types

boost::graph_traits<G>::traversal_category

This tag type must be convertible to incidence_graph_tag.
boost::graph_traits<G>::out_edge_iterator

An out-edge iterator for a vertex v provides access to the out-edges of the vertex. As such, the value type of an out-edge iterator is the edge descriptor type of its graph. An out-edge iterator must meet the requirements of MultiPassInputIterator.

boost::graph_traits<G>::degree_size_type

The unsigned intergral type used for representing the number out-edges or incident edges of a vertex.

Valid Expressions

source(e, g) | Returns the vertex descriptor for u of the edge (u,v) represented by e. Return type: vertex_descriptor
target(e, g) | Returns the vertex descriptor for v of the edge (u,v) represented by e. Return type: vertex_descriptor
out_edges(u, g) | Returns an iterator-range providing access to the out-edges (for directed graphs) or incident edges (for undirected graphs) of vertex u in graph g. The source vertex of an edge obtained via an out edge iterator is guaranteed (for both directed and undirected graphs) to be the vertex u used in the call to out_edges(u, g) and the target vertex must the a vertex adjacent to u.[1] Return type: std::pair<out_edge_iterator, out_edge_iterator>
out_degree(u, g) | Returns the number of out-edges (for directed graphs) or the number of incident edges (for undirected graphs) of vertex u in graph g. Return type: degree_size_type

Complexity guarantees

The source(), target(), and out_edges() functions must all be constant time. The out_degree() function must be linear in the number of out-edges.

See Also

Graph concepts

Notes

[1] For undirected graphs, the edge (u,v) is the same as edge (v,u), so without some extra guarantee an implementation would be free use any ordering for the pair of vertices in an out-edge. For example, out_edges(u, g) is called, and v is one of the vertices adjacent to u, then the implementation would be free to return (v,u) as an out-edge which would be non-intuitive and cause trouble for algorithms. Therefore, the extra requirement is added that the out-edge connecting u and v must be given as (u,v) and not (v,u).

Concept Checking Class

  template <class G>
  struct IncidenceGraphConcept
  {
    typedef typename boost::graph_traits<G>::out_edge_iterator out_edge_iterator;
    void constraints() {
      function_requires< GraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<out_edge_iterator> >();

      p = out_edges(u, g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
    }
    void const_constraints(const G& g) {
      p = out_edges(u, g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
    }
    std::pair<out_edge_iterator, out_edge_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::edge_descriptor e;
    G g;
   };
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Name

Incidence Graph : Provides an interface for efficient access to the out-edges of each vertex in the graph.

Synopsis

template <class G>
  struct IncidenceGraphConcept
  {
    typedef typename boost::graph_traits<G>::out_edge_iterator out_edge_iterator;
    void constraints() {
      function_requires< GraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<out_edge_iterator> >();

      p = out_edges(u, g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
    }
    void const_constraints(const G& g) {
      p = out_edges(u, g);
      e = *p.first;
      u = source(e, g);
      v = target(e, g);
    }
    std::pair<out_edge_iterator, out_edge_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::edge_descriptor e;
    G g;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia


Boost Graph Library: incident

incident

Complexity: | O(1)
Where Defined: | boost/graph/graph_utility.hpp

  template <class Graph>
  std::pair<typename graph_traits<Graph>::vertex_descriptor,
            typename graph_traits<Graph>::vertex_descriptor>
  incident(typename graph_traits<Graph>::edge_descriptor e, Graph& g)
This function takes and edge descriptor and returns the pair of vertices that are incident to the edge. For directed graphs, the first vertex is the source and the second vertex is the target. This function is equivalent to the expression std::make_pair(source(e, g), target(e, g)).

Example

  edge_descriptor e;
  vertex_descriptor u, v;
  ...
  tie(u, v) = incident(e, g);
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Incremental Connected Components

Incremental Connected Components

This section describes a family of functions and classes that work together to calculate the connected components of an undirected graph. The algorithm used here is based on the disjoint-sets (fast union-find) data structure [8,27] which is a good method to use for situations where the graph is growing (edges are being added) and the connected components information needs to be updated repeatedly. This method does not cover the situation where edges are both added and removed from the graph, hence it is called incremental [42] (and not fully dynamic). The disjoint-sets class is described in Section Disjoint Sets.

The following five operations are the primary functions that is used to calculate and maintain the connected components. The objects used here are a graph g, a disjoint-sets structure ds, and vertices u and v.

  • initialize_incremental_components(g, ds)
    Basic initialization of the disjoint-sets structure. Each vertex in the graph g is in its own set.
  • incremental_components(g, ds)
    The connected components are calculated based on the edges in the graph g and the information is embedded in ds.
  • ds.find_set(v)
    Extracts the component information for vertex v from the disjoint-sets.
  • ds.union_set(u, v)
    Update the disjoint-sets structure when edge (u,v) is added to the graph.

Complexity

The time complexity for the whole process is O(V + E alpha(E,V)) where E is the total number of edges in the graph (by the end of the process) and V is the number of vertices. alpha is the inverse of Ackermann's function which has explosive recursively exponential growth. Therefore its inverse function grows very slowly. For all practical purposes alpha(m,n) <= 4 which means the time complexity is only slightly larger than O(V + E).

Example

Maintain the connected components of a graph while adding edges using the disjoint-sets data structure. The full source code for this example can be found in examples/incremental_components.cpp.

typedef adjacency_list <vecS, vecS, undirectedS> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::vertices_size_type size_type;

const int N = 6;
Graph G(N);

std::vector<size_type> rank(num_vertices(G));
std::vector<Vertex> parent(num_vertices(G));
typedef size_type* Rank;
typedef Vertex* Parent;
disjoint_sets<Rank, Parent>  ds(&rank[0], &parent[0]);

initialize_incremental_components(G, ds);
incremental_components(G, ds);

graph_traits<Graph>::edge_descriptor e;
bool flag;
boost::tie(e,flag) = add_edge(0, 1, G);
ds.union_set(0,1);

boost::tie(e,flag) = add_edge(1, 4, G);
ds.union_set(1,4);

boost::tie(e,flag) = add_edge(4, 0, G);
ds.union_set(4,0);

boost::tie(e,flag) = add_edge(2, 5, G);
ds.union_set(2,5);

cout << "An undirected graph:" << endl;
print_graph(G, get(vertex_index, G));
cout << endl;

graph_traits<Graph>::vertex_iterator i,end;
for (boost::tie(i, end) = vertices(G); i != end; ++i)
  cout << "representative[" << *i << "] = " <<
    ds.find_set(*i) << endl;;
cout << endl;

typedef component_index<unsigned int> Components;
Components components(&parent[0], &parent[0] + parent.size());

for (Components::size_type c = 0; c < components.size(); ++c) {
  cout << "component " << c << " contains: ";
  Components::value_type::iterator
    j = components[c].begin(),
    jend = components[c].end();
  for ( ; j != jend; ++j)
    cout << *j << " ";
  cout << endl;
}

initialize_incremental_components

Graphs: | undirected
Properties: | rank, parent (in disjoint-sets)
Complexity: |

template <class VertexListGraph, class DisjointSets>
void initialize_incremental_components(VertexListGraph& G, DisjointSets& ds)

This prepares the disjoint-sets data structure for the incremental connected components algorithm by making each vertex in the graph a member of its own component (or set).

Where Defined

boost/graph/incremental_components.hpp

incremental_components

Graphs: | undirected
Properties: | rank, parent (in disjoint-sets)
Complexity: | O(E)

template <class EdgeListGraph, class DisjointSets>
void incremental_components(EdgeListGraph& g, DisjointSets& ds)

This function calculates the connected components of the graph, embedding the results in the disjoint-sets data structure.

Where Defined

boost/graph/incremental_components.hpp

Requirements on Types

  • The graph type must be a model of EdgeListGraph.

same_component

Properties: | rank, parent (in disjoint-sets)
Complexity: | O(alpha(E,V))

template <class Vertex, class DisjointSet>
bool same_component(Vertex u, Vertex v, DisjointSet& ds)

This function determines whether/font> uu and v are in the same component.

Where Defined

boost/graph/incremental_components.hpp

Requirements on Types

  • Vertex must be compatible with the rank and parent property maps of the DisjointSets data structure.

component_index

component_index<Index>

The is a class that provides an STL container-like view for the components of the graph. Each component is a container-like object, and the component_index object provides access to the component objects via operator[]. Acomponent_index object is initialized with the parents property in the disjoint-sets calculated from the incremental_components() function.

Where Defined

boost/graph/incremental_components.hpp

Members

Memberr | Description
size_type | The type used for representing the number of components.
value_type | The type for a component object. The component type has the following members.
value_type::value_type | The value type of a component object is a vertex ID.
value_type::iterator | This iterator can be used to traverse all of the vertices in the component. This iterator dereferences to give a vertex ID.
value_type::const_iterator | The const iterator.
value_type::iterator value_type::begin() const | Return an iterator pointing to the first vertex in the component.
value_type::iterator value_type::end() const | Return an iterator pointing past the end of the last vertex in the component.
template <class ComponentsContainer> component_index(const ComponentsContainer& c) | Construct the component_index using the information from the components container c which was the result of executing connected_components_on_edgelist.
value_type operator[](size_type i) | Returns the ith component in the graph.
size_type component_index::size() | Returns the number of components in the graph.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


The Boost Graph Library

The Boost Graph Library (BGL)

Graphs are mathematical abstractions that are useful for solving many types of problems in computer science. Consequently, these abstractions must be represented in computer programs. A standardized generic interface for traversing graphs is of utmost importance to encourage reuse of graph algorithms and data structures. Part of the Boost Graph Library is a generic interface that allows access to a graph's structure, but hides the details of the implementation. This is an ``open'' interface in the sense that any graph library that implements this interface will be interoperable with the BGL generic algorithms and with other algorithms that also use this interface. The BGL provides some general purpose graph classes that conform to this interface, but they are not meant to be the ``only'' graph classes; there certainly will be other graph classes that are better for certain situations. The main contribution of the BGL is the formulation of this interface.

The BGL graph interface and graph components are generic, in the same sense as the the Standard Template Library (STL) [2]. In the following sections, we review the role that generic programming plays in the STL and compare that to how we applied generic programming in the context of graphs.

Those familiar with generic programming, goto Table of Contents.

Download the source for the BGL that is available as part of the Boost distribution, from here.

How to Build the BGL

The Boost Graph Library is a header-only library and does not need to be built to be used. The only exception is the GraphViz input parser.

When compiling programs that use the BGL, be sure to compile with optimization. For instance, select "Release" mode with Microsoft Visual C++ or supply the flag -O2 or -O3 to GCC.

Genericity in STL

There are three ways in which the STL is generic.

Algorithm/Data-Structure Interoperability

First, each algorithm is written in a data-structure neutral way, allowing a single template function to operate on many different classes of containers. The concept of an iterator is the key ingredient in this decoupling of algorithms and data-structures. The impact of this technique is a reduction in the STL's code size from O(M*N) to O(M+N), where M is the number of algorithms and N is the number of containers. For example, consider a situation of 20 algorithms and 5 data-structures, this would be the difference between writing 100 functions versus only 25 functions. And the differences continues to grow faster and faster as the number of algorithms and data-structures increase.

Extension through Function Objects

Secondly, STL's algorithms and containers are extensible. The user can adapt and customize the STL through the use of function objects. This flexibility makes STL a great tool for solving real-world problems. Each programming problem brings its own set of entities and interactions that must be modeled. Function objects provide a mechanism for extending the STL to handle the specifics of each problem domain.

Element Type Parameterization

Lastly, STL's containers are parameterized on the element type. Though hugely important, this is perhaps the least ``interesting'' way in which STL is generic. Generic programming is often summarized by a brief description of parameterized lists such as std::list<T>. This hardly scratches the surface.

Genericity in the Boost Graph Library

Like the STL, there are three ways in which the BGL is generic.

Algorithm/Data-Structure Interoperability

First, the graph algorithms of the BGL are written to an interface that abstracts away the details of the particular graph data-structure. Like the STL, the BGL uses iterators to define the interface for data-structure traversal. There are three distinct graph traversal patterns: traversal of all vertices in the graph, through all of the edges, and along the adjacency structure of the graph (from a vertex to each of its neighbors). There are separate iterators for each pattern of traversal.

This generic interface allows template functions such as breadth_first_search() to work on a large variety of graph data-structures, from graphs implemented with pointer-linked nodes to graphs encoded in arrays. This flexibility is important in the domain of graphs. Graph data-structures are often custom-made for a particular application. Traditionally, if programmers want to reuse an algorithm implementation they must convert/copy their graph data into the graph library's prescribed graph structure. This is the case with libraries such as LEDA, GTL, Stanford GraphBase; it is especially true of graph algorithms written in Fortran. This severely limits the reuse of their graph algorithms.

In contrast, custom-made (or even legacy) graph structures can be used as-is with the generic graph algorithms of the BGL, using external adaptation (see Section How to Convert Existing Graphs to the BGL). External adaptation wraps a new interface around a data-structure without copying and without placing the data inside adaptor objects. The BGL interface was carefully designed to make this adaptation easy. The built interfacing code for using a variety of graph dstructures (LEDA graphs, Stanford GraphBase graphs, and even Fortran-style arrays) in BGL graph algorithms demonstrates this.

Extension through Visitors

Secondly, the graph algorithms of the BGL are extensible. The BGL introduces the notion of a visitor, which is just a function object with multiple methods. In graph algorithms, there are often several key ``event points'' at which it is useful to insert user-defined operations. The visitor object has a different method that is invoked at each event point. The particular event points and corresponding visitor methods depend on the particular algorithm. They often include methods like start_vertex(), discover_vertex(), examine_edge(), tree_edge(), and finish_vertex().

Vertex and Edge Property Multi-Parameterization

The third way that the BGL is generic is analogous to the parameterization of the element-type in STL containers, though again the story is a bit more complicated for graphs. The programmer needs to associate values (called "properties") with both the vertices and the edges of the graph. In addition, it will often be necessary to associate multiple properties with each vertex and edge; this is known as multi-parameterization. The STL std::list<T> class has a parameter T for its element type. Similarly, BGL graph classes have template parameters for vertex and edge ``properties''. A property specifies the parameterized type of the property and also assigns an identifying tag to the property. This tag is used to distinguish between the multiple properties which an edge or vertex may have. A property value that is attached to a particular vertex or edge can be obtained via a property map. There is a separate property map for each property.

Traditional graph libraries and graph structures fall down when it comes to the parameterization of graph properties. This is one of the primary reasons that graph data-structures must be custom-built for applications. The parameterization of properties in the BGL graph classes makes them well suited for re-use.

Algorithms

The BGL algorithms consist of a core set of algorithm patterns (implemented as generic algorithms) and a larger set of graph algorithms. The core algorithm patterns are

  • Breadth First Search

  • Depth First Search

  • Uniform Cost Search

By themselves, the algorithm patterns do not compute any meaningful quantities over graphs; they are merely building blocks for constructing graph algorithms. The graph algorithms in the BGL currently include

  • Dijkstra's Shortest Paths

  • Bellman-Ford Shortest Paths

  • Johnson's All-Pairs Shortest Paths

  • Kruskal's Minimum Spanning Tree

  • Prim's Minimum Spanning Tree

  • Connected Components

  • Strongly Connected Components

  • Dynamic Connected Components (using Disjoint Sets)

  • Topological Sort

  • Transpose

  • Reverse Cuthill Mckee Ordering

  • Smallest Last Vertex Ordering

  • Sequential Vertex Coloring

Data Structures

The BGL currently provides two graph classes and an edge list adaptor:

  • adjacency_list

  • adjacency_matrix

  • edge_list

The adjacency_list class is the general purpose ``swiss army knife'' of graph classes. It is highly parameterized so that it can be optimized for different situations: the graph is directed or undirected, allow or disallow parallel edges, efficient access to just the out-edges or also to the in-edges, fast vertex insertion and removal at the cost of extra space overhead, etc.

The adjacency_matrix class stores edges in a |V| x |V| matrix (where |V| is the number of vertices). The elements of this matrix represent edges in the graph. Adjacency matrix representations are especially suitable for very dense graphs, i.e., those where the number of edges approaches |V|2.

The edge_list class is an adaptor that takes any kind of edge iterator and implements an Edge List Graph.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Inverse Adjacency Iterator Adaptor Documentation

Inverse Adjacency Iterator Adaptor

Defined in header boost/graph/adjacency_iterator.hpp

The inverse adjacency iterator adaptor transforms an in_edge_iterator into an inverse adjacency iterator. That is, it takes an iterator that traverses over edges, and creates an iterator that traverses over the source vertices of those edges.

Synopsis

namespace boost {
  template <class Graph, class VertexDescriptor, class InEdgeIter>
  class inv_adjacency_iterator_generator {
  public:
    typedef iterator_adaptor<...> type;
  };
}

Example

The following is an example of how to use the inv_adjacency_iterator_generator class.

#include <boost/graph/adjacency_iterator.hpp>

class my_graph {
  // ...
  typedef ... in_edge_iterator;
  typedef ... vertex_descriptor;
  typedef boost::inv_adjacency_iterator_generator<my_graph, vertex_descriptor, in_edge_iterator>::type inv_adjacency_iterator;
  // ...
};

Template Parameters

Parameter | Description
Graph | The graph type, which must model Incidence Graph.
VertexDescriptor This must be the same type as graph_traits<Graph>::vertex_descriptor. The reason why this is a template parameter is that the primary use of inv_adjacency_iterator_generator is inside the definition of the graph class, and in that context we can not use graph_traits on the not yet fully defined graph class.
Default: graph_traits<Graph>::vertex_descriptor
InEdgeIter This must be the same type as graph_traits<Graph>::in_edge_iterator.
Default: graph_traits<Graph>::in_edge_iterator

Model of

The inverse adjacency iterator adaptor (the type inv_adjacency_iterator_generator<...>::type) is a model of Multi-Pass Input Iterator .

Members

The inverse adjacency iterator type implements the member functions and operators required of the Random Access Iterator concept, except that the reference type is the same as the value_type so operator*() returns by-value. In addition it has the following constructor:
inv_adjacency_iterator_generator::type(const InEdgeIter& it, const Graph* g)
Revised 01 Oct 2007
© Copyright Jeremy Siek 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this
copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim
as to its suitability for any purpose. 		Top


Boost Graph Library: Isomorphism

 isomorphism

// named parameter version
template <class Graph1, class Graph2, class P, class T, class R>
bool isomorphism(const Graph1& g1, const Graph2& g2,
                 const bgl_named_params<P,T,R>& params = all defaults)

// non-named parameter version
template <typename Graph1, typename Graph2,
      typename IndexMapping, typename VertexInvariant,
      typename V1Map, typename V2Map>
bool isomorphism(const Graph1& g1, const Graph2& g2,
         IsoMap f, VertexInvariant invariant,
         VertexIndex1Map i1_map, VertexIndex2Map i2_map)

An isomorphism is a 1-to-1 mapping of the vertices in one graph to the vertices of another graph such that adjacency is preserved. Another words, given graphs G1 = (V1,E1) and G2 = (V2,E2) an isomorphism is a function f such that for all pairs of vertices a,b in V1, edge (a,b) is in E1 if and only if edge (f(a),f(b)) is in E2.

This function returns true if there exists an isomorphism between graph 1 and graph 2 and false otherwise. Also, if a isomorphism map named parameter is provided then an isomorphism is recorded in the map.

The current implementation is based on descriptions of a backtracking algorithm in [46,48]. The file isomorphism-impl.pdf contains a "literate" description of the implementation. The algorithm used is simple but slow. A more efficient (and much more complex) algorithm is described in [47]. When (and if) a version of this algorithm is ported to the BGL interface it should replace the current algorithm.

Where Defined

boost/graph/isomorphism.hpp

Parameters

IN: const Graph1& g1
A directed or undirected graph. The graph type must model of Vertex List Graph and Edge List Graph.
IN: const Graph2& g2
A directed or undirected graph. The graph type must model Bidirectional Graph and Vertex List Graph.

Named Parameters

OUT: isomorphism_map(IsoMap f)
The mapping from vertices in graph 1 to vertices in graph 2. This must be a Read/Write Property Map.
Default: an iterator_property_map constructed from a std::vector of graph 2's vertex descriptor type and the vertex index map for graph 1.
Python: Must be a vertex_vertex_map for the first graph.
IN: vertex_invariant(VertexInvariant i)
A mapping i from vertices to integers such that if there is some isomorphism that maps v onto v' then i(v) == i(v'). The VertexInvariant type must be a BinaryFunction where the first argument is a vertex descriptor, the second argument is a graph, and the result type is an integer. The vertex invariant must work with the types for both graph 1 and graph 2 and therefore may need to have a templated operator().
Default: degree_vertex_invariant
Python: Any callable Python object will suffice.
IN: vertex_index1_map(VertexIndex1Map i1_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndex1Map must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of graph 1 needs to be usable as the key type of the map.
Default: get(vertex_index, g1) Note: If this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.
IN: vertex_index2_map(VertexIndex2Map i2_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndex2Map must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of graph 2 needs to be usable as the key type of the map.
Default: get(vertex_index, g2) Note: If this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The worst-case time complexity is O(|V|). s

Example

See libs/graph/example/isomorphism.cpp.
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Iterator Adaptor

Each specialization of the iterator_adaptor class template is derived from a specialization of iterator_facade. The core interface functions expected by iterator_facade are implemented in terms of the iterator_adaptor's Base template parameter. A class derived from iterator_adaptor typically redefines some of the core interface functions to adapt the behavior of the Base type. Whether the derived class models any of the standard iterator concepts depends on the operations supported by the Base type and which core interface functions of iterator_facade are redefined in the Derived class.

  • Overview

  • Reference

    • iterator_adaptor requirements

    • iterator_adaptor base class parameters

    • iterator_adaptor public operations

    • iterator_adaptor protected member functions

    • iterator_adaptor private member functions

  • Tutorial Example

Overview

The iterator_adaptor class template adapts some Base1 type to create a new iterator. Instantiations of iterator_adaptor are derived from a corresponding instantiation of iterator_facade and implement the core behaviors in terms of the Base type. In essence, iterator_adaptor merely forwards all operations to an instance of the Base type, which it stores as a member.

[1]

(1, 2) The term "Base" here does not refer to a base class and is not meant to imply the use of derivation. We have followed the lead of the standard library, which provides a base() function to access the underlying iterator object of a reverse_iterator adaptor.

The user of iterator_adaptor creates a class derived from an instantiation of iterator_adaptor and then selectively redefines some of the core member functions described in the iterator_facade core requirements table. The Base type need not meet the full requirements for an iterator; it need only support the operations used by the core interface functions of iterator_adaptor that have not been redefined in the user's derived class.

Several of the template parameters of iterator_adaptor default to use_default. This allows the user to make use of a default parameter even to specify a parameter later in the parameter list. Also, the defaults for the corresponding associated types are somewhat complicated, so metaprogramming is required to compute them, and use_defaultcan help to simplify the implementation. Finally, the identity of the use_default type is not left unspecified because specification helps to highlight that the Reference template parameter may not always be identical to the iterator's reference type, and will keep users from making mistakes based on that assumption.

Reference

template <
    class Derived
  , class Base
  , class Value               = use_default
  , class CategoryOrTraversal = use_default
  , class Reference           = use_default
  , class Difference = use_default
>
class iterator_adaptor
  : public iterator_facade<Derived, V', C', R', D'> // see details
{
    friend class iterator_core_access;
 public:
    iterator_adaptor();
    explicit iterator_adaptor(Base const& iter);
    typedef Base base_type;
    Base const& base() const;
 protected:
    typedef iterator_adaptor iterator_adaptor_;
    Base const& base_reference() const;
    Base& base_reference();
 private: // Core iterator interface for iterator_facade.
    typename iterator_adaptor::reference dereference() const;

    template <
    class OtherDerived, class OtherIterator, class V, class C, class R, class D
    >
    bool equal(iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> const& x) const;

    void advance(typename iterator_adaptor::difference_type n);
    void increment();
    void decrement();

    template <
        class OtherDerived, class OtherIterator, class V, class C, class R, class D
    >
    typename iterator_adaptor::difference_type distance_to(
        iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> const& y) const;

 private:
    Base m_iterator; // exposition only
};

iterator_adaptor requirements

static_cast<Derived*>(iterator_adaptor*) shall be well-formed. The Base argument shall be Assignable and Copy Constructible.

iterator_adaptor base class parameters

The V', C', R', and D' parameters of the iterator_facade used as a base class in the summary of iterator_adaptor above are defined as follows:

V' = if (Value is use_default)
          return iterator_traits<Base>::value_type
      else
          return Value

C' = if (CategoryOrTraversal is use_default)
          return iterator_traversal<Base>::type
      else
          return CategoryOrTraversal

R' = if (Reference is use_default)
          if (Value is use_default)
              return iterator_traits<Base>::reference
          else
              return Value&
      else
          return Reference

D' = if (Difference is use_default)
          return iterator_traits<Base>::difference_type
      else
          return Difference

iterator_adaptor public operations

iterator_adaptor();
Requires: | The Base type must be Default Constructible.
Returns: | An instance of iterator_adaptor with m_iterator default constructed.
explicit iterator_adaptor(Base const& iter);
Returns: An instance of iterator_adaptor with m_iterator copy constructed from iter.
Base const& base() const;
Returns: m_iterator

iterator_adaptor protected member functions

Base const& base_reference() const;
Returns: A const reference to m_iterator .
Base& base_reference();
Returns: A non-const reference to m_iterator .

iterator_adaptor private member functions

typename iterator_adaptor::reference dereference() const;
Returns:

*m_iterator

template <
class OtherDerived, class OtherIterator, class V, class C, class R, class D
>
bool equal(iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> const& x) const;
Returns: m_iterator == x.base()
void advance(typename iterator_adaptor::difference_type n);
Effects: m_iterator += n;
void increment();
Effects:

++m_iterator;

void decrement();
Effects:

--m_iterator;

template <
    class OtherDerived, class OtherIterator, class V, class C, class R, class D
>
typename iterator_adaptor::difference_type distance_to(
    iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> const& y) const;

Returns:

y.base() - m_iterator

Tutorial Example

This section further refines the node_iter class template developed in the iterator_facade tutorial. Refer to the iterator_facade tutorial beofre commencing this section.

node_base* really is an iterator

As node_base is an abstract class: a pointer to a node_base points at some base subobject of an instance of some other class, and incrementing a node_base* moves it past this base subobject to some unknown location. To deal with that incremented position compare another node_base* to it. In other words, the original iterator traverses a one-element array.

The node_base* object that underlies node_iterator is itself an iterator, just like all other pointers.Examine that pointer closely from an iterator perspective, notice that it has much in common with the node_iterator being built. First, they share most of the same associated types (value_type, reference, pointer, and difference_type). Second, even some of the core functionality is the same: operator* and operator== on the node_iterator return the result of invoking the same operations on the underlying pointer, via the node_iterator's dereference and equal member functions). The only real behavioral difference between node_base*and node_iterator can be observed when they are incremented: node_iterator follows the m_next pointer, while node_base* just applies an address offset.

It turns out that the pattern of building an iterator on another iterator-like type (the Base1 type) while modifying just a few aspects of the underlying type's behavior is an extremely common one, and it's the pattern addressed by iterator_adaptor. Using iterator_adaptor is very much like using iterator_facade, but because iterator_adaptor tries to mimic as much of the Base type's behavior as possible, there is no need to supply a Value argument, nor implement any core behaviors other than increment. The implementation of node_iter is thus reduced to:

template <class Value>
class node_iter
  : public boost::iterator_adaptor<
        node_iter<Value>                // Derived
      , Value*                          // Base
      , boost::use_default              // Value
      , boost::forward_traversal_tag    // CategoryOrTraversal
    >
{
 private:
    struct enabler {};  // a private type avoids misuse

 public:
    node_iter()
      : node_iter::iterator_adaptor_(0) {}

    explicit node_iter(Value* p)
      : node_iter::iterator_adaptor_(p) {}

    template <class OtherValue>
    node_iter(
        node_iter<OtherValue> const& other
      , typename boost::enable_if<
            boost::is_convertible<OtherValue*,Value*>
          , enabler
        >::type = enabler()
    )
      : node_iter::iterator_adaptor_(other.base()) {}

 private:
    friend class boost::iterator_core_access;
    void increment() { this->base_reference() = this->base()->next(); }
};

Note the use of node_iter::iterator_adaptor_ here: because iterator_adaptor defines a nested iterator_adaptor_ type that refers to itself, that gives a convenient way to refer to the complicated base class type of node_iter<Value>.
[Note: This technique is known not to work with Borland C++ 5.6.4 and Metrowerks CodeWarrior versions prior to 9.0]

The code snippet below is an example program that exercises this version of the node iterators.

// Copyright David Abrahams 2004. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "node_iterator3.hpp"
#include <string>
#include <memory>
#include <iostream>
#include <algorithm>
#include <boost/mem_fn.hpp>
#include <cassert>

int main()
{
    std::auto_ptr<node<int> > nodes(new node<int>(42));
    nodes->append(new node<std::string>(" is greater than "));
    nodes->append(new node<int>(13));

    // Check interoperability
    assert(node_iterator(nodes.get()) == node_const_iterator(nodes.get()));
    assert(node_const_iterator(nodes.get()) == node_iterator(nodes.get()));

    assert(node_iterator(nodes.get()) != node_const_iterator());
    assert(node_const_iterator(nodes.get()) != node_iterator());

    std::copy(
        node_iterator(nodes.get()), node_iterator()
      , std::ostream_iterator<node_base>(std::cout, " ")
    );
    std::cout << std::endl;

    std::for_each(
        node_iterator(nodes.get()), node_iterator()
      , boost::mem_fn(&node_base::double_me)
    );

    std::copy(
        node_const_iterator(nodes.get()), node_const_iterator()
      , std::ostream_iterator<node_base>(std::cout, "/")
    );
    std::cout << std::endl;
    return 0;

In the case of node_iter, it's not very compelling to pass boost::use_default as iterator_adaptor's Value argument; an easier method is to pass node_iter's Value along to iterator_adaptor.Most iterator class templates built with iterator_adaptor are parameterized on another iterator type, rather than on its value_type. For example, boost::reverse_iterator takes an iterator type argument and reverses its direction of traversal, since the original iterator and the reversed one have all the same associated types, iterator_adaptor's delegation of default types to its Base saves the implementor of boost::reverse_iterator from writing:

std::iterator_traits<Iterator>::some-associated-type

at least four times.

Review the documentation and implementations of reverse_iterator and the other Boost specialized iterator adaptors to get an idea of iterator_adaptor. In particular, refer transform_iterator, which is perhaps the most straightforward adaptor, and also counting_iterator, which demonstrates that iterator_adaptor's Base type needn't be an iterator.

©Copyright 2003.
David Abrahams,dave@boost-consulting.com.
Jeremy Siek,jsiek@osl.iu.edu.
Thomas Witt, witt@ive.uni-hannover.de.


Iterator Facade

iterator_facade is a base class template that implements the interface of standard iterators in terms of a few core functions and associated types, to be supplied by a derived iterator class.

  • Overview

    • Usage

    • Iterator Core Access

    • operator[]

    • operator->

  • Reference

    • iterator_facade Requirements

    • iterator_facade operations

  • Tutorial Example

    • The Problem

    • A Basic Iterator Using iterator_facade

      • Template Arguments for iterator_facade

        • Derived

        • Value

        • CategoryOrTraversal

        • Reference

        • Difference

      • Constructors and Data Members

      • Implementing the Core Operations

    • A constant node_iterator

    • Interoperability

    • Telling the Truth

    • Wrap Up

Overview

While the iterator interface is rich, there is a core subset of the interface that is necessary for all the functionality. The following are the core behaviors identified for iterators:

  • dereferencing

  • incrementing

  • decrementing

  • equality comparison

  • random-access motion

  • distance measurement

In addition to the behaviors listed above, the core interface elements include the associated types exposed through iterator traits: value_type, reference, difference_type, and iterator_category.

Iterator facade uses the Curiously Recurring Template Pattern (CRTP) [Cop95] so that the user can specify the behavior of iterator_facade in a derived class. Former designs used policy objects to specify the behavior, but that approach was discarded for several reasons:

  1. the creation and eventual copying of the policy object may create overhead that can be avoided with the current approach.

  2. The policy object approach does not allow for custom constructors on the created iterator types, an essential feature if iterator_facade should be used in other library implementations.

  3. Without the use of CRTP, the standard requirement that an iterator's operator++ returns the iterator type itself would mean that all iterators built with the library would have to be specializations of iterator_facade<...>, rather than something more descriptive like indirect_iterator<T*>. Cumbersome type generator metafunctions would be needed to build new parameterized iterators, and a separate iterator_adaptor layer would be impossible.

Usage

The user of iterator_facade derives the iterator class from a specialization of iterator_facade and passes the derived iterator class as iterator_facade's first template parameter. The order of the other template parameters have been carefully chosen to take advantage of useful defaults. For example, when defining a constant lvalue iterator, the user can pass a const-qualified version of the iterator's value_type as iterator_facade's Value parameter and omit the Reference parameter which follows.

The derived iterator class must define member functions implementing the iterator's core behaviors. The following table describes expressions which are required to be valid depending on the category of the derived iterator type. These member functions are described briefly below and in more detail in the iterator facade requirements.

Expression

Effects

i.dereference()

Access the value referred to

i.equal(j)

Compare for equality with j

i.increment()

Advance by one position

i.decrement()

Retreat by one position

i.advance(n)

Advance by n positions

i.distance_to(j)

Measure the distance to j

In addition to implementing the core interface functions, an iterator derived from iterator_facade typically defines several constructors. To model any of the standard iterator concepts, the iterator must at least have a copy constructor. Also, if the iterator type X is meant to be automatically interoperate with another iterator type Y (as with constant and mutable iterators) then there must be an implicit conversion from X to Y or from Y to X (but not both), typically implemented as a conversion constructor. Finally, if the iterator is to model Forward Traversal Iterator or a more-refined iterator concept, a default constructor is required.

Iterator Core Access

iterator_facade and the operator implementations need to be able to access the core member functions in the derived class. Making the core member functions public would expose an implementation detail to the user. The design used here ensures that implementation details do not appear in the public interface of the derived iterator type.

Preventing direct access to the core member functions has two advantages. First, there is no possibility for the user to accidentally use a member function of the iterator when a member of the value_type was intended. This has been an issue with smart pointer implementations in the past. The second and main advantage is that library implementers can freely exchange a hand-rolled iterator implementation for one based on iterator_facade without fear of breaking code that was accessing the public core member functions directly.

In a naive implementation, keeping the derived class' core member functions private would require it to grant friendship to iterator_facade and each of the seven operators. In order to reduce the burden of limiting access, iterator_core_access is provided, a class that acts as a gateway to the core member functions in the derived iterator class. The author of the derived class only needs to grant friendship to iterator_core_access to make his core member functions available to the library.

iterator_core_access is typically, implemented as an empty class containing only private static member functions which invoke the iterator core member functions. There is, however, no need to standardize the gateway protocol. Note that even if iterator_core_access used public member functions it would not open a safety loophole, as every core member function preserves the invariants of the iterator.r.

operator[]

The indexing operator for a generalized iterator presents special challenges. A random access iterator's operator[] is only required to return something convertible to its value_type. Requiring that it return an lvalue would rule out currently-legal random-access iterators which hold the referenced value in a data member (e.g. counting_iterator), because *(p+n) is a reference into the temporary iterator p+n, which is destroyed when operator[] returns.

Writable iterators built with iterator_facade implement the semantics required by the preferred resolution to issue 299 and adopted by proposal n1550: the result of p[n] is an object convertible to the iterator's value_type, and p[n] = x is equivalent to *(p + n) = x (Note: This result object may be implemented as a proxy containing a copy of p+n). This approach will work properly for any random-access iterator regardless of the other details of its implementation. A user who knows more about the implementation of the iterator is free to implement an operator[] that returns an lvalue in the derived iterator class; it will hide the one supplied by iterator_facade from clients of the iterator.

operator->

The reference type of a readable iterator (and today's input iterator) need not in fact be a reference, so long as it is convertible to the iterator's value_type. When the value_typeis a class, however, it must still be possible to access members through operator->. Therefore, an iterator whose reference type is not in fact a reference must return a proxy containing a copy of the referenced value from its operator->.

The return types for iterator_facade's operator-> and operator[] are not explicitly specified. Instead, those types are described in terms of a set of requirements, which must be satisfied by the iterator_facade implementation.n.

[Cop95]

(1, 2) [Coplien, 1995] Coplien, J., Curiously Recurring Template Patterns, C++ Report, February 1995, pp. 24-27.

Reference

template <
    class Derived
  , class Value
  , class CategoryOrTraversal
  , class Reference  = Value&
  , class Difference = ptrdiff_t
>
class iterator_facade {
 public:
    typedef remove_const<Value>::type value_type;
    typedef Reference reference;
    typedef Value* pointer;
    typedef Difference difference_type;
    typedef /* see below */ iterator_category;

    reference operator*() const;
    /* see below */ operator->() const;
    /* see below */ operator[](difference_type n) const;
    Derived& operator++();
    Derived operator++(int);
    Derived& operator--();
    Derived operator--(int);
    Derived& operator+=(difference_type n);
    Derived& operator-=(difference_type n);
    Derived operator-(difference_type n) const;
 protected:
    typedef iterator_facade iterator_facade_;
};

// Comparison operators
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type // exposition
operator ==(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator !=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

// Iterator difference
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
/* see below */
operator-(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
          iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);

// Iterator addition
template <class Dr, class V, class TC, class R, class D>
Derived operator+ (iterator_facade<Dr,V,TC,R,D> const&,
                   typename Derived::difference_type n);

template <class Dr, class V, class TC, class R, class D>
Derived operator+ (typename Derived::difference_type n,
                   iterator_facade<Dr,V,TC,R,D> const&);

The iterator_category member of iterator_facade is

iterator-category(CategoryOrTraversal, value_type, reference)

where iterator-category is defined as follows:

iterator-category(C,R,V) :=
   if (C is convertible to std::input_iterator_tag
       || C is convertible to std::output_iterator_tag
   )
       return C

   else if (C is not convertible to incrementable_traversal_tag)
       the program is ill-formed

   else return a type X satisfying the following two constraints:

      1. X is convertible to X1, and not to any more-derived
         type, where X1 is defined by:

           if (R is a reference type
               && C is convertible to forward_traversal_tag)
           {
               if (C is convertible to random_access_traversal_tag)
                   X1 = random_access_iterator_tag
               else if (C is convertible to bidirectional_traversal_tag)
                   X1 = bidirectional_iterator_tag
               else
                   X1 = forward_iterator_tag
           }
           else
           {
               if (C is convertible to single_pass_traversal_tag
                   && R is convertible to V)
                   X1 = input_iterator_tag
               else
                   X1 = C
           }

      2. category-to-traversal(X) is convertible to the most
         derived traversal tag type to which X is also
         convertible, and not to any more-derived traversal tag
         type.

[Note: the intention is to allow iterator_category to be one of the five original category tags when convertibility to one of the traversal tags would add no information]

The enable_if_interoperable template used above is for exposition purposes. The member operators should only be in an overload set provided the derived types Dr1 and Dr2 are interoperable, meaning that at least one of the types is convertible to the other. The enable_if_interoperable approach uses SFINAE to take the operators out of the overload set when the types are not interoperable. The operators should behave as-if enable_if_interoperable were defined to be:

template <bool, typename> enable_if_interoperable_impl
{};

template <typename T> enable_if_interoperable_impl<true,T>
{ typedef T type; };

template<typename Dr1, typename Dr2, typename T>
struct enable_if_interoperable
  : enable_if_interoperable_impl<
        is_convertible<Dr1,Dr2>::value || is_convertible<Dr2,Dr1>::value
      , T
    >
{};

iterator_facade Requirements

The following table describes the typical valid expressions on iterator_facade's Derived parameter, depending on the iterator concept(s) it will model. The operations in the first column must be made accessible to member functions of class iterator_core_access. In addition, static_cast<Derived*>(iterator_facade*) is well-formed.

In the table below, F is iterator_facade<X,V,C,R,D>, a is an object of type X, b and c are objects of type const X, n is an object of F::difference_type, y is a constant object of a single pass iterator type interoperable with X, and z is a constant object of a random access traversal iterator type interoperable with X.

iterator_facade Core Operations

Expression | Return Type | Assertion/Note | Used to implement Iterator Concept(s)
c.dereference() | F::reference |  | Readable Iterator, Writable Iterator
c.equal(y) | convertible to bool | true if c and y refer to the same position. | Single Pass Iterator
a.increment() | unused |  | Incremental Iterator
a.decrement() | unused |  | Bidirectional Traversal Iterator
a.advance(n) | unused |  | Random Access Traversal Iterator
c.distance_to(z) | convertible to F::difference_type | equivalent to distance(c, X(z)). | Random Access Traversal Iterator

iterator_facade operations

The operations in this section are described in terms of operations on the core interface of Derived which may be inaccessible (i.e. private). The implementation should access these operations through member functions of class iterator_core_access.

reference operator*() const;
Returns:

static_cast<Derived const*>(this)->dereference()

operator->() const; (see below)
Returns:

If reference is a reference type, an object of type pointer equal to:

&static_cast<Derived const*>(this)->dereference()
Otherwise returns an object of unspecified type such that, (*static_cast<Derived const*>(this))->m is equivalent to (w = **static_cast<Derived const*>(this), w.m) for some temporary object w of type value_type.

unspecified operator[](difference_type n) const;
Returns: an object convertible to value_type. For constant objects v of type value_type, and n of type difference_type, (*this)[n] = v is equivalent to *(*this + n) = v, and static_cast<value_type const&>((*this)[n]) is equivalent to static_cast<value_type const&>(*(*this + n))

Derived& operator++();

Effects:

static_cast<Derived*>(this)->increment();
return *static_cast<Derived*>(this);

Derived operator++(int);

Effects:

Derived tmp(static_cast<Derived const*>(this));
++*this;
return tmp;

Derived& operator--();

Effects:

static_cast<Derived*>(this)->decrement();
return *static_cast<Derived*>(this);

Derived operator--(int);

Effects:

Derived tmp(static_cast<Derived const*>(this));
--*this;
return tmp;

Derived& operator+=(difference_type n);

Effects:

static_cast<Derived*>(this)->advance(n);
return *static_cast<Derived*>(this);

Derived& operator-=(difference_type n);

Effects:

static_cast<Derived*>(this)->advance(-n);
return *static_cast<Derived*>(this);

Derived operator-(difference_type n) const;

Effects:

Derived tmp(static_cast<Derived const*>(this));
return tmp -= n;

template <class Dr, class V, class TC, class R, class D>
Derived operator+ (iterator_facade<Dr,V,TC,R,D> const&,
                   typename Derived::difference_type n);

template <class Dr, class V, class TC, class R, class D>
Derived operator+ (typename Derived::difference_type n,
                   iterator_facade<Dr,V,TC,R,D> const&);

Effects:

Derived tmp(static_cast<Derived const*>(this));
return tmp += n;

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator ==(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).equal((Dr2 const&)rhs).

Otherwise,

((Dr2 const&)rhs).equal((Dr1 const&)lhs).

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator !=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

!((Dr1 const&)lhs).equal((Dr2 const&)rhs).

Otherwise,

!((Dr2 const&)rhs).equal((Dr1 const&)lhs).

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) < 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) > 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) <= 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) >= 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) > 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) < 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) >= 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) <= 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,difference>::type
operator -(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Return Type:

if is_convertible<Dr2,Dr1>::value

then

difference shall be iterator_traits<Dr1>::difference_type.

Otherwise

difference shall be iterator_traits<Dr2>::difference_type
Returns:

if is_convertible<Dr2,Dr1>::value

then

-((Dr1 const&)lhs).distance_to((Dr2 const&)rhs).

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs).

Tutorial Example

This section walks the user through the implementation of a few iterators using iterator_facade, based around the simple example of a linked list of polymorphic objects. This example was inspired by a posting by Keith Macdonald on the Boost-Users mailing list.

The Problem

The code snippet below is an example of a polymorphic linked list node base class:

# include <iostream>

struct node_base
{
    node_base() : m_next(0) {}

    // Each node manages all of its tail nodes
    virtual ~node_base() { delete m_next; }

    // Access the rest of the list
    node_base* next() const { return m_next; }

    // print to the stream
    virtual void print(std::ostream& s) const = 0;

    // double the value
    virtual void double_me() = 0;

    void append(node_base* p)
    {
        if (m_next)
            m_next->append(p);
        else
            m_next = p;
    }

 private:
    node_base* m_next;
};

Lists can hold objects of different types by linking together specializations of the following template:

template <class T>
struct node : node_base
{
    node(T x)
      : m_value(x)
    {}

    void print(std::ostream& s) const { s << this->m_value; }
    void double_me() { m_value += m_value; }

 private:
    T m_value;
};

Print any node using the following streaming operator:

inline std::ostream& operator<<(std::ostream& s, node_base const& n)
{
    n.print(s);
    return s;
}

The first challenge is to build an appropriate iterator over these lists.

A Basic Iterator Using iterator_facade

Construct a node_iterator class using inheritance from iterator_facade to implement most of the iterator's operations.

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>

class node_iterator
  : public boost::iterator_facade<...>
{
   ...
};

Template Arguments for iterator_facade

iterator_facade has several template parameters, so decide what types to use for the arguments. The parameters are Derived, Value, CategoryOrTraversal, Reference, and Difference.

Derived

The first parameter is the iterator class name itself, node_iterator as iterator_facade is meant to be used with the CRTP [Cop95].

Value

The Value parameter determines the node_iterator's value_type. In this case, the user iterates over node_base objects, so Value will be node_base.

CategoryOrTraversal

Now, determine the iterator traversal concept that the node_iterator will model. The iterator cannot be a bidirectional traversal iterator as singly-linked lists have only forward links. The iterator must be a forward traversal iterator, as it must make multiple passes over the same linked list (unlike, say, an istream_iterator which consumes the stream it traverses). Therefore, pass boost::forward_traversal_tag in this position1.

[1] iterator_facade also supports old-style category tags, so pass std::forward_iterator_tag here; either way, the resulting iterator's iterator_category will end up being std::forward_iterator_tag.

Reference

The Reference argument becomes the type returned by node_iterator's dereference operation, and will also be the same as std::iterator_traits<node_iterator>::reference. The library's default for this parameter is Value&; since node_base& is a good choice for the iterator's reference type, omit this argument, or pass use_default.

Difference

The Difference argument determines how the distance between two node_iterators will be measured and will also be the same as std::iterator_traits<node_iterator>::difference_type. The library's default for Difference is std::ptrdiff_t, an appropriate type for measuring the distance between any two addresses in memory, and one that works for almost any iterator, so we can omit this argument, too.

The declaration of node_iterator will therefore look something like:

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>

class node_iterator
  : public boost::iterator_facade<
        node_iterator
      , node_base
      , boost::forward_traversal_tag
    >
{
   ...
};

Constructors and Data Members

Next ,decide how to represent the iterator's position. This representation is in the form of data members, so write constructors to initialize them. The node_iterator's position is quite naturally represented using a pointer to a node_base. It needs a constructor to build an iterator from a node_base*, and a default constructor to satisfy the forward traversal iterator requirements2. The node_iterator then becomes:

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>

class node_iterator
  : public boost::iterator_facade<
        node_iterator
      , node_base
      , boost::forward_traversal_tag
    >
{
 public:
    node_iterator()
      : m_node(0)
    {}

    explicit node_iterator(node_base* p)
      : m_node(p)
    {}

 private:
    ...
    node_base* m_node;
};
[2] Technically, the C++ standard places almost no requirements on a default-constructed iterator, so write the default constructor to leave m_node uninitialized when efficiency is a concern.

Implementing the Core Operations

The last step is to implement the core operations required by the concepts the iterator models. Refer the table, see that the first three rows are applicable because node_iteratorneeds to satisfy the requirements for readable iterator, single pass iterator, and incrementable iterator.

Supply dereference, equal, and increment members. As these members should not become a part of node_iterator's public interface, make them private and grant friendship to boost::iterator_core_access, a "back-door" that iterator_facade uses to get access to the core operations:

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>

class node_iterator
  : public boost::iterator_facade<
        node_iterator
      , node_base
      , boost::forward_traversal_tag
    >
{
 public:
    node_iterator()
      : m_node(0) {}

    explicit node_iterator(node_base* p)
      : m_node(p) {}

 private:
    friend class boost::iterator_core_access;

    void increment() { m_node = m_node->next(); }

    bool equal(node_iterator const& other) const
    {
        return this->m_node == other.m_node;
    }

    node_base& dereference() const { return *m_node; }

    node_base* m_node;
};

A complete and conforming readable, forward-traversal iterator. For a working example, see the program below:

// Copyright David Abrahams 2004. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "node_iterator1.hpp"
#include <string>
#include <memory>
#include <iostream>
#include <algorithm>
#include <functional>

int main()
{
    std::auto_ptr<node<int> > nodes(new node<int>(42));
    nodes->append(new node<std::string>(" is greater than "));
    nodes->append(new node<int>(13));

    std::copy(
        node_iterator(nodes.get()), node_iterator()
      , std::ostream_iterator<node_base>(std::cout, " ")
    );
    std::cout << std::endl;

    std::for_each(
        node_iterator(nodes.get()), node_iterator()
      , std::mem_fun_ref(&node_base::double_me)
    );

    std::copy(
        node_iterator(nodes.get()), node_iterator()
      , std::ostream_iterator<node_base>(std::cout, "/")
    );
    std::cout << std::endl;
}

A constant node_iterator

Constant and Mutable iterators

The term mutable iterator means an iterator through which the object it references (its "referent") can be modified. A constant iterator is one which doesn't allow modification of its referent.

The words constant and mutable do not refer to the ability to modify the iterator itself. For example, an int const* is a non-const constant iterator, which can be incremented but doesn't allow modification of its referent, and int* const is a const mutable iterator, which cannot be modified but which allows modification of its referent.It probably doesn't help much that a container's constant iterator is called const_iterator.

Now, the node_iterator gives clients access to both node's print(std::ostream&) const member function, and its mutating double_me() member. To build a constant node_iterator , make three changes:

class const_node_iterator
  : public boost::iterator_facade<
        const_node_iterator
      , node_base const
      , boost::forward_traversal_tag
    >
{
 public:
    const_node_iterator()
      : m_node(0) {}

    explicit const_node_iterator(node_base* p)
      : m_node(p) {}

 private:
    friend class boost::iterator_core_access;

    void increment() { m_node = m_node->next(); }

    bool equal(const_node_iterator const& other) const
    {
        return this->m_node == other.m_node;
    }

    node_base const& dereference() const { return *m_node; }

    node_base const* m_node;
};

const and an iterator's value_type

The C++ standard requires an iterator's value_type not be const-qualified, so iterator_facade strips the const from its Value parameter in order to produce the iterator's value_type. Making the Value argument const provides a useful hint to iterator_facade that the iterator is a constant iterator, and the default Reference argument will be correct for all lvalue iterators.

As a matter of fact, node_iterator and const_node_iterator are so similar that it makes sense to factor the common code out into a template as follows:

template <class Value>
class node_iter
  : public boost::iterator_facade<
        node_iter<Value>
      , Value
      , boost::forward_traversal_tag
    >
{
 public:
    node_iter()
      : m_node(0) {}

    explicit node_iter(Value* p)
      : m_node(p) {}

 private:
    friend class boost::iterator_core_access;

    bool equal(node_iter<Value> const& other) const
    {
        return this->m_node == other.m_node;
    }

    void increment()
    { m_node = m_node->next(); }

    Value& dereference() const
    { return *m_node; }

    Value* m_node;
};
typedef node_iter<node_base> node_iterator;
typedef node_iter<node_base const> node_const_iterator;

Interoperability

The const_node_iterator works perfectly well on its own, but taken together with node_iterator it doesn't quite meet expectations. For example, to be able to pass a node_iterator where a const_node_iterator was expected, just as with std::list<int>'s iterator and const_iterator. Furthermore, given a node_iterator and a const_node_iterator into the same list, the user should be able to compare them for equality.

The ability to use two different iterator types together is known as interoperability. To achieve interoperability in this case, templatize the equal function and add a templatized converting constructor3 4:

template <class Value>
class node_iter
  : public boost::iterator_facade<
        node_iter<Value>
      , Value
      , boost::forward_traversal_tag
    >
{
 public:
    node_iter()
      : m_node(0) {}

    explicit node_iter(Value* p)
      : m_node(p) {}

    template <class OtherValue>
    node_iter(node_iter<OtherValue> const& other)
      : m_node(other.m_node) {}

 private:
    friend class boost::iterator_core_access;
    template <class> friend class node_iter;

    template <class OtherValue>
    bool equal(node_iter<OtherValue> const& other) const
    {
        return this->m_node == other.m_node;
    }

    void increment()
    { m_node = m_node->next(); }

    Value& dereference() const
    { return *m_node; }

    Value* m_node;
};
typedef impl::node_iterator<node_base> node_iterator;
typedef impl::node_iterator<node_base const> node_const_iterator;
[3] An old compiler cannot handle this example, see the example code for workarounds.
[4] If node_iterator had been a random access traversal iterator, templatize its distance_to function as well.

The code snippet below is an example program which exercises the interoperable iterators.

// Copyright David Abrahams 2004. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "node_iterator2.hpp"
#include <string>
#include <memory>
#include <iostream>
#include <algorithm>
#include <boost/mem_fn.hpp>
#include <cassert>

int main()
{
    std::auto_ptr<node<int> > nodes(new node<int>(42));
    nodes->append(new node<std::string>(" is greater than "));
    nodes->append(new node<int>(13));

    // Check interoperability
    assert(node_iterator(nodes.get()) == node_const_iterator(nodes.get()));
    assert(node_const_iterator(nodes.get()) == node_iterator(nodes.get()));

    assert(node_iterator(nodes.get()) != node_const_iterator());
    assert(node_const_iterator(nodes.get()) != node_iterator());

    std::copy(
        node_iterator(nodes.get()), node_iterator()
      , std::ostream_iterator<node_base>(std::cout, " ")
    );
    std::cout << std::endl;

    std::for_each(
        node_iterator(nodes.get()), node_iterator()
      , boost::mem_fn(&node_base::double_me)
    );

    std::copy(
        node_const_iterator(nodes.get()), node_const_iterator()
      , std::ostream_iterator<node_base>(std::cout, "/")
    );
    std::cout << std::endl;
    return 0;
}

Telling the Truth

Now node_iterator and node_const_iterator behave exactly as expected. Convert and compare the iterators in one direction, that is, from node_iterator to node_const_iterator . Converting from node_const_iterator to node_iterator , gives an error when the converting constructor tries to initialize node_iterator's m_node, a node* with a node const*.

The problem is that boost::is_convertible<node_const_iterator,node_iterator>::value will be true, but it should be false. is_convertible can see as far as the declaration of node_iter's converting constructor, but can not look inside at the definition to make sure it will compile. A perfect solution would make node_iter's converting constructor disappear when the m_node conversion would fail.

In fact, that is possible using boost::enable_if. Rewrite the converting constructor as follows, to remove it from the overload set when it's not appropriate:

#include <boost/type_traits/is_convertible.hpp>
#include <boost/utility/enable_if.hpp>

  ...

private:
  struct enabler {};

public:
  template <class OtherValue>
  node_iter(
      node_iter<OtherValue> const& other
    , typename boost::enable_if<
          boost::is_convertible<OtherValue*,Value*>
        , enabler
      >::type = enabler()
  )
    : m_node(other.m_node) {}

Wrap Up

This concludes the iterator_facade tutorial. Please refer iterator_adaptor for another way to approach writing these iterators which might even be superior.

©Copyright 2003.
David Abrahams,dave@boost-consulting.com.
Jeremy Siek,jsiek@osl.iu.edu.
Thomas Witt, witt@ive.uni-hannover.de.


Iterator Property Map Adaptor

Iterator Property Map

iterator_property_map<RandomAccessIterator, OffsetMap, T, R>

This property map is an adaptor that converts any random access iterator into a Lvalue Property Map. The OffsetMap type is responsible for converting key objects to integers that can be used as offsets with the random access iterator.

Example

// print out the capacity and flow for all the edges in the graph
template <class Graph, class CapacityPMap, class FlowPMap>
void print_network(Graph& G, CapacityPMap capacity, FlowPMap flow)
{
  typedef typename boost::graph_traits<Graph>::vertex_iterator    Viter;
  typedef typename boost::graph_traits<Graph>::out_edge_iterator OutEdgeIter;
  typedef typename boost::graph_traits<Graph>::in_edge_iterator InEdgeIter;

  Viter ui, uiend;
  for (boost::tie(ui, uiend) = vertices(G); ui != uiend; ++ui) {
    OutEdgeIter out, out_end;
    std::cout << *ui << "\t";

    for(boost::tie(out, out_end) = out_edges(*ui, G); out != out_end; ++out)
      std::cout << "--(" << get(capacity, *out) << ", "
       << get(flow, *out) << ")--> " << target(*out,G) << "\t";
    std::cout << std::endl << "\t";

    InEdgeIter in, in_end;
    for(boost::tie(in, in_end) = in_edges(*ui, G); in != in_end; ++in)
      std::cout << "<--(" << get(capacity, *in) << "," << get(flow, *in) << ")-- "
           << source(*in,G) << "\t";
    std::cout << std::endl;
  }
}

int main(int, char*[])
{
  typedef boost::adjacency_list<boost::vecS, boost::vecS,
    boost::bidirectionalS, boost::no_plugin,
    boost::plugin<boost::id_tag, std::size_t> > Graph;

  const int num_vertices = 9;
  Graph G(num_vertices);

  int capacity[] = { 10, 20, 20, 20, 40, 40, 20, 20, 20, 10 };
  int flow[] = { 8, 12, 12, 12, 12, 12, 16, 16, 16, 8 };

  // add edges to the graph, and assign each edge an ID number
  // to index into the property arrays
  add_edge(G, 0, 1, 0);
  // ...

  typedef boost::graph_traits<Graph>::edge_descriptor Edge;
  typedef boost::property_map<Graph, boost::id_tag>::type EdgeID_PMap;
  EdgeID_PMap edge_id = get(boost::edge_index(), G);

  boost::iterator_property_map<int*, EdgeID_PMap, int, int&>
     capacity_pa(capacity, edge_id),
     flow_pa(flow, edge_id);

  print_network(G, capacity_pa, flow_pa);

  return 0;
}

Where Defined

boost/property_map.hpp

Model Of

Lvalue Property Map

Template Parameters

Parameter | Description | Default
Iterator | Must be a model of Random Access Iterator. |
OffsetMap | Must be a model of Readable Property Map and the value type must be convertible to the difference type of the iterator. |
T | The value type of the iterator. | std::iterator_traits<RandomAccessIterator>::value_type
R | The reference type of the iterator. | std::iterator_traits<RandomAccessIterator>::reference

Members

In addition to the methods and functions required by Lvalue Property Map, this class has the following members.

property_traits<iterator_property_map>::value_type

This is the same type as std::iterator_traits<Iterator>::value_type.

iterator_property_map(Iterator i)

Constructor. The OffsetMap is default constructed.

iterator_property_map(Iterator i, OffsetMap m)

Constructor.

reference operator[](difference_type v) const

The operator bracket for property access. The reference and difference_type types are from std::iterator_traits<Iterator>.

Non-Member functions

  template <class RAIter, class OffsetMap>
  iterator_property_map<RAIter, OffsetMap,
    typename std::iterator_traits<RAIter>::value_type,
    typename std::iterator_traits<RAIter>::reference
    >
  make_iterator_property_map(RAIter iter, OffsetMap omap)

A function for creating an iterator map.

  template <class RAIter, class OffsetMap, class ValueType>
  iterator_property_map<RAIter, OffsetMap,
    typename std::iterator_traits<RAIter>::value_type,
    typename std::iterator_traits<RAIter>::reference
    >
  make_iterator_property_map(RAIter iter, OffsetMap omap, ValueType dummy_arg)

Use this function instead of the 2-argument version if your compiler does not support partial specialization (like Visual C++).

Copyright © 2000-2002
Jeremy Siek, Univ.of Notre Dame (jsiek@osl.iu.edu)
Lie-Quan Lee, Univ.of Notre Dame (llee1@osl.iu.edu)
Andrew Lumsdaine, Univ.of Notre Dame (lums@osl.iu.edu)


IteratorConstructibleGraph

The IteratorConstructibleGraph concept describes the interface for graph types that can be constructed using a kind of edge iterator. The edge iterator can be any InputIterator that dereferences to a pair of integers (i,j), which represent an edge that should be in the graph. The two integers i and j represent vertices where 0 <= i < |V| and 0 <= j < |V|. The edge iterator's value type should be std::pair<T,T> (or at least be a structure that has members first and second) and the value type T of the pair must be convertible to the vertices_size_type of the graph (an integer). There are two valid expressions required by this concept, both of which are constructors. The first creates a graph object from a first/last iterator range. The second constructor also takes a first/last iterator range and in addition requires the number of vertices and number of edges. For some graph and edge iterator types the second constructor can be more efficient than the first.

Example

The following example creates two graph objects from an array of edges (vertex pairs). The type Edge* satisfies the requirements for an InputIterator and can therefore be used to construct a graph.

  typedef ... IteratorConstructibleGraph;
  typedef boost::graph_traits Traits;

  typedef std::pair Edge;
  Edge edge_array[] =
    { Edge(0,1), Edge(0,2), Edge(0,3), Edge(0,4), Edge(0,5),
      Edge(1, 2), Edge(1,5), Edge(1,3),
      Edge(2, 4), Edge(2,5),
      Edge(3, 2),
      Edge(4, 3), Edge(4,1),
      Edge(5, 4) };
  Edge* first = edge_array,
    last = edge_array + sizeof(edge_array)/sizeof(Edge);

  IteratorConstructibleGraph G1(first, last);
  // do something with G1 ...

  Traits::vertices_size_type size_V = 6;
  Traits::edges_size_type size_E = sizeof(edge_array)/sizeof(Edge);
  IteratorConstructibleGraph G2(first, last, size_V, size_E);
  // do something with G2 ...

Refinement of

Graph

Notation

GG | is a graph type that models IteratorConstructibleGraph.
g | is an object of type G.
first, last | are edge iterators (see above).
Tr | is an object of type graph_traits<G>.
n_vertices | is an object of type Tr::vertices_size_type.
n_edges | is an object of type Tr::edges_size_type.

Valid Expressions

G g(first, last);
Construct graph object g given an edge range [first,last).
G g(first, last, n_vertices, n_edges);
Construct graph object g given an edge range [first,last), the number of vertices, and the number of edges. Sometimes this constructor is more efficient than the constructor lacking the graph size information.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


johnson_all_pairs_shortest_paths

// named parameter version
  template <class VertexAndEdgeListGraph, class DistanceMatrix,
            class VertexID, class Weight, class BinaryPredicate,
            class BinaryFunction, class Infinity, class DistanceZero>
  bool
  johnson_all_pairs_shortest_paths(VertexAndEdgeListGraph& g1,
               DistanceMatrix& D,
               VertexID id1, Weight w1, const BinaryPredicate& compare,
               const BinaryFunction& combine, const Infinity& inf,
               DistanceZero zero);

template <typename Graph, typename DistanceMatrix, typename P, typename T, typename R>
bool johnson_all_pairs_shortest_paths(Graph& g, DistanceMatrix& D,
  const bgl_named_params<P, T, R>& params = all defaults)

// non-named parameter version
template <typename Graph, typename DistanceMatrix,
          typename VertexIndex, typename WeightMap, typename DT>
bool
johnson_all_pairs_shortest_paths(VertexAndEdgeListGraph& g1,
  DistanceMatrix& D,
  VertexIndex i_map, WeightMap w_map, DT zero)

This algorithm finds the shortest distance between every pair of vertices in the graph. The algorithm returns false if there is a negative weight cycle in the graph and true otherwise. The distance between each pair of vertices is stored in the distance matrix D. This is one of the more time intensive graph algorithms, having a time complexity of O(V E log V).

This algorithm should be used to compute shortest paths between every pair of vertices for sparse graphs. For dense graphs, use floyd_warshall_all_pairs_shortest_paths.

Where Defined

boost/graph/johnson_all_pairs_shortest.hpp

Parameters

IN: Graph& g
A directed or undirected graph. The graph type must be a model of Vertex List Graph, Edge List Graph, and Incidence Graph.
OUT: DistanceMatrix& D
The length of the shortest path between each pair of vertices u,v in the graph is stored in D[u][v]. The set of types {DistanceMatrix, vertices_size_type, D} must be a model of BasicMatrix where D is the value type of the DistanceMap.

Named Parameters

IN: weight_map(WeightMap w_map)
The weight or ``length'' of each edge in the graph. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for the map must be Addable with the value type of the distance map.
Default: get(edge_weight, g)
UTIL: weight_map2(WeightMap2 w2_map)
This parameter is no longer needed, and will be ignored.
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure in the internal call to Dijkstra's algorithm. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note: if you use this default, make sure your graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
UTIL: distance_map(DistanceMap d_map)
This parameter is no longer needed, and will be ignored.
IN: distance_compare(CompareFunction cmp)
This function is use to compare distances to determine which vertex is closer to the source vertex. The CompareFunction type must be a model of \stlconcept{BinaryPredicate} and have argument types that match the value type of the WeightMap property map.
Default: std::less<DT> with DT=typename property_traits<WeightMap>::value_type
IN: distance_combine(CombineFunction cmb)
This function is used to combine distances to compute the distance of a path. The CombineFunction type must be a model of Binary Function. The first argument type of the binary function must match the value type of the DistanceMap property map and the second argument type must match the value type of the WeightMap property map. The result type must be the same type as the distance value type.
Default: std::plus<DT> with DT=typename property_traits<WeightMap>::value_type
IN: distance_inf(DT inf)
This value is used to initialize the distance for each vertex before the start of the algorithm. The type DT must be the value type of the WeigthMap.
Default: std::numeric_limits::max()
IN: distance_zero(DT zero)
This value is used to initialize the distance for the source vertex before the start of the algorithm. The type DT must be the value type of the We igthMap.
Default: 0
UTIL/OUT: color_map(ColorMap c_map)
This is used during the execution of the algorithm to mark the vertices. The vertices start out white and become gray when they are inserted in the queue. They then turn black when they are removed from the queue. At the end of the algorithm, vertices reachable from the source vertex will have been colored black. All other vertices will still be white. The ty pe ColorMap must be a model of Read/Write Property Map. A vertex descriptor must be usable as the key type of the map, and the value type of the map must be a model of Color Value.
Default: an iterator_property_map c reated from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.

Complexity

The time complexity is O(V E log V).

Example

The file example/johnson-eg.cpp applies Johnson's algorithm for all-pairs shortest paths to the example graph from page 568 of the CLR [8].

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Function kamada_kawai_spring_layout

boost::kamada_kawai_spring_layout — Kamada-Kawai spring layout for connected, undirected graphs.

Synopsis

template<typename Graph, typename PositionMap, typename WeightMap, typename T,
         bool EdgeOrSideLength, typename Done, typename VertexIndexMap,
         typename DistanceMatrix, typename SpringStrengthMatrix,
         typename PartialDerivativeMap>
  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position,
                                  WeightMap weight,
                                  unspecified edge_or_side_length, Done done,
                                  typename property_traits< WeightMap >::value_type spring_constant,
                                  VertexIndexMap index,
                                  DistanceMatrix distance,
                                  SpringStrengthMatrix spring_strength,
                                  PartialDerivativeMap partial_derivatives);
template<typename Graph, typename PositionMap, typename WeightMap, typename T,
         bool EdgeOrSideLength, typename Done, typename VertexIndexMap>
  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position,
                                  WeightMap weight,
                                  unspecified edge_or_side_length, Done done,
                                  typename property_traits< WeightMap >::value_type spring_constant,
                                  VertexIndexMap index);
template<typename Graph, typename PositionMap, typename WeightMap, typename T,
         bool EdgeOrSideLength, typename Done>
  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position,
                                  WeightMap weight,
                                  unspecified edge_or_side_length, Done done,
                                  typename property_traits< WeightMap >::value_type spring_constant = typename property_traits< WeightMap >::value_type(1));
template<typename Graph, typename PositionMap, typename WeightMap, typename T,
         bool EdgeOrSideLength>
  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position,
                                  WeightMap weight,
                                  unspecified edge_or_side_length);

Where Defined

boost/graph/kamada_kawai_spring_layout.hpp

Description

This algorithm [57] performs graph layout (in two dimensions) for connected, undirected graphs. It operates by relating the layout of graphs to a dynamic spring system and minimizing the energy within that system. The strength of a spring between two vertices is inversely proportional to the square of the shortest distance (in graph terms) between those two vertices. Essentially, vertices that are closer in the graph-theoretic sense (i.e., by following edges) will have stronger springs and will therefore be placed closer together.

Prior to invoking this algorithm, it is recommended that the vertices be placed along the vertices of a regular n-sided polygon via circle_layout.

Returns: true if layout was successful or false if a negative weight cycle was detected or the graph is disconnected.

Parameters

IN: const Graph& g
The graph, whose type Graph must model the VertexListGraph, EdgeListGraph, and IncidenceGraph concepts. The graph must be undirected and connected.
Python: This parameter is named grap h in Python.
OUT: PositionMap position
This property map is used to store the position of each vertex. The type PositionMap must be a model of Writable Property Map, with the graph's vertex descriptor type as its key type.
Python: The position map must be a vertex_point2d_map for the graph.
Python default: graph.get_vertex_point2d_map("position")
IN: weight_map(WeightMap w_map)
The weight or ``length'' of each edge in the graph. The weights must all be non-negative, and the algorithm will throw a negative_edge exception is one of the edges is negative. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for this map must be the same as the value type of the distance map.
Default: get(edge_weight, g)
Python: Must be an edge_double_map for the graph.
Python default: graph.get_edge_double_map("weight")
IN: EdgeOrSideLength edge_or_side_length
Provides either the unit length e of an edge in the layout or the length of a side s of the display area, and must be either boost::edge_length(e) or boost::side_length(s) , respectively. Python: In Python, this value always refers to the side length and may only be a double.
IN: Done done
A 4-argument function object that is passed the current value of delta_p (i.e., the energy of vertex p ), the vertex p , the graph g , and a boolean flag indicating whether delta_p is the maximum energy in the system (when true ) or the energy of the vertex being moved. Default: layout_tolerance instantiated over the value type of the weight map.
Python: Any callable Python object with an appropriate signature suffices.
IN: typename property_traits<WeightMap>::value_type spring_constant
The constant multiplied by each spring's strength. Larger values create systems with more energy that can take longer to stabilize; smaller values create systems with less energy that stabilize quickly but do not necessarily result in pleasing layouts.
Default: 1.
IN: VertexIndexMap index
As a mapping from vertices to index values between 0 and num_vertices(g) .
Default:get(vertex_index,g). Note: if this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.
UTIL/OUT: DistanceMap distance
This parameter will be used to store the distance from every vertex to every other vertex, which is computed in the first stages of the algorithm. This value's type must be a model of BasicMatrix with value type equal to the value type of the weight map.
Default: A vector of vectors.
Python: Unsupported parameter.
UTIL/OUT: SpringStrengthMatrix spring_strength
This matrix will be used to store the strength of the spring between every pair of vertices. This value's type must be a model of BasicMatrix with value type equal to the value type of the weight map.
Default: A vector of vectors of the value type of the weight map.
Python: Unsupported parameter.
UTIL: PartialDerivativeMap partial_derivatives
A property map that will be used to store the partial derivates of each vertex with respect to the x and y coordinates. This must be a Read/Write Property Map whose value type is a pair with both types equivalent to the value type of the weight map. The default is an iterator property map.
Default: An iterator_property_map created from an std::vector of std::pair<weight_type, weight_type>, where weight_type is the value type of the WeightMap.
Python: Unsupported parameter.
Python IN: bool progressive
When false, performs layout of the graph on a circle before running the Kamada-Kawai algorithm. If true, the algorithm is executing starting with the vertex configuration in the position map.
Default: False.
Copyright © 2004
Douglas Gregor, Indiana University (dgregor -at cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Kevin Bacon Example

Six Degrees of Kevin Bacon

A fun application of graph theory comes up in the popular game ``Six Degrees of Kevin Bacon''. The idea of the game is to connect an actor to Kevin Bacon through a trail of actors who appeared together in movies, and do so in less than six steps. For example, Theodore Hesburgh (President Emeritus of the University of Notre Dame) was in the movie ``Rudy'' with the actor Gerry Becker, who was in the movie ``Sleepers'' with Kevin Bacon. Why Kevin Bacon? For some reason the three students who invented the game, Mike Ginelli, Craig Fass, and Brian Turtle decided that Kevin Bacon is the center of the entertainment world. The Kevin Bacon game is quite similar to the Erdös Number which has ``been a part of the folklore of mathematicians throughout the world for many years''.

The ``Six Degrees of Kevin Bacon'' game is really a graph problem. If the user assigns each actor to a vertex, and add an edge between two actors if they have appeared together in a movie, then the user has a graph that represents the data for this game. Then the problem of finding a trail of actors to Kevin Bacon becomes a traditional graph problem: that of finding a path between two vertices. Since we wish to find a path that is shorter than six steps, ideally we would find the shortest path between the vertices. One might think to apply Dijkstra's shortest path algorithm to this problem, but that would be overkill since Dijkstra's algorithm is meant for situations when each edge has an associated length (or weight) and the goal is to find the path with the shortest cumulative length. Since we are only concerned with finding the shortest paths in terms of the number of edges the breadth-first search algorithm will solve the problem (and use less time than Dijkstra's).

Input File and Graph Setup

For this example we will use a much smaller graph than all movies and all actors. The full source code for this example is in examples/kevin-bacon.cpp. We have supplied a file kevin-bacon.dat which contains a list of actor pairs who appeared in the same movie. Each line of the file contains an actor's name, a movie, and another actor that appeared in the movie. The ";'' character is used as a separator. Here is an excerpt.

Patrick Stewart;Prince of Egypt, The (1998);Steve Martin

The first task will be to read in the file and create a graph from it. Use std::ifstream to input the file.

  std::ifstream datafile("./kevin-bacon.dat");
  if (!datafile) {
    std::cerr << "No ./kevin-bacon.dat file" << std::endl;
    return EXIT_FAILURE;
  }

We will want to attach the actor's names to the vertices in the graph, and the movie names to the edges. We use the property class to specify the addition of these vertex and edge properties to the graph. We choose to use an undirected graph, because the relationship of actors appearing together in a movie is symmetric.

  using namespace boost;
  typedef adjacency_list<vecS, vecS, undirectedS,
    property<vertex_name_t, string>,
    property<edge_name_t, string > >
  > Graph;

To access the properties, obtain property map objects from the graph. The following code shows how this is done.

  property_map<Graph, vertex_name_t>::type actor_name = get(vertex_name, g);
  property_map<Graph, edge_name_t>::type connecting_movie = get(edge_name, g);

Next we can start to loop through the file, parsing each line into a sequence of tokens using the Boost Tokenizer Library.

  for (std::string line; std::getline(datafile, line);) {
    char_delimiters_separator<char> sep(false, "", ";");
    tokenizer<> line_toks(line, sep);
    ...
  }

Each line of the input corresponds to an edge in the graph, with the two actors as the vertices incident to the edge, and the movie name will be a property attached to the edge. One challenge in creating the graph from this format of input is that the edges are specified by a pair of actor names. As we add vertices to the graph, we'll need to create a map from actor names to their vertices so that later appearances of the same actor (on a different edge) can be linked with the correct vertex in the graph. The std::map can be used to implement this mapping.

  typedef graph_traits<Graph>::vertex_descriptor Vertex;
  typedef std::map<string, Vertex> NameVertexMap;
  NameVertexMap actors;

The first token will be an actor's name. If the actor is not already in our actor's map we will add a vertex to the graph, set the name property of the vertex, and record the vertex descriptor in the map. If the actor is already in the graph, we will retrieve the vertex descriptor from the map's pos iterator.

  NameVertexMap::iterator pos;
  bool inserted;
  Vertex u, v;

  tokenizer<>::iterator i = line_toks.begin();
  std::string actors_name = *i++;

  tie(pos, inserted) = actors.insert(std::make_pair(actors_name, Vertex()));
  if (inserted) {
    u = add_vertex(g);
    actor_name[u] = actors_name;
    pos->second = u;
  } else
    u = pos->second;

The second token is the name of the movie, and the third token is the other actor. We use the same technique as above to insert the second actor into the graph.

  std::string movie_name = *i++;

  tie(pos, inserted) = actors.insert(std::make_pair(*i, Vertex()));
  if (inserted) {
    v = add_vertex(g);
    actor_name[v] = *i;
    pos->second = v;
  } else
    v = pos->second;

The final step is to add an edge connecting the two actors, and record the name of the connecting movie.

  graph_traits<Graph>::edge_descriptor e;
  tie(e, inserted) = add_edge(u, v, g);
  if (inserted)
    connecting_movie[e] = movie_name;

A Custom Visitor Class

We create a custom visitor class to record the Kevin Bacon numbers. The Kevin Bacon number will be the shortest distances from each actor to Kevin Bacon. This distance has also been referred to as the ``Bacon Number'' in memory of the ``Erdos Number'' used to rank mathematicians according to how many publications separate them from Paul Erdos. The shortest distance to from Kevin Bacon to each actor will follow the breadth-first tree. The BFS algorithm identifies edges that belong to the breadth-first tree and calls our visitor's tree_edge function. We record the distance to the target vertex as one plus the distance to the source vertex.

  template <typename DistanceMap>
  class bacon_number_recorder : public default_bfs_visitor
  {
  public:
    bacon_number_recorder(DistanceMap dist) : d(dist) { }

    template <typename Edge, typename Graph>
    void tree_edge(Edge e, const Graph& g) const
    {
      typename graph_traits<Graph>::vertex_descriptor
    u = source(e, g), v = target(e, g);
    d[v] = d[u] + 1;
    }
  private:
      DistanceMap d;
  };
  // Convenience function
  template <typename DistanceMap>
  bacon_number_recorder<DistanceMap>
  record_bacon_number(DistanceMap d)
  {
    return bacon_number_recorder<DistanceMap>(d);
  }

We will use a vector to store the bacon numbers.

  std::vector<int> bacon_number(num_vertices(g));

Apply the Breadth-First Search

The BFS algorithm requires a source vertex as input; of course this will be Kevin Bacon. We now call the BGL BFS algorithm, passing in the graph, source vertex, and the visitor.

  Vertex src = actors["Kevin Bacon"];
  bacon_number[src] = 0;

  breadth_first_search(g, src, visitor(record_bacon_number(&bacon_number[0])));

We can output the Bacon number for each actor simply by looping through all the vertices in the graph and use them to index into the bacon_number vector.

  graph_traits<Graph>::vertex_iterator i, end;
  for (boost::tie(i, end) = vertices(g); i != end; ++i)
    std::cout << actor_name[*i] << "'s bacon number is "
              << bacon_number[*i] << std::endl;

Note that vertex descriptor objects can not always be used as indices into vectors or arrays such as bacon_number. This is valid with the adjacency_list class with VertexList=vecS, but not with other variations of adjacency_list. A more generic way to index based on vertices is to use the ID property map (vertex_index_t) in coordination with the iterator_property_map.

Here are some excepts from the output of the program.

William Shatner's bacon number is 2
Denise Richards's bacon number is 1
Kevin Bacon's bacon number is 0
Patrick Stewart's bacon number is 2
Steve Martin's bacon number is 1
....
Copyright © 2000-2001br> Jeremy Siek, Indiana University (jsiek@osl.iu.edu) Top


Boost Graph Library: King Ordering

king_ordering

Graphs: | undirected
Properties: | color, degree
Complexity: | time: O(m2log(m)|E|) where m = max { degree(v) | v in V }
   (1)
  template <class IncidenceGraph, class OutputIterator,
            class ColorMap, class DegreeMap, class VertexIndexMap>
  OutputIterator
  king_ordering(const IncidenceGraph& g,
                typename graph_traits<Graph>::vertex_descriptor s,
                OutputIterator inverse_permutation,
                ColorMap color, DegreeMap degree, VertexIndexMap index_map);

  (2)
  template <class IncidenceGraph, class OutputIterator>
  OutputIterator
  king_ordering(const IncidenceGraph& g, OutputIterator inverse_permutation);

  template <class IncidenceGraph, class OutputItrator, class VertexIndexMap>
  OutputIterator
  king_ordering(const IncidenceGraph& g, OutputIterator inverse_permutation,
                VertexIndexMap index_map);

  template <class VertexListGraph, class OutputIterator,
            class ColorMap, class DegreeMap, class VertexIndexMap>
  OutputIterator
  king_ordering(const VertexListGraph& G, OutputIterator inverse_permutation,
                ColorMap color, DegreeMap degree, VertexIndexMap index_map);

  (3)
  template <class IncidenceGraph, class OutputIterator,
            class ColorMap, class DegreeMap, class VertexIndexMap>
  OutputIterator
  king_ordering(const IncidenceGraph& g,
            std::deque< typename
        graph_traits<Graph>::vertex_descriptor > vertex_queue,
                OutputIterator permutation,
                ColorMap color, DegreeMap degree, VertexIndexMap index_map);
The goal of the King ordering algorithm [62]is to reduce the bandwidth of a graph by reordering the indices assigned to each vertex. The King ordering algorithm works by a local minimization of the i-th bandwidths. The vertices are basically assigned a breadth-first search order, except that at each step, the adjacent vertices are placed in the queue in order of increasing pseudo-degree, where pseudo-degree is defined as the number of outgoing edges with white endpoints (vertices yet to be examined).

Version 1 of the algorithm lets the user choose the "starting vertex'', version 2 finds a good starting vertex using the pseudo-peripheral pair heuristic (among each component), while version 3 contains the starting nodes for each vertex in the deque. The choice of the "starting vertex'' can have a significant effect on the quality of the ordering.

The output of the algorithm are the vertices in the new ordering. Storing the output into a vector gives the permutation from the new ordering to the old ordering.

  inv_perm[new_index[u]] == u

Often times, it is the opposite permutation that is needed, the permutation from the old index to the new index. This can easily be computed in the following way.

  for (size_type i = 0; i != inv_perm.size(); ++i)
    perm[old_index[inv_perm[i]]] = i;

Parameters

For version 1:
  • IncidenceGraph& g  (IN)
    An undirected graph. The graph's type must be a model of a href="IncidenceGraph.html">IncidenceGraph..
    Python: The parameter is named graph..
  • vertex_descriptor s  (IN)
    The starting vertex.
    Python: Unsupported parameter.
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the a href="http://www.sgi.com/tech/stl/OutputIterator.html">output iterator in their new order.
    Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list..
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
    Python: Unsupported parameter.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
    Python: Unsupported parameter.
FFor version 2:
  • VertexListGraph& g  (IN)
    An undirected graph. The graph's type must be a model of a href="VertexListGraph.html">VertexListGraph..
    Python: The name of this parameter is graph..
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
    Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list..
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
    Python: Unsupported parameter.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
    Python: Unsupported parameter.
FFor version 3:
  • IncidenceGraph& g  (IN)
    An undirected graph. The graph's type must be a model of a href="IncidenceGraph.html">IncidenceGraph..
    Python: The parameter is named graph.
  • std::deque< typename graph_traits<Graph>::vertex_descriptor > vertex_queue  (IN)
    The deque containing the starting vertices for each component.
    Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list.
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the a     href="http://www.sgi.com/tech/stl/OutputIterator.html">output iterator in their new order.
     Python: This parameter is unused in Python. The new vertex ordering is returned as a Python list.
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
    Python: Unsupported parameter.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
    Python: Unsupported parameter.

Example

See example/king_ordering.cpp.

See Also

bandwidth,, and degree_property_map in boost/graph/properties.hpp.
Copyright © 2000-2001br> Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Known Problems

  1. The subgraph adaptor has several known problems:
    • Each instance of subgraph has its own copy of internal vertex and edge properties. Only at the root subgraph are the properties valid.
    • Edge and vertex removal functions are unimplemented.
    • The graph is required to have vertex descriptors of integral type (that are the same as the vertex index). Practically speaking, that means subgraph only applies to adjacency lists with VertexListS = vecS.
  2. The EdgeListS parameter to the adjacency list is essentially fixed to listS: other selectors are unlikely to work.
  3. Using a GraphProperty with adjacency_list may cause a VC++ internal compiler error.
  4. Using get(property, graph, edge) may cause a VC++ internal compiler error.
  5. "using boost::tie;" may cause VC++ internal compiler error.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Kruskal Minimum Spanning Tree

kruskal_minimum_spanning_tree

template <class Graph, class OutputIterator, class P, class T, class R>
OutputIterator
kruskal_minimum_spanning_tree(Graph& g, OutputIterator tree_edges,
    const bgl_named_params<P, T, R>& params = all defaults);

The kruskal_minimum_spanning_tree() function find a minimum spanning tree (MST) in an undirected graph with weighted edges. A MST is a set of edges that connects all the vertices in the graph where the total weight of the edges in the tree is minimized. For more details, see section Minimum Spanning Tree Problem. The edges in the MST are output to the tree_edges output iterator. This function uses Kruskal's algorithm to compute the MST [18,8,27,15].

Kruskal's algorithm starts with each vertex in a tree by itself, and with no edges in the minimum spanning tree T. The algorithm then examines each edge in the graph in order of increasing edge weight. If an edge connects two vertices in different trees the algorithm merges the two trees into a single tree and adds the edge to T. We use the ``union by rank'' and ``path compression'' heuristics to provide fast implementations of the disjoint set operations (MAKE-SET, FIND-SET, and UNION-SET). The algorithm is as follows:

KRUSKAL-MST(G, w)
  T := Ø
  for each vertex u in V
    MAKE-SET(DS, u)
  end for
  for each edge (u,v) in E in order of nondecreasing weight
    if FIND-SET(DS, u) != FIND-SET(DS, v)
      UNION-SET(DS, u, v)
      T := T U {(u,v)}
  end for
  return T

Where Defined

boost/graph/kruskal_min_spanning_tree.hpp

Parameters

IN: const Graph& g
An undirected graph. The graph type must be a model of Vertex List Graph and Edge List Graph.
Python: The parameter is named graph.
IN: OutputIterator spanning_tree_edges
The edges of the minimum spanning tree are output to this Output Iterator.
Python: This parameter is not used in Python. Instead, a Python list containing all of the spanning tree edges is returned.

Named Parameters

IN: weight_map(WeightMap w_map)
The weight or ``length'' of each edge in the graph. The WeightMap type must be a model of Readable Property Map and its value type must be Less Than Comparable. The key type of this map needs to be the graph's edge descriptor type.
Default: get(edge_weight, g)
Python: Must be an edge_double_map for the graph.
Python default: graph.get_edge_double_map("weight")
UTIL: rank_map(RankMap r_map)
This is used by the disjoint sets data structure. The type RankMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the rank map. The value type of the rank map must be an integer type.
Default: an iterator_property_map created from a std::vector of the integers of size num_vertices(g) and using the i_map for the index map.
Python: Unsupported parameter.
UTIL: predecessor_map(PredecessorMap p_map)
This is used by the disjoint sets data structure, and is not used for storing predecessors in the spanning tree. The predecessors of the spanning tree can be obtained from the spanning tree edges output. The type PredecessorMap must be a model of Read/Write Property Map. The key type value types of the predecessor map must be the vertex descriptor type of the graph.
Default: an iterator_property_map created from a std::vector of vertex descriptors of size num_vertices(g) and using the i_map for the index map.
Python: Unsupported parameter.
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This is only necessary if the default is used for the rank or predecessor maps. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note: if this is used as default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity is O(E log E).

Example

The file examples/kruskal-example.cpp contains an example of using Kruskal's algorithm.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Struct template layout_tolerance

boost::layout_tolerance — Determines when to terminate layout of a particular graph based on a given relative tolerance.

Synopsis

template<typename T = double>
struct layout_tolerance {
  // construct/copy/destruct
  layout_tolerance(const T & = T(0.001));

  // public member functions
  template<typename Graph>
    bool operator()(T,
                    typename boost::graph_traits< Graph >::vertex_descriptor,
                    const Graph &, bool) ;
};

Where Defined

boost/graph/kamada_kawai_spring_layout.hpp

layout_tolerance construct/copy/destruct

  1. layout_tolerance(const T & tolerance = T(0.001));

layout_tolerance public member functions

  1. template<typename Graph>
  bool operator()(T delta_p,
                  typename boost::graph_traits< Graph >::vertex_descriptor p,
                  const Graph & g, bool global) ;


Boost Graph Library: Converting Existing Graphs to BGL

How to Convert Existing Graphs to BGL

Though the main goal of BGL is to aid the development of new applications and graph algorithms, there are quite a few existing codes that could benefit from using BGL algorithms. One way to use the BGL algorithms with existing graph data structures is to copy data from the older graph format into a BGL graph which could then be used in the BGL algorithms. The problem with this approach is that it can be expensive to make this copy. Another approach is to wrap the existing graph data structure with a BGL interface.

The Adaptor pattern [12] typically requires that the adaptee object be contained inside a new class that provides the desired interface. This containment is not required when wrapping a graph for BGL because the BGL graph interface consists solely of free (global) functions. Instead of creating a new graph class, overload all the free functions required by the interface. This is called free function wrapper approach external adaptation.

One of the more commonly used graph classes is the LEDA parameterized GRAPH class [22]. In this section we will show how to create a BGL interface for this class. The first question is which BGL interfaces (or concepts) we should implement. The following concepts are straightforward and easy to implement on top of LEDA: VertexListGraph, BidirectionalGraph, and MutableGraph.

All types associated with a BGL graph class are accessed though the graph_traits class. We can partially specialize this traits class for the LEDA GRAPH class in the following way. The node and edge are the LEDA types for representing vertices and edges. The LEDA GRAPH is for directed graphs, so we choose directed_tag for the directed_category. The LEDA GRAPH does not automatically prevent the insertion of parallel edges, so we choose allow_parallel_edge_tag for the edge_parallel_category. The return type for the LEDA function number_of_nodes() and number_of_edges() is int, so we choose that type for the vertices_size_type and edges_size_type o f the graph. The iterator types will be more complicated, so we leave that for later.

namespace boost {
  template <class vtype, class etype>
  struct graph_traits< GRAPH<vtype,etype> > {
    typedef node vertex_descriptor;
    typedef edge edge_descriptor;

    // iterator typedefs...

    typedef directed_tag directed_category;
    typedef allow_parallel_edge_tag edge_parallel_category;
    typedef int vertices_size_type;
    typedef int edges_size_type;
  };
} // namespace boost

First we will write the source() and target() functions of the IncidenceGraph concept, which is part of the VertexListGraph concept. We use the LEDA GRAPH type for the graph parameter, and use graph_traits to specify the edge parameter and the vertex return type. We could also just use the LEDA types node and edge here, but it is better practice to always use graph_traits. If there is a need to change the associated vertex or edge type, there is need to do it in one place, inside the specialization of graph_traits instead of throughout the code. LEDA provides source() and target() functions, so merely call them.

namespace boost {
  template <class vtype, class etype>
  typename graph_traits< GRAPH<vtype,etype> >::vertex_descriptor
  source(
    typename graph_traits< GRAPH<vtype,etype> >::edge_descriptor e,
    const GRAPH<vtype,etype>& g)
  {
    return source(e);
  }

  template <class vtype, class etype>
  typename graph_traits< GRAPH<vtype,etype> >::vertex_descriptor
  target(
    typename graph_traits< GRAPH<vtype,etype> >::edge_descriptor e,
    const GRAPH<vtype,etype>& g)
  {
    return target(e);
  }
} // namespace boost

The next function from IncidenceGraph that we need to implement is out_edges(). This function returns a pair of out-edge iterators. Since LEDA does not use STL-style iterators we need to implement some. There is a very handy boost utility for implementing iterators, called iterator_adaptor. Writing a standard conforming iterator classes is actually quite difficult. With the iterator_adaptor class, just implement a policy class and the rest is taken care of. The following code is the policy class for the out-edge iterator. In LEDA, the edge object itself is used like an iterator. It has functions Succ_Adj_Edge() and Pred_Adj_Edge() to move to the next and previous (successor and predecessor) edge. One gotcha in using the LEDA edge as an iterator comes up in the dereference() function, which merely returns the edge object. The dereference operator for standard compliant iterators must be a const member function, which is why the edge argument is const. However, the return type should not be const, hence the need for const_cast.

namespace boost {
  struct out_edge_iterator_policies
  {
    static void increment(edge& e)
    { e = Succ_Adj_Edge(e,0); }

    static void decrement(edge& e)
    { e = Pred_Adj_Edge(e,0); }

    template <class Reference>
    static Reference dereference(type<Reference>, const edge& e)
    { return const_cast<Reference>(e); }

    static bool equal(const edge& x, const edge& y)
    { return x == y; }
  };
} // namespace boost

Now we go back to thee graph_traits class, and useeiterator_adaptor to fill in theeout_edge_iterator. We use the iterator

  namespace boost {
    template <class vtype, class etype>
    struct graph_traits< GRAPH<vtype,etype> > {
      // ...
      typedef iterator_adaptor<edge,
        out_edge_iterator_policies,
        iterator<std::bidirectional_iterator_tag,edge>
      > out_edge_iterator;
      // ...
    };
  } // namespace boost

With thee out_edge_iterator defined in graph_traits we are ready to define the out_edges() function. The following definition looks complicated at first glance, but it is really quite simple. The return type should be a pair of out-edge iterators, so we use std::pair and then graph_traits to access the out-edge iterator types. In the body of the function we construct the out-edge iterators by passing in the first adjacenct edge for the begin iterator, and 0 for the end iterator (which is used in LEDA as the end sentinel). The 0 argument to First_Adj_Edge tells LEDA we want out-edges (and not in-edges).

namespace boost {
  template <class vtype, class etype>
  inline std::pair<
    typename graph_traits< GRAPH<vtype,etype> >::out_edge_iterator,
    typename graph_traits< GRAPH<vtype,etype> >::out_edge_iterator >
  out_edges(
    typename graph_traits< GRAPH<vtype,etype> >::vertex_descriptor u,
    const GRAPH<vtype,etype>& g)
  {
    typedef typename graph_traits< GRAPH<vtype,etype> >
      ::out_edge_iterator Iter;
    return std::make_pair( Iter(First_Adj_Edge(u,0)), Iter(0) );
  }
} // namespace boost

The rest of the iterator types and interface functions are constructed using the same techniques as above. The complete code for the LEDA wrapper interface is inboost/graph/leda_graph.hpp. In the following code we use the BGL concept checks to make sure that we correctly implemented the BGL interface. These checks do not test the run-time behaviour of the implementation; that is tested in test/graph.cpp.

  #include <boost/graph/graph_concepts.hpp>
  #include <boost/graph/leda_graph.hpp>

  int
  main(int,char*[])
  {
    typedef GRAPH<int,int> Graph;
    function_requires< VertexListGraphConcept<Graph> >();
    function_requires< BidirectionalGraphConcept<Graph> >();
    function_requires< MutableGraphConcept<Graph> >();
    return 0;
  }
Copyright © 2000-20011
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Lengauer-Tarjan Dominator Tree Algorithm

lengauer_tarjan_dominator_tree

// The simplest version:
// Data structures for depth first search is created internally,
// and depth first search runs internally.
template <class Graph, class DomTreePredMap>
void
lengauer_tarjan_dominator_tree
  (const Graph& g,
   const typename graph_traits<Graph>::vertex_descriptor& entry,
   DomTreePredMap domTreePredMap)

// The version providing data structures for depth first search:
// After calling this function,
// user can reuse the depth first search related information
// filled in property maps.
template <class Graph, class IndexMap, class TimeMap, class PredMap,
             class VertexVector, class DomTreePredMap>
void
lengauer_tarjan_dominator_tree
  (const Graph& g,
   const typename graph_traits<Graph>::vertex_descriptor& entry,
   const IndexMap& indexMap,
   TimeMap dfnumMap, PredMap parentMap, VertexVector& verticesByDFNum,
   DomTreePredMap domTreePredMap)

// The version without depth first search:
// The caller should provide depth first search related information
// evaluated before.
template <class Graph, class IndexMap, class TimeMap, class PredMap,
             class VertexVector, class DomTreePredMap>
void
lengauer_tarjan_dominator_tree_without_dfs(
  (const Graph& g,
   const typename graph_traits<Graph>::vertex_descriptor& entry,
   const IndexMap& indexMap,
   TimeMap dfnumMap, PredMap parentMap, VertexVector& verticesByDFNum,
   DomTreePredMap domTreePredMap)

This algorithm [65,66,67] builds the dominator tree for directed graph. There are three options for dealing the depth first search related values. The simplest version creates data structures and run depth first search internally. However, chances are that a user wants to reuse the depth first search data, so we have two versions.

A vertex u dominates a vertex v, if every path of directed graph from the entry to v must go through u. In the left graph of Figure 1, vertex 1 dominates vertex 2, 3, 4, 5, 6 and 7, because we have to pass vertex 1 to reach those vertex. Note that vertex 4 dominates vertex 6, even though vertex 4 is a successor of vertex 6. Dominator relationship is useful in many applications especially for compiler optimization. We can define the immediate dominator for each vertex such that idom(n) dominates n but does not dominate any other dominator of n. For example, vertex 1, 3 and 4 are dominators of vertex 6, but vertex 4 is the immediate dominator, because vertex 1 and 3 dominates vertex 4. If we make every idom of each vertex as its parent, we can build the dominator tree like the right part of Figure 1

Figure 1: An example graph and its dominator tree.

An easy way to build dominator tree is to use iterative bit vector algorithm, but it may be slow in the worst case. We implemented Lengauer-Tarjan algorithm whose time complexity is O((V+E)log(V+E)).

Lengauer-Tarjan algorithm utilizes two techniques. The first one is, as an intermediate step, finding semidominator which is relatively easier to evaluate than immediate dominator, and the second one is the path compression. For the detail of the algorithm, see [65].

Where Defined

boost/graph/dominator_tree.hpp

Parameters

IN: const Graph& g
The graph object on which the algorithm will be applied. The type Graph must be a model of Vertex List Graph and Bidirectional Graph.
IN: vertex_descriptor entry
The entry vertex. The dominator tree will be rooted at this vertex.
IN: IndexMap indexMap
This maps each vertex to an integer in the range [0, num_vertices(g)). The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
IN/OUT: TimeMap dfnumMap
The sequence number of depth first search. The type TimeMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the TimeMap.
IN/OUT: PredMap parentMap
The predecessor map records the parent of the depth first search tree. The PredMap type must be a Read/Write Property Map whose key and value types are the same as the vertex descriptor type of the graph.
IN/OUT: VertexVector verticesByDFNum
The vector containing vertices in depth first search order. If we access this vector sequentially, it's equivalent to access vertices by depth first search order.
OUT: DomTreePredMap domTreePredMap
The dominator tree where parents are each children's immediate dominator.

Complexity

The time complexity is O((V+E)log(V+E)).

Example

See test/dominator_tree_test.cpp for an example of using Dijkstra's algorithm.

Copyright © 2005
JongSoo Park, Stanford University 		Top


LvaluePropertyMap

The LvaluePropertyMap provides an interface for accessing a reference to a property object (instead of a copy of the object as in the get() function required by ReadablePropertyMap). An LvaluePropertyMap can be mutable or non-mutable. The mutable LvaluePropertyMap returns a reference whereas the non-mutable returns a const reference.

Refinement of

ReadablePropertyMap for non-mutable or ReadWritePropertyMap for mutable property map.

Notation

PMap | A type that is a model of LvaluePropertyMap.
pmap | An object of type PMap.
key | An object of type boost::property_traits<PMap>::key_type.

Associated Types

Reference Type | boost::property_traits<PMap>::reference | The reference type, which must be a reference or const reference to the value typeof the property map.
Property Map Category | boost::property_traits<PMap>::category | The category of the property: a type convertible to boost::lvalue_property_map_tag.

Valid Expressions

Name | Expression | Return Type | Description
Access Property Value | pmap[key] | value_type& for mutable, const value_type& otherwise. | Obtain a reference to the property identified by key.

Concept Checking Class

  template <class PMap, class Key>
  struct LvaluePropertyMapConcept
  {
    typedef typename property_traits<PMap>::category Category;
    typedef boost::lvalue_property_map_tag LvalueTag;
    typedef const typename property_traits<PMap>::value_type& const_reference;
    void constraints() {
      function_requires< ReadWritePropertyMapConcept<PMap, Key> >();
      function_requires< ConvertibleConcept<Category, LvalueTag> >();

      const_reference ref = pmap[k];
    }
    PMap pmap;
    Key k;
  };

  template <class PMap, class Key>
  struct Mutable_LvaluePropertyMapConcept
  {
    typedef typename property_traits<PMap>::category Category;
    typedef boost::lvalue_property_map_tag LvalueTag;
    typedef typename property_traits<PMap>::value_type& reference;
    void constraints() {
      function_requires< ReadWritePropertyMapConcept<PMap, Key> >();
      function_requires<ConvertibleConcept<Category, LvalueTag> >();

      reference ref = pmap[k];
    }
    PMap pmap;
    Key k;
  };

See Also

Property map concepts

Copyright © 2000
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)


Lvalue Property Map

Name

LvaluePropertyMap:Provides an interface for accessing a reference to a property object (instead of a copy of the object as in the get() function required by ReadablePropertyMap).

Synopsis

template <class PMap, class Key>
  struct LvaluePropertyMapConcept
  {
    typedef typename property_traits<PMap>::category Category;
    typedef boost::lvalue_property_map_tag LvalueTag;
    typedef const typename property_traits<PMap>::value_type& const_reference;
    void constraints() {
      function_requires< ReadWritePropertyMapConcept<PMap, Key> >();
      function_requires< ConvertibleConcept<Category, LvalueTag> >();

      const_reference ref = pmap[k];
    }
    PMap pmap;
    Key k;
  };

  template <class PMap, class Key>
  struct Mutable_LvaluePropertyMapConcept
  {
    typedef typename property_traits<PMap>::category Category;
    typedef boost::lvalue_property_map_tag LvalueTag;
    typedef typename property_traits<PMap>::value_type& reference;
    void constraints() {
      function_requires< ReadWritePropertyMapConcept<PMap, Key> >();
      function_requires<ConvertibleConcept<Category, LvalueTag> >();

      reference ref = pmap[k];
    }
    PMap pmap;
    Key k;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Graph Library: Maximum Cardinality Matching

Maximum Cardinality Matching

template <typename Graph, typename MateMap>
void edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate);

template <typename Graph, typename MateMap, typename VertexIndexMap>
void edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate, VertexIndexMap vm);

template <typename Graph, typename MateMap>
bool checked_edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate);

template <typename Graph, typename MateMap, typename VertexIndexMap>
bool checked_edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate, VertexIndexMap vm);

A matching is a subset of the edges of a graph such that no two edges share a common vertex. Two different matchings in the same graph are illustrated below (edges in the matching are colored blue.) The matching on the left is a maximal matching, meaning that its size can't be increased by adding edges. The matching on the right is a maximum cardinality matching, meaning that is has maximum size over all matchings in the graph.

Both edmonds_maximum_cardinality_matching and checked_edmonds_maximum_cardinality_matching find the maximum cardinality matching in any undirected graph. The matching is returned in a MateMap, which is a ReadWritePropertyMap that maps vertices to vertices. In the mapping returned, each vertex is either mapped to the vertex it's matched to, or to graph_traits<Graph>::null_vertex() if it doesn't participate in the matching. If no VertexIndexMap is provided, both functions assume that the VertexIndexMap is provided as an internal graph property accessible by calling get(vertex_index,g). The only difference between edmonds_maximum_cardinality_matching and checked_edmonds_maximum_cardinality_matching is that as a final step, the latter algorithm runs a simple verification on the matching computed and returns true if and only if the matching is indeed a maximum cardinality matching.

Given a matching M, any vertex that isn't covered by an edge in M is called free. Any simple path containing exactly 2n + 1 edges that starts and ends at free vertices and contains n edges from M is called an alternating path. Given an alternating path p, all matching and non-matching edges on p can be swapped, resulting in a new matching that's larger than the original matching by exactly one edge. This method of incrementally increasing the size of matching, along with the following fact, forms the basis of Edmonds' matching algorithm:

An alternating path through the matching M exists if and only if M is not a maximum cardinality matching.
The difficult part is, of course, finding an augmenting path whenever one exists. The algorithm we use for finding a maximum cardinality matching consists of three basic steps:
  1. Create an initial matching.
  2. Repeatedly find an augmenting path and use it to increase the size of the matching until no augmenting path exists.
  3. Verify that the matching found is a maximum cardinality matching.
If checked_edmonds_maximum_cardinality_matching or edmonds_maximum_cardinality_matching is used, all three of these steps are chosen , but it's easy to plug in different algorithms for these three steps using a generic matching function discussed below - in fact, both checked_edmonds_maximum_cardinality_matching and edmonds_maximum_cardinality_matching are just inlined specializations of this function.

When quoting time bounds for algorithms, assume that VertexIndexMap is a property map that allows for constant-time mapping between vertices and indices (which is easily achieved if, for instance, the vertices are stored in contiguous memory.) se n and m to represent the size of the vertex and edge sets, respectively, of the input graph.

Algorithms for Creating an Initial Matching

  • empty_matching: Takes time O(n) to initialize the empty matching.
  • greedy_matching: The matching obtained by iterating through the edges and adding an edge if it doesn't conflict with the edges already in the matching. This matching is maximal, and is therefore guaranteed to contain at least half of the edges that a maximum matching has. Takes time O(m log n).
  • extra_greedy_matching: Sorts the edges in increasing order of the degree of the vertices contained in each edge, then constructs a greedy matching from those edges. Also a maximal matching, and can sometimes be much closer to the maximum cardinality matching than a simple greedy_matching. Takes time O(m log n), but the constants involved make this a slower algorithm than greedy_matching.

Algorithms for Finding an Augmenting Path

  • edmonds_augmenting_path_finder: Finds an augmenting path in time O(m alpha(m,n)), where alpha(m,n) is an inverse of the Ackerman function. alpha(m,n) is one of the slowest growing functions that occurs naturally in computer science; essentially, alpha(m,n) ≤ 4 for any graph that we'd ever hope to run this algorithm on. Since we arrive at a maximum cardinality matching after augmenting O(n) matchings, the entire algorithm takes time O(mn alpha(m,n)). Edmonds' original algorithm appeared in [64], but our implementation of Edmonds' algorithm closely follows Tarjan's description of the algorithm from [27].
  • no_augmenting_path_finder: Can be used if no augmentation of the initial matching is desired.

Verification Algorithms

  • maximum_cardinality_matching_verifier: Returns true if and only if the matching found is a maximum cardinality matching. Takes time O(m alpha(m,n)), which is on the order of a single iteration of Edmonds' algorithm.
  • no_matching_verifier: Always returns true
Why is a verification algorithm needed? Edmonds' algorithm is fairly complex, and it's nearly impossible for a human without a few days of spare time to figure out if the matching produced by edmonds_matching on a graph with, say, 100 vertices and 500 edges is indeed a maximum cardinality matching. A verification algorithm can do this mechanically, and it's much easier to verify by inspection that the verification algorithm has been implemented correctly than it is to verify by inspection that Edmonds' algorithm has been implemented correctly. The Boost Graph library makes it incredibly simple to perform the subroutines needed by the verifier (such as finding all the connected components of odd cardinality in a graph, or creating the induced graph on all vertices with a certain label) in just a few lines of code.

Understanding how the verifier works requires a few graph-theoretic facts. Let m(G) be the size of a maximum cardinality matching in the graph G. Denote by o(G) the number of connected components in G of odd cardinality, and for a set of vertices X, denote by G - X the induced graph on the vertex set V(G) - X. Then the Tutte-Berge Formula says that

2 * m(G) = min ( |V(G)| + |X| - o(G-X) )
Where the minimum is taken over all subsets X of the vertex set V(G). A side effect of the Edmonds Blossom-Shrinking algorithm is that it computes what is known as the Edmonds-Gallai decomposition of a graph: it decomposes the graph into three disjoint sets of vertices, one of which achieves the minimum in the Tutte-Berge Formula. An outline of our verification procedure is: Given a Graph g and MateMap mate,
  1. Check to make sure that mate is a valid matching on g.
  2. Run edmonds_augmenting_path_finder once on g and mate. If it finds an augmenting path, the matching isn't a maximum cardinality matching. Otherwise, we retrieve a copy of the vertex_state map used by the edmonds_augmenting_path_finder. The Edmonds-Gallai decomposition tells us that the set of vertices labeled V_ODD by the vertex_state map can be used as the set X to achieve the minimum in the Tutte-Berge Formula.
  3. Count the number of vertices labeled V_ODD, store this in num_odd_vertices.
  4. Create a filtered_graph consisting of all vertices that aren't labeled V_ODD. Count the number of odd connected components in this graph and store the result in num_odd_connected_components.
  5. Test to see if equality holds in the Tutte-Berge formula using |X| = num_odd_vertices and o(G-X) = num_odd_connected_components. Return true if it holds, false otherwise.
Assuming these steps are implemented correctly, the verifier will never return a false positive, and will only return a false negative if edmonds_augmenting_path_finder doesn't compute the vertex_state map correctly, in which case the edmonds_augmenting_path_finder isn't working correctly.

Creating Own Matching Algorithms

Creating a matching algorithm is as simple as plugging the algorithms described above into a generic matching function, which has the following signature:
template <typename Graph,
      typename MateMap,
      typename VertexIndexMap,
      template <typename, typename, typename> class AugmentingPathFinder,
      template <typename, typename> class InitialMatchingFinder,
      template <typename, typename, typename> class MatchingVerifier>
  bool matching(const Graph& g, MateMap mate, VertexIndexMap vm)
The matching functions provided are just inlined specializations of this function: edmonds_maximum_cardinality_matching uses edmonds_augmenting_path_finder as the AugmentingPathFinder, extra_greedy_matching as the InitialMatchingFinder, and no_matching_verifier as the MatchingVerifier. checked_edmonds_maximum_cardinality_matching uses the same parameters except that maximum_cardinality_matching_verifier is used for the MatchingVerifier.

These aren't necessarily the best choices for any situation - for example, it's been claimed in the literature that for sparse graphs, Edmonds' algorithm converges to the maximum cardinality matching more quickly if it isn't supplied with an intitial matching. Such an algorithm can be easily assembled by calling matching with

  • AugmentingPathFinder = edmonds_augmenting_path_finder
  • InitialMatchingFinder = empty_matching
and choosing the MatchingVerifier depending on how careful the user is feeling.

Suppose instead that the user wants a relatively large matching quickly, but are not exactly interested in a maximum matching. Both extra_greedy_matching and greedy_matching find maximal matchings, which means they're guaranteed to be at least half the size of a maximum cardinality matching, so matching could be called with

  • AugmentingPathFinder = no_augmenting_path_finder
  • InitialMatchingFinder = extra_greedy_matching
  • MatchingVerifier = maximum_cardinality_matching_verifier
The resulting algorithm will find an extra greedy matching in time O(m log n) without looking for augmenting paths. As a bonus, the return value of this function is true if and only if the extra greedy matching happens to be a maximum cardinality matching.

Where Defined

boost/graph/max_cardinality_matching.hpp

Parameters

IN: const Graph& g
An undirected graph. The graph type must be a model of Vertex and Edge List Graph and Incidence Graph.
IN: VertexIndexMap vm
Must be a model of ReadablePropertyMap, mapping vertices to integer indices.
OUT: MateMap mate
Must be a model of ReadWritePropertyMap, mapping vertices to vertices. For any vertex v in the graph, get(mate,v) will be the vertex that v is matched to, or graph_traits::null_vertex() if v isn't matched.

Complexity

Let m and n be the number of edges and vertices in the input graph, respectively. Assuming the VertexIndexMap supplied allows constant-time lookups, the time complexity for both edmonds_matching and checked_edmonds_matching is O(mn alpha(m,n)). alpha(m,n) is a slow growing function that is at most 4 for any feasible input.

Example

The file example/matching_example.cpp contains an example.

Copyright © 2005
Aaron Windsor (aaron.windsor@gmail.com) 		Top


Boost Graph Library: Minimum Degree Ordering

minimum_degree_ordering

  template <class AdjacencyGraph, class OutDegreeMap,
           class InversePermutationMap,
           class PermutationMap, class SuperNodeSizeMap, class VertexIndexMap>
  void minimum_degree_ordering
    (AdjacencyGraph& G,
     OutDegreeMap outdegree,
     InversePermutationMap inverse_perm,
     PermutationMap perm,
     SuperNodeSizeMap supernode_size, int delta, VertexIndexMap id)

The minimum degree ordering algorithm [21, 35] is a fill-in reduction matrix reordering algorithm. When solving a system of equations such as A x = b using a sparse version of Cholesky factorization (which is a variant of Gaussian elimination for symmetric matrices), often times elements in the matrix that were once zero become non-zero due to the elimination process. This is what is referred to as "fill-in", and fill-in is bad because it makes the matrix less sparse and therefore consume more time and space in later stages of the elimintation and in computations that use the resulting factorization. Now it turns out that reordering the rows of a matrix can have a dramatic affect on the amount of fill-in that occurs. So instead of solving A x = b, we instead solve the reordered (but equivalent) system (P A PT)(P x) = P b. Finding the optimal ordering is an NP-complete problem (e.i., it can not be solved in a reasonable amount of time) so instead we just find an ordering that is "good enough" using heuristics. The minimum degree ordering algorithm is one such approach.

A symmetric matrix A is typically represented with an undirected graph, however for this function we require the input to be a directed graph. Each nonzero matrix entry A(i, j) must be represented by two directed edges (e(i,j) and e(j,i)) in G.

The output of the algorithm are the vertices in the new ordering.

  inverse_perm[new_index[u]] == old_index[u]

and the permutation from the old index to the new index.

  perm[old_index[u]] == new_index[u]

The following equations are always held.

  for (size_type i = 0; i != inverse_perm.size(); ++i)
    perm[inverse_perm[i]] == i;

Parameters

  • AdjacencyGraph& G  (IN)
    A directed graph. The graph's type must be a model of Adjacency Graph, Vertex List Graph, Incidence Graph, and Mutable Graph. In addition, the functions num_vertices() and out_degree() are required.
  • OutDegreeMap outdegree  (WORK)
    This is used internally to store the out degree of vertices. This must be a LvaluePropertyMap with key type the same as the vertex descriptor type of the graph, and with a value type that is an integer type.
  • InversePermutationMap inverse_perm  (OUT)
    The new vertex ordering, given as the mapping from the new indices to the old indices (an inverse permutation). This must be an LvaluePropertyMap with a value type and key type a signed integer.
  • PermutationMap perm  (OUT)
    The new vertex ordering, given as the mapping from the old indices to the new indices (a permutation). This must be an LvaluePropertyMap with a value type and key type a signed integer.
  • SuperNodeSizeMap supernode_size  (WORK/OUT)
    This is used internally to record the size of supernodes and is also useful information to have. This is a LvaluePropertyMap with an unsigned integer value type and key type of vertex descriptor.
  • int delta  (IN)
    Multiple elimination control variable. If it is larger than or equal to zero then multiple elimination is enabled. The value of delta specifies the difference between the minimum degree and the degree of vertices that are to be eliminated.
  • VertexIndexMap id  (IN)
    Used internally to map vertices to their indices. This must be a Readable Property Map with key type the same as the vertex descriptor of the graph and a value type that is some unsigned integer type.

Example

See example/minimum_degree_ordering.cpp.

Implementation Notes

Note: This section is still under construction, please check the boost website for more information.

Copyright © 2001
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Monoid

A Monoid is a concept that describes a simple kind of algebraic system. A monoid consists of a set of elements S, a binary operation, and an identity element. The C++ representation of a monoid consists of a function object that implements the binary operation, a set of objects that represent the elements of S, and an object that represents the identity element.

Refinement of

The element type must be a model of Assignable and CopyConstructible. The function object type must be a model of BinaryFunction.

Notation

X | Is the element type of the Monoid.
a, b | are objects of type X.
op | Is the function object implementing the Monoid operation.
i | is an object of type X and is the identity element for the Monoid.

Valid Expressions

op(a, b) | See below for semantics. Return type: X
a == b | Returns true if a and b represent the same element of S. Return type: bool
a != b | Returns true if a and b represent different elements of S. Return type: bool

Invariants

  • Closure
    The result of     op(a, b) is also an element of S.

  • Associativity
    op(op(a, b), c) == op(a, op(b, c))

  • Definition of Identity Element
    op(a, i) == a

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Multi-Pass Input Iterator

This concept is a refinement of Input Iterator, adding the requirements that the iterator can be used to make multiple passes through a range, and that if it1 == it2 and it1 is dereferenceable then ++it1 == ++it2. The Multi-Pass Input Iterator is very similar to the Forward Iterator. The only difference is that a Forward Iterator requires the reference type to be value_type&, whereas MultiPassInputIterator is like Input Iterator in that the reference type merely has to be convertible to value_type.

Design Notes

comments by Valentin Bonnard:

I think that introducing Multi-Pass Input Iterator isn't the right solution. Do you also want to define Multi-Pass Bidirectionnal Iterator and Multi-Pass Random Access Iterator ? I don't, definitely. It only confuses the issue. The problem lies into the existing hierarchy of iterators, which mixes movabillity, modifiability and lvalue-ness, and these are clearly independent.

The terms Forward, Bidirectional and Random Access are about movabillity and shouldn't be used to mean anything else. In a completely orthogonal way, iterators can be immutable, mutable, or neither. lvalue-ness of iterators is also orthogonal with immutability. With these clean concepts, your Multi-Pass Input Iterator is just called a Forward Iterator.

Other translations are:
std::Forward Iterator -> ForwardIterator & Lvalue Iterator
std::Bidirectionnal Iterator -> Bidirectionnal Iterator & Lvalue Iterator
std::Random Access Iterator -> Random Access Iterator & Lvalue Iterator

Note: In practice the only operation not allowed on my Forward Iterator which is allowed on std::Forward Iterator is &*it. I think that &* is rarely needed in generic code.

reply by Jeremy Siek:

The above analysis by Valentin is right on. Of course, there is the problem with backward compatibility. The current STL implementations are based on the old definition of Forward Iterator. The right course of action is to get Forward Iterator, etc. changed in the C++ standard. Once that is done we can drop Multi-Pass Input Iterator.

Copyright © 2000 Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)
Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


MutableGraph

A MutableGraph can be changed via the addition or removal of edges and vertices.

Refinement of

Graph

Notation

G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
u,v | are objects of type boost::graph_traits<G>::vertex_descriptor.
iter | is an object of type boost::graph_traits<G>::out_edge_iterator.
p | is an object of a type that models Predicate and whose argument type matches the edge_descriptor type.

Valid Expressions

add_edge(u, v, g) | Inserts the edge (u,v) into the graph, and returns an edge descriptor pointing to the new edge. If the graph disallows parallel edges, and the edge (u,v) is already in the graph, then the bool flag returned is false and the returned edge descriptor points to the already existing edge. Note: For undirected graphs, (u,v) is the same edge as (v,u), so after a call to the function add_edge(), this implies that edge (u,v) will appear in the out-edges of u and (u,v) (or equivalently (v,u)) will appear in the out-edges of v. Put another way, v will be adjacent to u and u will be adjacent to v. Return type: std::pair<edge_descriptor, bool>
remove_edge(u, v, g) | Remove the edge (u,v) from the graph. If the graph allows parallel edges this remove all occurrences of (u,v). Return type: void Precondition: u and v are vertices in the graph. Postcondition: (u,v) is no longer in the edge set for g.
remove_edge(e, g) | Remove the edge efrom the graph. Return type: void Precondition: e is an edge in the graph. Postcondition: e is no longer in the edge set for g.
remove_edge(iter, g) | Remove the edge pointed to be iter from the graph. This expression is only required when the graph also models IncidenceGraph. Return type: void Precondition: *iter is an edge in the graph. Postcondition: *iter is no longer in the edge set for g.
remove_edge_if(p, g) | Remove all the edges from graph g for which the predicate p returns true. Return type: void
remove_out_edge_if(u, p, g) | Remove all the out-edges of vertex u for which the predicate p returns true. This expression is only required when the graph also models IncidenceGraph. Return type: void
remove_in_edge_if(u, p, g) | Remove all the in-edges of vertex u for which the predicate p returns true. This expression is only required when the graph also models BidirectionalGraph. Return type: void
add_vertex(g) | Add a new vertex to the graph. The vertex_descriptor for the new vertex is returned. Return type: vertex_descriptor
clear_vertex(u, g) | Remove all edges to and from vertex u from the graph. Return type: void Precondition: u is a valid vertex descriptor of g. Postcondition: u does not appear as a source or target of any edge in g.
remove_vertex(u, g) | Remove u from the vertex set of the graph. Note that undefined behavior may result if there are edges remaining in the graph who's target is u. Typically the clear_vertex() function should be called first. Return type: void Precondition: u is a valid vertex descriptor of g. Postcondition: num_vertices(g) is one less, u no longer appears in the vertex set of the graph and it is no longer a valid vertex descriptor.

Complexity Guarantees

  • Edge insertion must be either amortized constant time or it can be O(log(E/V)) if the insertion also checks to prevent the addition of parallel edges (which is a ``feature'' of some graph types).

  • Edge removal is guaranteed to be O(E).

  • Vertex insertion is guaranteed to be amortized constant time.

  • Clearing a vertex is O(E + V).

  • Vertex removal is O(E + V).

Models

  • adjacency_list

Concept Checking Class

  template <class G>
  struct MutableGraphConcept
  {
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    void constraints() {
      v = add_vertex(g);
      clear_vertex(v, g);
      remove_vertex(v, g);
      e_b = add_edge(u, v, g);
      remove_edge(u, v, g);
      remove_edge(e, g);
    }
    G g;
    edge_descriptor e;
    std::pair<edge_descriptor, bool> e_b;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::out_edge_iterator iter;
  };

  template <class edge_descriptor>
  struct dummy_edge_predicate {
    bool operator()(const edge_descriptor& e) const {
      return false;
    }
  };

  template <class G>
  struct MutableIncidenceGraphConcept
  {
    void constraints() {
      function_requires< MutableGraph<G> >();
      remove_edge(iter, g);
      remove_out_edge_if(u, p, g);
    }
    G g;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    dummy_edge_predicate<edge_descriptor> p;
    typename boost::graph_traits<G>::vertex_descriptor u;
    typename boost::graph_traits<G>::out_edge_iterator iter;
  };

  template <class G>
  struct MutableBidirectionalGraphConcept
  {
    void constraints() {
      function_requires< MutableIncidenceGraph<G> >();
      remove_in_edge_if(u, p, g);
    }
    G g;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    dummy_edge_predicate<edge_descriptor> p;
    typename boost::graph_traits<G>::vertex_descriptor u;
  };

  template <class G>
  struct MutableEdgeListGraphConcept
  {
    void constraints() {
      function_requires< MutableGraph<G> >();
      remove_edge_if(p, g);
    }
    G g;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    dummy_edge_predicate<edge_descriptor> p;
  };

Copyright © 2000-2001 Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


MutableGraph

Name

MutableGraph: This can be changed via the addition or removal of edges and vertices.

Synopsis

template <class G>
  struct MutableGraphConcept
  {
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    void constraints() {
      v = add_vertex(g);
      clear_vertex(v, g);
      remove_vertex(v, g);
      e_b = add_edge(u, v, g);
      remove_edge(u, v, g);
      remove_edge(e, g);
    }
    G g;
    edge_descriptor e;
    std::pair<edge_descriptor, bool> e_b;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::out_edge_iterator iter;
  };

  template <class edge_descriptor>
  struct dummy_edge_predicate {
    bool operator()(const edge_descriptor& e) const {
      return false;
    }
  };

  template <class G>
  struct MutableIncidenceGraphConcept
  {
    void constraints() {
      function_requires< MutableGraph<G> >();
      remove_edge(iter, g);
      remove_out_edge_if(u, p, g);
    }
    G g;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    dummy_edge_predicate<edge_descriptor> p;
    typename boost::graph_traits<G>::vertex_descriptor u;
    typename boost::graph_traits<G>::out_edge_iterator iter;
  };

  template <class G>
  struct MutableBidirectionalGraphConcept
  {
    void constraints() {
      function_requires< MutableIncidenceGraph<G> >();
      remove_in_edge_if(u, p, g);
    }
    G g;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    dummy_edge_predicate<edge_descriptor> p;
    typename boost::graph_traits<G>::vertex_descriptor u;
  };

  template <class G>
  struct MutableEdgeListGraphConcept
  {
    void constraints() {
      function_requires< MutableGraph<G> >();
      remove_edge_if(p, g);
    }
    G g;
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    dummy_edge_predicate<edge_descriptor> p;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


MutablePropertyGraph

A MutablePropertyGraph is a MutableGraph with properties attached internally to the vertices and edges. Give the value of the properties when adding vertices and edges.

Refinement of

MutableGraph and PropertyGraph

Notation

G | A type that is a model of Graph.
g | An object of type G.
e | An object of type boost::graph_traits<G>::edge_descriptor.
u,v | are objects of type boost::graph_traits<G>::vertex_descriptor.
ep | is an object of type G::edge_property_type
vp | is an object of type G::vertex_property_type

Associated Types

Edge Property Type | graph_traits<G>::edge_property_type
Vertex Property Type | graph_traits<G>::vertex_property_type

Valid Expressions

add_edge(u, v, ep, g) | Inserts the edge (u,v) into the graph, and copies object ep into the property for that edge. Return type: std::pair<edge_descriptor, bool>
add_vertex(vp, g) | Add a new vertex to the graph and copy vp into the property for the new vertex. The vertex_descriptor for the new vertex is returned. Return type: vertex_descriptor

Models

  • adjacency_list

Concept Checking Class

  template <class G>
  struct MutablePropertyGraphConcept
  {
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    void constraints() {
      function_requires< MutableGraphConcept<G> >();
      v = add_vertex(vp, g);
      p = add_edge(u, v, ep, g);
    }
    G g;
    std::pair<edge_descriptor, bool> p;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::vertex_property_type vp;
    typename boost::graph_traits<G>::edge_property_type ep;
  };

Copyright © 2000-2001Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


MutablePropertyGraph

Name

MutablePropertyGraph: A MutableGraph with properties attached internally to the vertices and edges.

Synopsis

template <class G>
  struct MutablePropertyGraphConcept
  {
    typedef typename boost::graph_traits<G>::edge_descriptor edge_descriptor;
    void constraints() {
      function_requires< MutableGraphConcept<G> >();
      v = add_vertex(vp, g);
      p = add_edge(u, v, ep, g);
    }
    G g;
    std::pair<edge_descriptor, bool> p;
    typename boost::graph_traits<G>::vertex_descriptor u, v;
    typename boost::graph_traits<G>::vertex_property_type vp;
    typename boost::graph_traits<G>::edge_property_type ep;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia


Boost Graph Library: null_visitor

null_visitor

null_visitor

This is an EventVisitor that does nothing.

Model of

EventVisitor

Associated Types

Type | Description
null_visitor::event_filter | This type is void

Member Functions

Member | Description
null_visitor(); | Default constructor.
template <class X, class Graph>
void operator()(X x, const Graph& g); 		This does nothing.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: opposite

opposite

Complexity: | O(1)
Where Defined: | boost/graph/graph_utility.hpp

  template <class Graph>
  typename graph_traits<Graph>::vertex_descriptor
  opposite(typename graph_traits<Graph>::edge_descriptor e,
           typename graph_traits<Graph>::vertex_descriptor v,
           const Graph& g)
Given an edge and a vertex which must be incident to the edge, this function returns the opposite vertex. So if v is the source vertex, this function returns the target vertex. If v is the target, then this function returns the source vertex.

Example

edge_descriptor e;
...
vertex_descriptor u, v;
tie(u, v) = incident(e, g);
assert(v == opposite(e, u, g));
assert(u == opposite(e, v, g));
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Power Law Out Degree (PLOD) Generator

plod_iterator

plod_iterator

template<typename RandomGenerator, typename Graph>
class plod_iterator
{
public:
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<vertices_size_type, vertices_size_type> value_type;
  typedef const value_type& reference;
  typedef const value_type* pointer;
  typedef void difference_type;

  plod_iterator();
  plod_iterator(RandomGenerator& gen, vertices_size_type n,
                double alpha, double beta, bool allow_self_loops = false);
  // Iterator operations
  reference operator*() const;
  pointer operator->() const;
  plod_iterator& operator++();
  plod_iterator operator++(int);
  bool operator==(const plod_iterator& other) const;
  bool operator!=(const plod_iterator& other) const;
};

This class template implements a generator for scale-free graphs using the Power Law Out Degree (PLOD) algorithm [63], suitable for initializing an adjacency_list or other graph structure with iterator-based initialization. A scale-free graph typically has a very skewed degree distribution, where few vertices have a very high degree and a large number of vertices have a very small degree. Many naturally evolving networks, such as the World Wide Web, are scale-free graphs, making these graphs a good model for certain networking problems.

The Power Law Out Degree (PLOD) algorithm generates a scale-free graph from three parameters, n, alpha, and beta, by allocating a certain number of degree "credits" to each vertex, drawn from a power-law distribution. It then creates edges between vertices, deducting a credit from each involved vertex (in the undirected case) or the source vertex (in the directed case). The number of credits assigned to a vertex is: beta*x-alpha, where x is a random value between 0 and n-1. The value of beta controls the y-intercept of the curve, so that increasing beta increases the average degree of vertices. The value of alpha controls how steeply the curve drops off, with larger values indicating a steeper curve. The web graph, for instance, has alpha ~ 2.72.

Where Defined

boost/graph/plod_generator.hpp

Constructors

plod_iterator();
Constructs a past-the-end iterator.
plod_iterator(RandomGenerator& gen, vertices_size_type n,
              double alpha, double beta, bool allow_self_loops = false);
Constructs a PLOD generator iterator that creates a graph with n vertices. Probabilities are drawn from the random number generator gen. Self-loops are permitted only when allow_self_loops is true.

Example

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/plod_generator.hpp>
#include <boost/random/linear_congruential.hpp>

typedef boost::adjacency_list<> Graph;
typedef boost::plod_iterator<boost::minstd_rand, Graph> SFGen;

int main()
{
  boost::minstd_rand gen;
  // Create graph with 100 nodes
  Graph g(SFGen(gen, 100, 2.5, 1000), SFGen(), 100);
  return 0;
}


Boost Graph Library: predecessor_recorder

Predecessor Recorder

predecessor_recorder<PredecessorMap, EventTag>

This is an EventVisitor that records the predecessor (or parent) of a vertex in a predecessor property map. This is particularly useful in graph search algorithms where recording the predecessors is an efficient way to encode the search tree, traversed during the search. The predecessor recorder is typically used with the on_tree_edge or on_relax_edge events, and cannot be used with vertex events.

Use predecessor_recorder with graph algorithms by wrapping it with the algorithm specific adaptor, such as bfs_visitor and dfs_visitor . Also, this event visitor can be combined with other event visitors using std::pair to form an EventVisitorList.

Algorithms such as Dijkstra's and breadth-first search will not assign a predecessor to the source vertex (which is the root of the search tree). Often times it is useful to initialize the source vertex's predecessor to itself, thereby identifying the root vertex as the only vertex which is its own parent. When using an algorithm like depth-first search that creates a forest (multiple search trees), it is useful to intialize the predecessor of every vertex to itself, so that all the root nodes can be distinguished.

Example

See the example for bfs_visitor .

Model of

EventVisitor

Where Defined

boost/graph/visitors.hpp

Template Parameters

Parameter | Description | Default
PredecessorMap | A WritablePropertyMap, where the key type and the value type are the vertex descriptor type of the graph. |
EventTag | The tag to specify when the predecessor_recorder should be applied during the graph algorithm. EventTag must be an edge event. |

Associated Types

Type | Description
predecessor_recorder::event_filter | This will be the same type as the template parameter EventTag .

Member Functions

Member | Description
predecessor_recorder(PredecessorMap pa); | Construct a predecessor recorder object with predecessor property map pa.
template <class Edge, class Graph>
void operator()(Edge e, const Graph& g); 		Given edge e = (u,v), this records u as the predecessor (or parent) of v.

Non-Member Functions

Function Description
template <class PredecessorMap, class Tag>
predecessor_recorder<PredecessorMap, Tag>
record_predecessors(PredecessorMap pa, Tag);

A convenient way to create a predecessor_recorder.

See Also

Visitor concepts

The following are other event visitors: distance_recorder, time_stamper, and property_writer.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Prim Minimum Spanning Tree

prim_minimum_spanning_tree

// named parameter version
template <class Graph, class PredMap, class P, class T, class R>
void prim_minimum_spanning_tree(const Graph& g, PredMap p_map,
  const bgl_named_params<P, T, R>& params)

// non-named parameter version
template <class Graph, class DijkstraVisitor,
      class PredecessorMap, class DistanceMap,
      class WeightMap, class IndexMap>
void prim_minimum_spanning_tree(const Graph& g,
   typename graph_traits<Graph>::vertex_descriptor s,
   PredecessorMap predecessor, DistanceMap distance, WeightMap weight,
   IndexMap index_map, DijkstraVisitor vis)

This is Prim's algorithm [25,8,27,15] for solving the minimum spanning tree problem for an undirected graph with weighted edges. A MST is a set of edges that connects all the vertices in the graph where the total weight of the edges in the tree is minimized. See Section Minimum Spanning Tree Problem for more details. The implementation is simply a call to dijkstra_shortest_paths() with the appropriate choice of comparison and combine functors. The pseudo-code for Prim's algorithm is listed below.

PRIM-MST(G, s, w)
  for each vertex u in V[G]
    color[u] := WHITE
    d[u] := infinity
  color[s] := GRAY
  d[s] := 0
  ENQUEUE(PQ, s)
  p[s] := s
  while (PQ != Ø)
    u := DEQUEUE(PQ)
    for each v in Adj[u]
      if (w(u,v) < d[v])
        d[v] := w(u,v)
        p[v] := u
        if (color[v] =  WHITE)
          ENQUEUE(PQ, v)
          color[v] := GRAY
        else if (color[v] =  GRAY)
          UPDATE(PQ, v)
      else
        do nothing
    end for
    color[u] := BLACK
  end while
  return (p, d)

initialize vertex u

start vertex s
discover vertex s

examine vertex u
examining edge (u,v)

edge (u,v) relaxed

discover vertex v

edge (u,v) not relaxed

finish u

Where Defined

boost/graph/prim_minimum_spanning_tree.hpp

Parameters

IN: const Graph& g
An undirected graph. The type Graph must be a model of Vertex List Graph and Incidence Graph.
Python: The parameter is named graph.
OUT: PredecessorMap p_map
The predecessor map records the edges in the minimum spanning tree. Upon completion of the algorithm, the edges (p[u],u) for all u in V are in the minimum spanning tree. If p[u] = u then u is either the root of the tree or is a vertex that is not reachable from the root. The PredecessorMap type must be a Read/Write Property Map with key and vertex types the same as the vertex descriptor type of the graph.
Python: Must be a vertex_vertex_map for the graph.

Named Parameters

IN: root_vertex(vertex_descriptor r)
The vertex that will be the root of the minimum spanning tree. The choice of the root vertex is arbitrary.
Default: *vertices(g).first
IN: weight_map(WeightMap w_map)
The weight or "length'' of each edge in the graph. The type WeightMap must be a model of Readable Property Map. The edge descriptor type of the graph needs to be usable as the key type for the weight map. The value type for the map must be Addable with the value type of the distance map.
Default: get(edge_weight, g)
Python: Must be an edge_double_map for the graph.
Python default: graph.get_edge_double_map("weight")
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This is necessary for efficient updates of the heap data structure when an edge is relaxed. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note: if this is used as default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.
UTIL/OUT: distance_map(DistanceMap d_map)
The shortest path weight from the source vertex s to each vertex in the graph g is recorded in this property map. The shortest path weight is the sum of the edge weights along the shortest path. The type DistanceMap must be a model of Read/Write Property Map. The vertex descriptor type of the graph needs to be usable as the key type of the distance map. The value type of the distance map must be Less Than Comparable.
Default: iterator_property_map created from a std::vector of the WeightMap's value type of size num_vertices(g) and using the i_map for the index map.
Python: Must be a vertex_double_map for the graph.
UTIL/OUT: color_map(ColorMap c_map)
This is used during the execution of the algorithm to mark the vertices. The vertices start out white and become gray when they are inserted in the queue. They then turn black when they are removed from the queue. At the end of the algorithm, vertices reachable from the source vertex will have been colored black. All other vertices will still be white. The type ColorMap must be a model of Read/Write Property Map. A vertex descriptor must be usable as the key type of the map, and the value type of the map must be a model of Color Value.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map f or the graph.
OUT: visitor(DijkstraVisitor v)
Use this to specify actions that the user would like to happen during certain event points within the algorithm. The type DijkstraVisitor must be a model of the Dijkstra Visitor concept. The visitor object is passed by value [1].
Default: dijkstra_visitor<null_visitor>
Python: The parameter should be an object that derives from the DijkstraVisitor type of the graph.

Complexity

The time complexity is O(E log V).

Example

The file examples/prim-example.cpp contains an example of using Prim's algorithm.

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore the user may want the visitor to hold this state by pointer or reference.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Boost Graph Library: Bandwidth

profile

  (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  profile(const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  profile(const Graph& g, VertexIndexMap index_map)

The profile is the sum of all the maximum distances between the i-th vertex and any of its neighbors with an index j>i.

B(G) = max { |index[u] - index[v]|  | (u,v) in E }

Defined in

boost/graph/profile.hpp
Copyright © 2001-2002
Marc Wintermantel, ETH Zurich (wintermantel@imes.mavt.ethz.ch) 		Top


Boost Graph Library: Property

property<PropertyTag, T, NextProperty>

This class can be used with the adjacency_list and the adjacency_matrix classes to specify what kind of properties should be attached to the vertices and edges of the graph, and to the graph object itself.

Synopsis

namespace boost {
  template <class Tag, class T, class NextProperty = no_property>
  struct property : public NextProperty {
    typedef NextProperty next_type;
    typedef Tag tag_type;
    typedef T value_type;
    property();
    property(const T& v);
    property(const T& v, const NextProperty& b);
    // copy constructor and assignment operator will be generated by compiler
    T m_value;
  };
}

Template Parameters

Parameter | Description | Default
PropertyTag | A type to identify (give a unique name to) the property. There are several predefined tags, and it is easy to add more. For convenience, BGL also provides predefined objects of the tag types (enum values) for use as arguments to functions that expect property tag objects (such as adjacency_list's property map accessor functions). |
T | This type specifies the type of the property values. |
NextProperty | This parameter allows property types to be nested, so that an arbitrary number of properties can be attached to the same graph. | no_property

Where Defined

boost/pending/property.hpp

Associated Types

next_type
The NextProperty type parameter.
tag_type
The Tag type parameter.
value_type
The T type parameter.

Member Functions

property()
Construct a property object with member m_value a default constructed instance of type T and with the super object default constructed. Note that T must be Default Constructible for this property, and all the inherited property types.
property(const T& v)
Construct a property object with member m_value a copy of v.
property(const T& v, const NextProperty& b)
Construct a property object with member m_value a copy of v and whose super class NextProperty is constructed from b.

Property Tags

The following property tags are defined in boost/graph/properties.hpp.
  namespace boost {
    enum edge_name_t { edge_name };
    enum edge_weight_t { edge_weight };
    enum edge_index_t { edge_index };
    enum edge_capacity_t { edge_capacity };
    enum edge_residual_capacity_t { edge_residual_capacity };
    enum edge_reverse_t { edge_reverse };
    enum vertex_name_t { vertex_name };
    enum vertex_distance_t { vertex_distance };
    enum vertex_index_t { vertex_index };
    enum vertex_color_t { vertex_color };
    enum vertex_degree_t { vertex_degree };
    enum vertex_out_degree_t { vertex_out_degree };
    enum vertex_in_degree_t { vertex_in_degree };
    enum vertex_discover_time_t { vertex_discover_time };
    enum vertex_finish_time_t { vertex_finish_time };
    enum graph_name_t { graph_name };

    BOOST_INSTALL_PROPERTY(vertex, index);
    BOOST_INSTALL_PROPERTY(edge, index);
    // ...
  }


Boost Graph Library: Property Map Traits

Property Map
property_map<PropertyGraph, PropertyTag>

This is the traits class that produces the type for a property map object for a particular graph type. The property is specified by the PropertyTag template parameter. Graph classes must specialize this traits class to provide their own implementation for property maps.

  template <class Graph, class PropertyTag>
  struct property_map {
    typedef ... type;
    typedef ... const_type;
  };

Where Defined

boost/graph/properties.hpp

Associated Types

Map Type | type | The (mutable) property map type for mapping vertex or edge descriptors of the graph to their associated property values, where the property is specified by PropertyTag. This type is a property map.
Const Map Type | const_type | The (non-mutable) property map type for mapping vertex or edge descriptors of the graph to their associated property values, where the property is specified by PropertyTag. This type is a property map.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: property_writer

property_writer

property_writer<PropertyMap, OutputIterator, EventTag>
This is an EventVisitor that can be used to output the property of a vertex or edge at some event-point within an algorithm.

property_writer can be used with graph algorithms by wrapping it with the algorithm specific adaptor, such as bfs_visitor and dfs_visitor. Also, this event visitor can be combined with other event visitors using std::pair to form an EventVisitorList.

Example

The following is an excerpt from examples/dave.cpp.
  std::ostream_iterator cout_int(std::cout, " ");
  std::ostream_iterator cout_char(std::cout, " ");

  boost::breadth_first_search
    (G, ertex(a, G), make_bfs_visitor(
     std::make_pair(write_property(name, cout_char, on_discover_vertex()),
     std::make_pair(write_property(distance.begin(), cout_int,
                                   on_discover_vertex()),
     std::make_pair(print_edge(name, std::cout, on_examine_edge()),
                    print_endl(std::cout, on_finish_vertex()
                     ))))));

Model of

EventVisitor

Where Defined

boost/graph/visitors.hpp

Template Parameters

Parameter | Description | Default
PropertyMap | A ReadablePropertyMap, where the key_type is the vertex descriptor type or edge descriptor of the graph (depending on the kind of event tag), and the value_type of the property is convertible to the value_type of the OutputIterator. |
OutputIterator | The iterator type used to write out the property values, which must be a model of OutputIterator. |
EventTag | The tag to specify when the/font> property_writer should be applied during the graph algorithm. |

Associated Types

Type | Description
property_writer::event_filter | This will be the same type as the template parameter EventTag.

Member Functions

Member/font> | Description
property_writer(PropertyMap pa, OutputIterator out); | Construct a property writer object with the property map pa and output iterator out.
template <class X, class Graph>
void operator()(X x, const Graph& g); 		This writs the property value for x to the output iterator.
*out++ = get(pa, x);

Non-Member Functions

Function/font> Description
template <class PropertyMap, class OutputIterator, class Tag>
property_writer<PropertyMap, OutputIterator, Tag>
write_property(PropertyMap pa, OutputIterator out, Tag); 		A convenient way to create a property_writer.

See Also

Visitor concepts

The following are other event visitors: distance_recorder, predecessor_recorder, and time_stamper.

Copyright © 2000-2001br> Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


PropertyGraph

A PropertyGraph is a graph that has some property associated with each of the vertices or edges in the graph. As a given graph may have several properties associated with each vertex or edge, a tag is used to identity which property is being accessed. The graph provides a function which returns a property map object.

Refinement of

Graph

Notation

G | A type that is a model of PropertyGraph.
g | An object of type G.
X | Either the vertex or edge descriptor type for G.
x | An object of type X.
Map | The type boost::property_map<G, Property>::const_type.
v | An object of type boost::property_traits<Map>::value_type.
PropertyTag | A type that models the PropertyTag concept.
p | An object of type PropertyTag.
pmap | An object of type Map.

Associated types

boost::property_map<G, PropertyTag>::type

The type of the property map for the property specified by PropertyTag. This type must be a model of ReadWritePropertyMap with a key type the same as the graph's vertex or edge descriptor type.

boost::property_map<G, PropertyTag>::const_type

The type of the const property map for the property specified by PropertyTag. This type must be a model of ReadablePropertyMap with a key type the same as the graph's vertex or edge descriptor type.

Valid Expressions

get(p, g)

Returns the property map for the property specified by the PropertyTag type. The object p is only used to carry the type.
Return type: boost::property_map<G, PropertyTag>::type if g is mutable
and
boost::property_map<G, PropertyTag>::const_type otherwise.

get(p, g, x) Returns the property value (specified by the PropertyTag type) associated with object x (a vertex or edge). The object p is only used to carry the type. This function is equivalent to:
get(get(p, g), x)
Return type: boost::property_traits<Map>::value_type
put(p, g, x, v) Set the property (specified by the PropertyTag type) associated with object x (a vertex or edge) to the value v. The object p is only used to carry the type. This function is equivalent to:
pmap = get(p, g);
put(pmap, x, v)
Return type: void

Complexity

The get() property map function must be constant time.

Models

  • adjacency_list with VertexProperty=property<vertex_distance_t,int,property<vertex_in_degree_t,int> > and PropertyTag=vertex_distance_t.

  • adjacency_list with VertexPropertyTag=property<vertex_distance_t,int,property<vertex_in_degree_t,int> > and PropertyTag=vertex_in_degree_t.

Concept Checking Class

  template <class Graph, class X, class PropertyTag>
  struct PropertyGraphConcept
  {
    typedef typename property_map<G, PropertyTag>::type Map;
    typedef typename property_map<G, PropertyTag>::const_type const_Map;
    void constraints() {
      function_requires< GraphConcept<G> >();
      function_requires< ReadWritePropertyMapConcept<Map, X> >();
      function_requires< ReadablePropertyMapConcept<const_Map, X> >();

      Map pmap = get(PropertyTag(), g);
      pval = get(PropertyTag(), g, x);
      put(PropertyTag(), g, x, pval);
      ignore_unused_variable_warning(pmap);
    }
    void const_constraints(const G& g) {
      const_Map pmap = get(PropertyTag(), g);
      pval = get(PropertyTag(), g, x);
      ignore_unused_variable_warning(pmap);
    }
    G g;
    X x;
    typename property_traits<Map>::value_type pval;
  };

See Also

property_map

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


PropertyGraph

Name

PropertyGraph: A graph that has some property associated with each of the vertices or edges in the graph.

Synopsis

template <class Graph, class X, class PropertyTag>
  struct PropertyGraphConcept
  {
    typedef typename property_map<G, PropertyTag>::type Map;
    typedef typename property_map<G, PropertyTag>::const_type const_Map;
    void constraints() {
      function_requires< GraphConcept<G> >();
      function_requires< ReadWritePropertyMapConcept<Map, X> >();
      function_requires< ReadablePropertyMapConcept<const_Map, X> >();

      Map pmap = get(PropertyTag(), g);
      pval = get(PropertyTag(), g, x);
      put(PropertyTag(), g, x, pval);
      ignore_unused_variable_warning(pmap);
    }
    void const_constraints(const G& g) {
      const_Map pmap = get(PropertyTag(), g);
      pval = get(PropertyTag(), g, x);
      ignore_unused_variable_warning(pmap);
    }
    G g;
    X x;
    typename property_traits<Map>::value_type pval;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Property Map Library

Boost Property Map Library

The Boost Property Map Library consists mainly of interface specifications in the form of concepts (similar to the iterator concepts in the STL [2]). These interface specifications are intended for use by implementers of generic libraries in communicating requirements on template parameters to their users. In particular, the Boost Property Map concepts define a general purpose interface for mapping key objects to corresponding value objects, thereby hiding the details of how the mapping is implemented from algorithms. The implementation of types fulfilling the property map interface is up to the client of the algorithm to provide. The property map requirements are purposefully vague on the type of the key and value objects to allow for the utmost genericity in the function templates of the generic library.

The need for the property map interface came from the Boost Graph Library (BGL), which contains many examples of algorithms that use the property map concepts to specify their interface. For an example, note the ColorMap template parameter of the breadth_first_search. In addition, the BGL contains many examples of concrete types that implement the property map interface. The adjacency_list class implements property maps for accessing objects (properties) that are attached to vertices and edges of the graph.

The Boost Property Map Library also contains a few adaptors that convert commonly used data-structures that implement a mapping operation, such as built-in arrays (pointers), iterators, and std::map, to have the property map interface. These adaptors are not meant to fulfill all mapping needs, but are to serve as an example of how to implement the interface as well as covering a few common cases. See the header files for details.

Property maps are statically-typed entities. If you need to access property maps in a more dynamic setting (e.g., because you're reading an unknown set of attributes from a file), you can use the dynamic_properties class to access a set of property maps through a dynamically-typed interface.

Property Map Concepts

The property map interface consists of a set of concepts (see definition of "concept" in The Boost Concept Check Library (BCCL) and [2]) that define a syntax for mapping key objects to corresponding value objects. Since the property map operations are global functions (actually they don't have to be global, but they are always called unqualified and may be found via argument dependent lookup), it is possible to overload the map functions such that nearly arbitrary property map types and key types can be used. The interface for property maps consists of three functions: get(), put(), and operator[]. The following concrete example from example1.cpp shows how the three functions could be used to access the addresses associated with various people. Use a separate function template here to highlight the parts of the program that use the property map concept interface. In the main() function we use std::map and boost::associative_property_map, but it would have been OK to use any type (including a custom type that you create) that fulfills the property map requirements.

#include <iostream>
#include <map>
#include <string>
#include <boost/property_map.hpp>

template <typename AddressMap>
void foo(AddressMap address)
{
  typedef typename boost::property_traits<AddressMap>::value_type value_type;
  typedef typename boost::property_traits<AddressMap>::key_type key_type;

  value_type old_address, new_address;
  key_type fred = "Fred";
  old_address = get(address, fred);
  new_address = "384 Fitzpatrick Street";
  put(address, fred, new_address);

  key_type joe = "Joe";
  value_type& joes_address = address[joe];
  joes_address = "325 Cushing Avenue";
}

int
main()
{
  std::map<std::string, std::string> name2address;
  boost::associative_property_map< std::map<std::string, std::string> >
    address_map(name2address);

  name2address.insert(make_pair(std::string("Fred"),
                std::string("710 West 13th Street")));
  name2address.insert(make_pair(std::string("Joe"),
                std::string("710 West 13th Street")));

  foo(address_map);

  for (std::map<std::string, std::string>::iterator i = name2address.begin();
       i != name2address.end(); ++i)
    std::cout << i->first << ": " << i->second << "\n";

  return EXIT_SUCCESS;
}

For each property map object there is a set of valid keys for which the mapping to value objects is defined. Invoking a property map function on an invalid key results in undefined behavior. The property map concepts do not specify how this set of valid keys is created or modified. A function that uses a property map must specify the expected set of valid keys in its preconditions.

The need for property maps came out of the design of the Boost Graph Library, whose algorithms needed an interface for accessing properties attached to vertices and edges in a graph. In this context the vertex and edge descriptors are the key type of the property maps.

Several categories of property maps provide different access capabilities:

readable

The associated property data can only be read. The data is returned by-value. Many property maps defining the problem input (such as edge weight) can be defined as readable property maps.

writeable

The associated property can only be written to. The parent array used to record the paths in a bread-first search tree is an example of a property map that would be defined writeable.

read/write

The associated property can both be written and read. The distance property use in Dijkstra's shortest paths algorithm would need to provide both read and write capabilities.

lvalue

The associated property is actually represented in memory and it is possible to get a reference to it. The property maps in the lvalue category also support the requirements for read/write property maps.

There is a separate concept defined for each of the four property map categories. These property map concepts are listed below, with links to the documentation for each of them.

  • ReadablePropertyMap

  • WritablePropertyMap

  • ReadWritePropertyMap

  • LvaluePropertyMap

Property Map Category Tags

There is a tag struct for each of the categories of property maps, which is defined in the header <boost/property_map.hpp>.

namespace boost {

  struct readable_property_map_tag { };

  struct writable_property_map_tag { };

  struct read_write_property_map_tag :
    public readable_property_map_tag,
    public writable_property_map_tag { };

  struct lvalue_property_map_tag :
    public read_write_property_map_tag { };

}

Property Map Traits

Similar to the std::iterator_traits class of the STL, there is a boost::property_traits class that can be used to deduce the types associated with a property map type: the key and value types, and the property map category. There is a specialization of boost::property_traits so that pointers can be used as property map objects. In addition, the property map functions are overloaded for pointers. These traits classes and functions are defined in <boost/property_map.hpp>.

namespace boost {

  template <typename PropertyMap>
  struct property_traits {
     typedef typename PropertyMap::key_type key_type;
     typedef typename PropertyMap::value_type value_type;
     typedef typename PropertyMap::category category;
  };

}

Property Map Types

  • Built-in C++ pointer types.
    The following specialization of the     property_traits class and the overloads of put() and get() make it possible to use built-in C++ pointer types as property maps. These are defined in boost/property_map.hpp. More specifically, it means that T* is a model of LvaluePropertyMap, given a key type that is at least convertible std::ptrdiff_t.

    namespace boost {
  // specialization for using pointers as property maps
  template <typename T>
  struct property_traits<T*> {
    typedef T value_type;
    typedef std::ptrdiff_t key_type;
    typedef random_access_iterator_pa_tag category;
  };

  // overloads of the property map functions for pointers
  template<>
  void put(T* pmap, std::ptrdiff_t k, const T& val) { pmap[k] = val;  }

  template<>
  const T& get(const T* pmap, std::ptrdiff_t k) { return pmap[k]; }

}

  • identity_property_map

  • iterator_property_map

  • associative_property_map

  • const_associative_property_map

  • vector_property_map

History

The property map interface originated as data accessors in Dietmar Kühl's Masters Thesis on generic graph algorithms. The property map idea also appeared under the guise of decorators in early versions of the Generic Graph Component Library (GGCL), which is now the Boost Graph Library (BGL). The main motivation for the property map interface was to support the access of data associated with vertices and edges in a graph, though the applicability of property maps goes beyond this.

Acknowledgments

Thanks go to Dietmar Kühl for coming up with this mechanism, and thanks go to the Boost members who helped refine and improve the property map interface. Thanks to Dave Abrahams for managing the formal review of the BGL which included the property map library.

Notes to Implementors

It is inexpensive to copy a property map as, they are often passed by value.

Copyright © 2000-2002
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


PropertyTag

A Property Tag is a type used to name or identify properties that are attached to the vertices and edges of a graph. A Property type is not the type of the actual property values. Objects of the Property type are not used except to carry the type information which specifies the property. See the section about Custom Edge Properties for a description of how to define own property tags.

Associated Types

Property Kind | property_kind<PropertyTag>::type | This specifies whether the property is a Vertex Property Tag (vertex_property_tag), an Edge Property Tag (edge_property_tag), or a Graph Property Tag which is attached to the graph object itself (graph_property_tag). The tags are defined in boost/graph/properties.hpp. The property_kind traits class is defined in boost/pending/property.hpp.
Property ID Number | property_num<PropertyTag>::value | This is needed as a workaround when using compilers that do not support partial specialization (and hence prevent us from using boost::is_same to detect if two property tags are the same). The property_num traits class is defined in boost/pending/property.hpp.

Refinement of

Default Constructible

Models

The following models of the Property Tag concept are defined in boost/graph/properties.hpp.

  • vertex_index_t

  • edge_index_t

  • graph_name_t

  • vertex_name_t

  • edge_name_t

  • edge_weight_t

  • vertex_distance_t

  • vertex_color_t

  • vertex_degree_t

  • vertex_out_degree_t

  • vertex_in_degree_t

  • vertex_discover_time_t

  • vertex_finish_time_t

See Also

PropertyGraph and Property Map Concepts

Notes

On compilers that do not support partial specialization, each Property type is also required to specialize property_num<PropertyTag> to contain an enum named value which uniquely identifies the property type.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Publications

Boost Graph Library Related Publications

  • Dr. Dobb's Sept. 2000 Article
  • OOPSLA'99 GGCL Paper
  • Lie-Quan Lee's Master's Thesis about GGCL(ps) (pdf)
  • Dietmar Kühl's Master's Thesis: Design Pattern for the Implementation of Graph Algorithms
  • ISCOPE'99 Sparse Matrix Ordering (pdf)
  • C++ Template Workshop 2000, Concept Checking

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Push-Relabel Maximum Flow

push_relabel_max_flow

// named parameter version
template <class Graph, class P, class T, class R>
typename property_traits<CapacityEdgeMap>::value_type
push_relabel_max_flow(Graph& g,
   typename graph_traits<Graph>::vertex_descriptor src,
   typename graph_traits<Graph>::vertex_descriptor sink,
   const bgl_named_params<P, T, R>& params = all defaults)

// non-named parameter version
template <class Graph,
      class CapacityEdgeMap, class ResidualCapacityEdgeMap,
      class ReverseEdgeMap, class VertexIndexMap>
typename property_traits<CapacityEdgeMap>::value_type
push_relabel_max_flow(Graph& g,
   typename graph_traits<Graph>::vertex_descriptor src,
   typename graph_traits<Graph>::vertex_descriptor sink,
   CapacityEdgeMap cap, ResidualCapacityEdgeMap res,
   ReverseEdgeMap rev, VertexIndexMap index_map)

The push_relabel_max_flow() function calculates the maximum flow of a network. See Section Network Flow Algorithms for a description of maximum flow. The calculated maximum flow will be the return value of the function. The function also calculates the flow values f(u,v) for all (u,v) in E, which are returned in the form of the residual capacity r(u,v) = c(u,v) - f(u,v).

There are several special requirements on the input graph and property map parameters for this algorithm. First, the directed graph G=(V,E) that represents the network must be augmented to include the reverse edge for every edge in E. That is, the input graph should be Gin = (V,{E U ET}). The ReverseEdgeMap argument rev must map each edge in the original graph to its reverse edge, that is (u,v) -> (v,u) for all (u,v) in E. The CapacityEdgeMap argument cap must map each edge in E to a positive number, and each edge in ET to 0.

This algorithm was developed by Goldberg.

Complexity

The time complexity is O(V3).

Where Defined

boost/graph/preflow_push_max_flow.hpp

Parameters

IN: VertexListGraph& g
A directed graph. The graph's type must be a model of Vertex List Graph. For each edge (u,v) in the graph, the reverse edge (v,u) must also be in the graph.
IN: vertex_descriptor src
The source vertex for the flow network graph.
IN: vertex_descriptor sink
The sink vertex for the flow network graph.

Named Parameters

IN: capacity_map(EdgeCapacityMap cap)
The edge capacity property map. The type must be a model of a constant Lvalue Property Map. The key type of the map must be the graph's edge descriptor type.
 Default: get(edge_capacity, g)
OUT: residual_capacity_map(ResidualCapacityEdgeMap res)
The edge residual capacity property map. The type must be a model of a mutable Lvalue Property Map. The key type of the map must be the graph's edge descriptor type.
Default: get(edge_residual_capacity, g)
IN: reverse_edge_map(ReverseEdgeMap rev)
An edge property map that maps every edge (u,v) in the graph to the reverse edge (v,u). The map must be a model of constant Lvalue Property Map. The key type of the map must be the graph's edge descriptor type.
Default: get(edge_reverse, g)
IN: vertex_index_map(VertexIndexMap index_map)
Maps each vertex of the graph to a unique integer in the range [0, num_vertices(g)). The map must be a model of constant LvaluePropertyMap. The key type of the map must be the graph's vertex descriptor type.
Default: get(vertex_index, g) Note: if this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.

Example

This reads in an example maximum flow problem (a graph with edge capacities) from a file in the DIMACS format. The source for this example can be found in example/max_flow.cpp.
#include <boost/config.hpp>
#include <iostream>
#include <string>
#include <boost/graph/push_relabel_map_flow.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/read_dimacs.hpp>

int
main()
{
  using namespace boost;

  typedef adjacency_list_traits<vecS, vecS, directedS> Traits;
  typedef adjacency_list<vecS, vecS, directedS,
    property<vertex_name_t, std::string>,
    property<edge_capacity_t, long,
      property<edge_residual_capacity_t, long,
    property<edge_reverse_t, Traits::edge_descriptor> > >
  > Graph;

  Graph g;
  long flow;

  property_map<Graph, edge_capacity_t>::type
    capacity = get(edge_capacity, g);
  property_map<Graph, edge_reverse_t>::type
    rev = get(edge_reverse, g);
  property_map<Graph, edge_residual_capacity_t>::type
    residual_capacity = get(edge_residual_capacity, g);

  Traits::vertex_descriptor s, t;
  read_dimacs_max_flow(g, capacity, rev, s, t);

  flow = push_relabel_max_flow(g, s, t);

  std::cout << "c  The total flow:" << std::endl;
  std::cout << "s " << flow << std::endl << std::endl;

  std::cout << "c flow values:" << std::endl;
  graph_traits<Graph>::vertex_iterator u_iter, u_end;
  graph_traits<Graph>::out_edge_iterator ei, e_end;
  for (tie(u_iter, u_end) = vertices(g); u_iter != u_end; ++u_iter)
    for (tie(ei, e_end) = out_edges(*u_iter, g); ei != e_end; ++ei)
      if (capacity[*ei] > 0)
        std::cout << "f " << *u_iter << " " << target(*ei, g) << " "
                  << (capacity[*ei] - residual_capacity[*ei]) << std::endl;
  return 0;
}
The output is:
c  The total flow:
s 4

c flow values:
f 0 1 4
f 1 2 4
f 2 3 2
f 2 4 2
f 3 1 0
f 3 6 2
f 4 5 3
f 5 6 0
f 5 7 3
f 6 4 1
f 6 7 1

See Also

edmunds_karp_max_flow().
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Boost Graph Library: Python Bindings (Experimental)

The Boost Graph Library offers a wealth of graph algorithms and data types for C++. These algorithms are flexible and efficient, but the mechanisms employed to achieve this goal can result in long compile times that slow the development cycle.

The Python bindings are build using the Boost.Python library. The bindings are meant to strike a balance of usability, flexibility, and efficiency, making it possible to rapidly develop useful systems using the BGL in Python.

The Python bindings for the BGL are now part of a separate project. They are no longer available within the Boost tree.

Copyright © 2005
Doug Gregor, Indiana University ()
Andrew Lumsdaine, Indiana University () 		Top


Quick Tour of Boost Graph Library

A Quick Tour of the Boost Graph Library

The domain of graph data structures and algorithms is in some respects more complicated than that of containers. The abstract iterator interface used by STL is not sufficiently rich to encompass the numerous ways that graph algorithms may traverse a graph. Instead, formulate an abstract interface that serves the same purpose for graphs that iterators do for basic containers (though iterators still play a large role). Figure 1 depicts the analogy between the STL and the BGL.

Figure 1: The analogy between the STL and the BGL.

The graph abstraction consists of a set of vertices (or nodes), and a set of edges (or arcs) that connect the vertices. Figure 2 depicts a directed graph with five vertices (labeled 0 through 4) and 11 edges. The edges leaving a vertex are called the out-edges of the vertex. The edges {(0,1),(0,2),(0,3),(0,4)} are all out-edges of vertex 0. The edges entering a vertex are called the in-edges of the vertex. The edges {(0,4),(2,4),(3,4)} are all in-edges of vertex 4.

Figure 2: An example of a directed graph.

In the following sections, the BGL is used to construct this example graph and manipulate it in various ways. The complete source code for this example can be found in examples/quick_tour.cpp. Each of the following sections discusses a "slice" of this example file. Excerpts from the output of the example program will also be listed.

Constructing a Graph

In this example BGL adjacency_list class is used to demonstrate the main ideas in the BGL interface. The adjacency_list class provides a generalized version of the classic "adjacency list" data structure. The adjacency_list is a template class with six template parameters, though here the user only fill in the first three parameters and use the defaults for the remaining three. The first two template arguments (vecS, vecS) determine the data structure used to represent the out-edges for each vertex in the graph and the data structure used to represent the graph's vertex set (see section Choosing the Edgelist and VertexList for information about the tradeoffs of the different data structures). The third argument, bidirectionalS, selects a directed graph that provides access to both out and in-edges. The other options for the third argument are directedS which selects a directed graph with only out-edges, and undirectedS which selects an undirected graph.

Once the graph type is selected, the graph in Figure 2 can be created by declaring a graph object and filling in edges using the add_edge() function of the MutableGraph interface (which adjacency_list implements). Use the array of pairs edge_array merely as a convenient way to explicitly create the edges for this example.

  #include <iostream>                  // for std::cout
  #include <utility>                   // for std::pair
  #include <algorithm>                 // for std::for_each
  #include <boost/graph/graph_traits.hpp>
  #include <boost/graph/adjacency_list.hpp>
  #include <boost/graph/dijkstra_shortest_paths.hpp>

  using namespace boost;

  int main(int,char*[])
  {
    // create a typedef for the Graph type
    typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;

    // Make convenient labels for the vertices
    enum { A, B, C, D, E, N };
    const int num_vertices = N;
    const char* name = "ABCDE";

    // writing out the edges in the graph
    typedef std::pair<int, int> Edge;
    Edge edge_array[] =
    { Edge(A,B), Edge(A,D), Edge(C,A), Edge(D,C),
      Edge(C,E), Edge(B,D), Edge(D,E) };
    const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);

    // declare a graph object
    Graph g(num_vertices);

    // add the edges to the graph object
    for (int i = 0; i < num_edges; ++i)
      add_edge(edge_array[i].first, edge_array[i].second, g);
    ...
    return 0;
  }

Instead of calling the add_edge() function for each edge, use the edge iterator constructor of the graph. This is typically more efficient than using add_edge(). Pointers to the edge_array can be viewed as iterators, so , call the iterator constructor by passing pointers to the beginning and end of the array.

    Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices);

Instead of creating a graph with a certain number of vertices to begin with, it is also possible to add and remove vertices with the add_vertex() and remove_vertex() functions, also of the MutableGraph interface.

Accessing the Vertex Set

Now that a graph is created, the graph can be used interface to access the graph data in different ways. First, all of the vertices in the graph can be accessed using the vertices() function of the VertexListGraph interface. This function returns a std::pair of vertex iterators (the first iterator points to the "beginning" of the vertices and the second iterator points "past the end"). Dereferencing a vertex iterator gives a vertex object. The type of the vertex iterator is given by the graph_traits class. Note that different graph classes can have different associated vertex iterator types, which is why the graph_traits class is needed. Given some graph type, the graph_traits class will provide access to the vertex_iterator type.

The following example prints out the index for each of the vertices in the graph. All vertex and edge properties, including index, are accessed via property map objects. The property_map class is used to obtain the property map type for a specific property (specified by vertex_index_t, one of the BGL predefined properties) and function call get(vertex_index, g) returns the actual property map object.

  // ...
  int main(int,char*[])
  {
    // ...

    // get the property map for vertex indices
    typedef property_map<Graph, vertex_index_t>::type IndexMap;
    IndexMap index = get(vertex_index, g);

    std::cout << "vertices(g) = ";
    typedef graph_traits<Graph>::vertex_iterator vertex_iter;
    std::pair<vertex_iter, vertex_iter> vp;
    for (vp = vertices(g); vp.first != vp.second; ++vp.first)
      std::cout << index[*vp.first] <<  " ";
    std::cout << std::endl;
    // ...
    return 0;
  }
The output is:
  vertices(g) = 0 1 2 3 4

Accessing the Edge Set

The set of edges for a graph can be accessed with the edges() function of the EdgeListGraph interface. Similar to the vertices() function, this returns a pair of iterators, but in this case the iterators are edge iterators. Dereferencing an edge iterator gives an edge object. The source() and target() functions return the two vertices that are connected by the edge. Instead of explicitly creating a std::pair for the iterators, this time use the tie() helper function. This handy function can be used to assign the parts of a std::pair into two separate variables, in this case ei and ei_end. This is usually more convenient than creating a std::pair and is the method of choice for the BGL.

  // ...
  int main(int,char*[])
  {
    // ...
    std::cout << "edges(g) = ";
    graph_traits<Graph>::edge_iterator ei, ei_end;
    for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
        std::cout << "(" << index[source(*ei, g)]
                  << "," << index[target(*ei, g)] << ") ";
    std::cout << std::endl;
    // ...
    return 0;
  }
The output is:
  edges(g) = (0,1) (0,2) (0,3) (0,4) (2,0) (2,4) (3,0)
    (3,1) (3,4) (4,0) (4,1)

The Adjacency Structure

In the next few examples, explore the adjacency structure of the graph from the point of view of a particular vertex. Look at the vertices' in-edges, out-edges,and its adjacent vertices. Encapsulate this in an "exercise vertex" function, and apply it to each vertex in the graph. To demonstrate the STL-interoperability of BGL, the STL for_each() function is used to iterate through the vertices and apply the function.

  //...
  int main(int,char*[])
  {
    //...
    std::for_each(vertices(g).first, vertices(g).second,
                  exercise_vertex<Graph>(g));
    return 0;
  }

Use a functor for exercise_vertex instead of just a function because the graph object will be needed while accessing information about each vertex; using a functor gives a place to keep a reference to the graph object during the execution of the std::for_each(). Also template the functor on the graph type so that it is reusable with different graph classes. Here is the start of the exercise_vertex functor:

  template <class Graph> struct exercise_vertex {
    exercise_vertex(Graph& g_) : g(g_) {}
    //...
    Graph& g;
  };

Vertex Descriptors

The first thing to know in order to write the operator() method of the functor is the type for the vertex objects of the graph. The vertex type will be the parameter to the operator() method. To be precise, we do not deal with actual vertex objects, but rather with vertex descriptors. Many graph representations (such as adjacency lists) do not store actual vertex objects, while others do (e.g., pointer-linked graphs). This difference is hidden underneath the "black-box" of the vertex descriptor object. The vertex descriptor is something provided by each graph type that can be used to access information about the graph via the out_edges(), in_edges(), adjacent_vertices(), and property map functions that are described in the following sections. The vertex_descriptor type is obtained through the graph_traits class. The typename keyword used below is necessary because the type on the left hand side of the scope :: operator (the graph_traits<Graph> type) is dependent on a template parameter (the Graph

  template <class Graph> struct exercise_vertex {
    //....
    typedef typename graph_traits<Graph>
      ::vertex_descriptor Vertex;

    void operator()(const Vertex& v) const
    {
      //...
    }
    //...
  };

Out-Edges, In-Edges, and Edge Descriptors

The out-edges of a vertex are accessed with the out_edges() function of the IncidenceGraph interface. The out_edges() function takes two arguments: the first argument is the vertex and the second is the graph object. The function returns a pair of iterators which provide access to all of the out-edges of a vertex (similar to how the vertices() function returned a pair of iterators). The iterators are called out-edge iterators and dereferencing one of these iterators gives an edge descriptor object. An edge descriptor plays the same kind of role as the vertex descriptor object, it is a "black box" provided by the graph type. The following code snippet prints the source-target pairs for each out-edge of vertex v.

  template <class Graph> struct exercise_vertex {
    //...
    void operator()(const Vertex& v) const
    {
      typedef graph_traits<Graph> GraphTraits;
      typename property_map<Graph, vertex_index_t>::type
        index = get(vertex_index, g);

      std::cout << "out-edges: ";
      typename GraphTraits::out_edge_iterator out_i, out_end;
      typename GraphTraits::edge_descriptor e;
      for (tie(out_i, out_end) = out_edges(v, g);
           out_i != out_end; ++out_i) {
        e = *out_i;
        Vertex src = source(e, g), targ = target(e, g);
        std::cout << "(" << index[src] << ","
                  << index[targ] << ") ";
      }
      std::cout << std::endl;
      //...
    }
    //...
  };
For vertex 0 the output is:
  out-edges: (0,1) (0,2) (0,3) (0,4)

The in_edges() function of the BidirectionalGraph interface provides access to all the in-edges of a vertex through in-edge iterators. The in_edges() function is only available for the adjacency_list if bidirectionalS is supplied for the Directed template parameter. There is an extra cost in space when bidirectionalS is specified instead of directedS.

  template <class Graph> struct exercise_vertex {
    //...
    void operator()(const Vertex& v) const
    {
      //...
      std::cout << "in-edges: ";
      typedef typename graph_traits<Graph> GraphTraits;
      typename GraphTraits::in_edge_iterator in_i, in_end;
      for (tie(in_i, in_end) = in_edges(v,g);
           in_i != in_end; ++in_i) {
        e = *in_i;
        Vertex src = source(e, g), targ = target(e, g);
        std::cout << "(" << index[src] << "," << index[targ] << ") ";
      }
      std::cout << std::endl;
      //...
    }
    //...
  };
For vertex 0 the output is:
  in-edges: (2,0) (3,0) (4,0)

Adjacent Vertices

Given the out-edges of a vertex, the target vertices of these edges are adjacent to the source vertex. Sometimes an algorithm does not need to look at the edges of the graph and only cares about the vertices. Therefore the graph interface also includes the adjacent_vertices() function of the AdjacencyGraph interface which provides direct access to the adjacent vertices. This function returns a pair of adjacency iterators. Dereferencing an adjacency iterator gives a vertex descriptor for an adjacent vertex.

  template <class Graph> struct exercise_vertex {
    //...
    void operator()(Vertex v) const
    {
      //...
      std::cout << "adjacent vertices: ";
      typename graph_traits<Graph>::adjacency_iterator ai;
      typename graph_traits<Graph>::adjacency_iterator ai_end;
      for (tie(ai, ai_end) = adjacent_vertices(v, g);
           ai != ai_end; ++ai)
        std::cout << index[*ai] <<  " ";
      std::cout << std::endl;
    }
    //...
  };
For vertex 4 the output is:
  adjacent vertices: 0 1

Adding Some Color to the Graph

BGL attempts to be as flexible as possible in terms of accommodating how properties are attached to a graph. For instance, a property such as edge weight may need to be used throughout a graph object's lifespan and therefore it would be convenient to have the graph object also manage the property storage. On the other hand, a property like vertex color may only be needed for the duration of a single algorithm, and it would be better to have the property stored separately from the graph object. The first kind of property is called an internally stored property while the second kind is called an externally stored property. BGL uses a uniform mechanism to access both kinds of properties inside its graph algorithms called the property map interface, described in Section Property Map Concepts. In addition, the PropertyGraph concept defines the interface for obtaining a property map object for an internally stored property.

The BGL adjacency_list class allows users to specify internally stored properties through plug-in template parameters of the graph class. How to do this is discussed in detail in Section Internal Properties. Externally stored properties can be created in many different ways, although they are ultimately passed as separate arguments to the graph algorithms. One straightforward way to store properties is to create an array indexed by vertex or edge index. In the adjacency_list with vecS specified for the VertexList template parameter, vertices are automatically assigned indices, which can be accessed via the property map for the vertex_index_t. Edges are

In the following example, a graph is constructed and applied dijkstra_shortest_paths().. The complete source code for the example is in examples/dijkstra-example.cpp.Dijkstra's algorithm computes the shortest distance from the starting vertex to every other vertex in the graph.

Dijkstra's algorithm requires that a weight property is associated with each edge and a distance property with each vertex. Here an internal property for the weight and an external property for the distance is used. For the weight property , the property class is used and int is specified as the type used to represent weight values and edge_weight_t for the property tag (which is one of the BGL predefined property tags). The weight property is then used as a template argument for adjacency_list.

The listS and vecS types are selectors that determine the data structure used inside the adjacency_list (see Section Choosing the Edgelist and VertexList ). The directedS type specifies that the graph should be directed (versus undirected). The following code shows the specification of the graph type and then the initialization of the graph. The edges and weights are passed to the graph constructor in the form of iterators (a pointer qualifies as a RandomAccessIterator).

  typedef adjacency_list<listS, vecS, directedS,
                         no_property, property<edge_weight_t, int> > Graph;
  typedef graph_traits<Graph>::vertex_descriptor Vertex;
  typedef std::pair<int,int> E;

  const int num_nodes = 5;
  E edges[] = { E(0,2),
                E(1,1), E(1,3), E(1,4),
                E(2,1), E(2,3),
                E(3,4),
                E(4,0), E(4,1) };
  int weights[] = { 1, 2, 1, 2, 7, 3, 1, 1, 1};

  Graph G(edges + sizeof(edges) / sizeof(E), weights, num_nodes);

For the external distance property a std::vectorr

  // vector for storing distance property
  std::vector<int> d(num_vertices(G));

  // get the first vertexx
  Vertex s = *(vertices(G).first);
  // invoke variant 2 of Dijkstra's algorithm
  dijkstra_shortest_paths(G, s, distance_map(&d[0]));

  std::cout << "distances from start vertex:" << std::endl;
  graph_traits<Graph>::vertex_iterator vi;
  for(vi = vertices(G).first; vi != vertices(G).second; ++vi)
    std::cout << "distance(" << index(*vi) << ") = "
              << d[*vi] << std::endl;
  std::cout << std::endl;
The output is:
  distances from start vertex:
  distance(0) = 0
  distance(1) = 6
  distance(2) = 1
  distance(3) = 4
  distance(4) = 5

Extending Algorithms with Visitors

Often times an algorithm in a library almost does what you need, but not quite. For example, in the previous section we used Dijkstra's algorithm to calculate the shortest distances to each vertex, but perhaps we also wanted to record the tree of shortest paths. One way to do this is to record the predecessor (parent) for each node in the shortest-paths tree.

It would be nice if we could avoid rewriting Dijkstra's algorithm, and just add that little bit extra needed to record the predecessors [1]. In the STL, this kind of extensibility is provided by functors, which are optional parameters to each algorithm. In the BGL this role is fulfilled by visitors.

A visitor is like a functor, but instead of having just one "apply" method, it has several. Each of these methods get invoked at certain well-defined points within the algorithm. The visitor methods are explained in detail in Section Visitor Concepts. The BGL provides a number of visitors for some common tasks including a predecessor recording visitor. The user is encouraged to write his or her own visitors as a way of extending the BGL. Here we will take a quick look at the implementation and use of the predecessor recorder. Since we will be using the dijkstra_shortest_paths() algorithm, the visitor we create must be a Dijkstra Visitor.

record_predecessors visitor is separated into two parts. For the storage and access of the predecessor property, we will use a property map. The predecessor visitor will then only be responsible for what parent to record. To implement this, we create a record_predecessors class and template it on the predecessor property map PredecessorMap. Since this visitor will only be filling in one of the visitor methods, we will inherit from dijkstra_visitor which will provide empty methods for the rest. The constructor of the predecessor_recorder will take the property map object and save it away in a data member.

  template <class PredecessorMap>
  class  record_predecessors : public dijkstra_visitor<>
  {
  public:
    record_predecessors(PredecessorMap p)
      : m_predecessor(p) { }

    template <class Edge, class Graph>
    void edge_relaxed(Edge e, Graph& g) {
      // set the parent of the target(e) to source(e)
      put(m_predecessor, target(e, g), source(e, g));
    }
  protected:
    PredecessorMap m_predecessor;
  };

The job of recording the predecessors is quite simple. When Dijkstra's algorithm relaxes an edge (potentially adding it to the shortest-paths tree) , record the source vertex as the predecessor of the target vertex. Later, if the edge is relaxed again the predecessor property will be overwritten by the new predecessor. Here, use the put() function associated with the property map to record the predecessor. The edge_filter of the visitor tells the algorithm when to invoke the explore() method. In this case the user only wants to be notified about edges in the shortest-paths tree so specify tree_edge_tag.

As a finishing touch,create a helper function to make it more convenient to create predecessor visitors. All BGL visitors have a helper function like this.

  template <class PredecessorMap>
  record_predecessors<PredecessorMap>
  make_predecessor_recorder(PredecessorMap p) {
    return record_predecessors<PredecessorMap>(p);
  }

Now the record_predecessors in Dijkstra's algorithm is ready to use. Luckily, BGL's Dijkstra's algorithm is already equipped to handle visitors, so, just pass in the new visitor. In this example there is only one visitor which needs to be used, but the BGL is also equipped to handle the use of multiple visitors in the same algorithm (see Section Visitor Concepts).

  using std::vector;
  using std::cout;
  using std::endl;
  vector<Vertex> p(num_vertices(G)); //the predecessor array
  dijkstra_shortest_paths(G, s, distance_map(&d[0]).
                          visitor(make_predecessor_recorder(&p[0])));

  cout << "parents in the tree of shortest paths:" << endl;
  for(vi = vertices(G).first; vi != vertices(G).second; ++vi) {
    cout << "parent(" << *vi;
    if (p[*vi] == Vertex())
      cout << ") = no parent" << endl;
    else
      cout << ") = " << p[*vi] << endl;
  }
The output is:font>
  parents in the tree of shortest paths:
  parent(0) = no parent
  parent(1) = 4
  parent(2) = 0
  parent(3) = 2
  parent(4) = 3

Notes

[1]a> The new version of Dijkstra's algorithm now includes a named parameter for recording predecessors, so a predecessor visitor is no long necessary, though this still makes for a good example.
Copyright © 2000
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Boost Graph Library: random

Graph_Random: The header <boost/graph/random> provides routines to create random graph, select random vertices and edges, and randomize properties.

Synopsis

  template <class Graph, class RandomNumGen>
  typename graph_traits<Graph>::vertex_descriptor
  random_vertex(Graph& g, RandomNumGen& gen);

  template <class Graph, class RandomNumGen>
  typename graph_traits<Graph>::edge_descriptor
  random_edge(Graph& g, RandomNumGen& gen);

  template <typename MutableGraph, class RandNumGen>
  void generate_random_graph
    (MutableGraph& g,
     typename graph_traits<MutableGraph>::vertices_size_type V,
     typename graph_traits<MutableGraph>::vertices_size_type E,
     RandNumGen& gen,
     bool self_edges = false);

  template<class Property, class G, class RandomGenerator>
    void randomize_property(G& g, RandomGenerator rg);

Description

random_vertex

  template <class Graph, class RandomNumGen>
  typename graph_traits<Graph>::vertex_descriptor
  random_vertex(Graph& g, RandomNumGen& gen);

Effects: Selects a random vertex in a graph and returns it.

Preconditions: num_vertices(g) != 0

Complexity: O(num_vertices(g))

random_edge

  template <class Graph, class RandomNumGen>
  typename graph_traits<Graph>::edge_descriptor
  random_edge(Graph& g, RandomNumGen& gen);

Effects: Selects a random edge in a graph and returns it.

Preconditions: num_edges(g) != 0

Complexity: O(num_edges(g))

generate_random_graph

  template <typename MutableGraph, class RandNumGen>
  void generate_random_graph
    (MutableGraph& g,
     typename graph_traits<MutableGraph>::vertices_size_type V,
     typename graph_traits<MutableGraph>::vertices_size_type E,
     RandNumGen& gen,
     bool allow_parallel = true,
     bool self_edges = false);

Effects: Adds V vertices and E edges, to g. Source and target vertices of each edge are randomly chosen. If self_edges is false, then no edge will have the same source and targets.

Precondition: num_vertices(g) == 0

Compleixity: O(V*E)

randomize_property

  template<class Property, class G, class RandomGenerator>
    void randomize_property(G& g, RandomGenerator& rg);

Effects: Sets the random value of property on either all vertices, or all edges, depending on property kind.

Complexity: O(V) or O(E), depending on property kind.

© Copyright Vladimir Prus 2003. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies.
This document is provided ``as is'' without express or implied warranty, and with no claim as to its suitability for any purpose.


Boost Graph Library: Random Graph Layout

random_graph_layout

random_graph_layout

// non-named parameter version
template<typename Graph, typename PositionMap, typename Dimension,
         typename RandomNumberGenerator>
void
random_graph_layout(const Graph& g, PositionMap position_map,
                    Dimension minX, Dimension maxX,
                    Dimension minY, Dimension maxY,
                    RandomNumberGenerator& gen);

 This algorithm places the points of the graph at random
locations.

Where Defined

boost/graph/random_layout.hpp

Parameters

IN: const Graph& g

  The graph object on which the algorithm will be applied.
  The type Graph must be a model of
  Vertex And Edge List Graph.

IN/OUT: PositionMap position

  The property map that stores the position of each vertex. The type
    PositionMap must be a model of Lvalue Property
  Map such that the vertex descriptor
    type of
    Graph is
  convertible to its key type. Its value type must be a structure with
  fields x
    and
    y, representing the coordinates of
  the vertex.

IN: Dimension minX

  The minimum x
    coordinate.

IN: Dimension maxX

  The maximum
    x

    coordinate.

IN: Dimension minY

  The minimum y coordinate.

IN: Dimension maxY

  The maximum y

    coordinate.

IN/UTIL: RandomNumberGenerator& gen

A random number generator that will be used to place vertices. The
type RandomNumberGenerator
must model the NumberGenerator
concept.

Complexity

 The time complexity is O(|V|).

Copyright © 2004

Doug Gregor, Indiana University


Boost read_graphviz

read_graphviz

template <typename MutableGraph>
bool read_graphviz(std::istream& in, MutableGraph& graph,
                   dynamic_properties& dp,
                   const std::string& node_id = "node_id");

// Only available if BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS is defined
template <typename MultiPassIterator, typename MutableGraph>
bool read_graphviz(MultiPassIterator begin, MultiPassIterator end,
                   MutableGraph& graph, dynamic_properties& dp,
                   const std::string& node_id = "node_id");

// Deprecated GraphViz readers
void read_graphviz(const std::string& file, GraphvizDigraph& g);
void read_graphviz(FILE* file, GraphvizDigraph& g);
void read_graphviz(const std::string& file, GraphvizGraph& g);
void read_graphviz(FILE* file, GraphvizGraph& g);

The read_graphviz function interprets a graph described using the GraphViz DOT language and builds a BGL graph that captures that description. Using this function, a graph using data stored as text can be initialized. To use the iterator version of read_graphviz, define the macro BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS before including the header <boost/graph/graphviz.hpp>. Doing so may greatly increase the amount of time required to compile the GraphViz reader.

The DOT language can specify both directed and undirected graphs, and read_graphviz differentiates between the two. One must pass read_graphviz an undirected graph when reading an undirected graph; the same is true for directed graphs. Furthermore, read_graphviz will throw an exception if it encounters parallel edges and cannot add them to the graph.

To handle properties expressed in the DOT language, read_graphviz takes a dynamic_properties object and operates on its collection of property maps. The reader passes all the properties encountered to this object, using the GraphViz string keys as the property keys. Furthermore, read_graphviz stores node identifier names under the vertex property map named node_id.

Requirements:
  • The type of the graph must model the Mutable Graph concept.
  • The type of the iterator must model the Multi-Pass Iterator concept.
  • The property map value types must be default-constructible.

Contents

  • Where Defined
  • Exceptions
  • Building the GraphViz Readers
  • Deprecated Readers
  • Notes
  • Future Work

Where Defined

<boost/graph/graphviz.hpp>

Exceptions

struct graph_exception : public std::exception {
  virtual ~graph_exception() throw();
  virtual const char* what() const throw() = 0;
};

struct bad_parallel_edge : public graph_exception {
  std::string from;
  std::string to;

  bad_parallel_edge(const std::string&, const std::string&);
  virtual ~bad_parallel_edge() throw();
  const char* what() const throw();
};

struct directed_graph_error : public graph_exception {
  virtual ~directed_graph_error() throw();
  virtual const char* what() const throw();
};

struct undirected_graph_error : public graph_exception {
  virtual ~undirected_graph_error() throw();
  virtual const char* what() const throw();
};

Under certain circumstances, read_graphviz will throw one of the above exceptions. The three concrete exceptions can all be caught using the general graph_exception moniker when greater precision is not needed. In addition, all of the above exceptions derive from the standard std::exception for even more generalized error handling.

The bad_parallel_edge exception is thrown when an attempt to add a parallel edge to the supplied MutableGraph fails. The DOT language supports parallel edges, but some BGL-compatible graph types do not. One example of such a graph is boost::adjacency_list<setS,vecS>, which allows at most one edge between any two vertices.

The directed_graph_error exception occurs when an undirected graph type is passed to read_graph but the textual representation of the graph is directed, as indicated by the digraph keyword in the DOT language.

The undirected_graph_error exception occurs when a directed graph type is passed to read_graph but the textual representation of the graph is undirected, as indicated by the graph keyword in the DOT language.

Building the GraphViz Readers

To use the GraphViz readers, build and link against the "boost_graph" library. The library can be built by following the Boost Jam Build Instructions for the subdirectory libs/graph/build.

Deprecated Readers

The deprecated readers do not provide exceptions on error (they abort), they require the use of one of the predefined graph types (GraphvizDigraph or GraphvizGraph), and they do not support arbitrary properties. They will be removed in a future Boost version.

Notes

  • The read_graphviz function does not use any code from the GraphViz distribution to interpret the DOT Language. Rather, the implementation was based on documentation found on the GraphViz web site, as well as experiments run using the dot application. The resulting interpretation may be subtly different from dot for some corner cases that are not well specified.
  • read_graphviz treats subgraphs as syntactic sugar. It does not reflect subgraphs as actual entities in the BGL. Rather, they are used to shorten some edge definitions as well as to give a subset of all nodes or edges certain properties. For example, the DOT graphs digraph { a -> subgraph {b -> c} -> e } and digraph { a -> b -> e ; a -> c -> e ; b -> c} are equivalent.
  • Subgraph IDs refer to subgraphs defined earlier in the graph description. Undefined subgraphs behave as empty subgraphs ({}).
  • On successful reading of a graph, every vertex and edge will have an associated value for every respective edge and vertex property encountered while interpreting the graph. These values will be set using the dynamic_properties object. Some properties may be put multiple times during the course of reading in order to ensure the same semantics as the GraphViz tools. Those edges and vertices that are not explicitly given a value for a property (and that property has no default) will be given the default constructed value of the value type. Be sure that property map value types are default constructible.

See Also

write_graphviz

Future Work

  • Currently the parser relies upon lowercase language keywords (i.e. "graph", "edge", etc.). The DOT Language specifies that they are case-insensitive. This change should be easy given the proper understanding of Spirit and some free time (hint hint!)
  • The parser currently does not handle continuation lines as defined in the DOT Language. Some more sophisticated parsing of identifiers (so-called "ID" in the source) is required to support this.


Readable Property Map

A Readable Property Map provides read-access to the value object associated with a given key via a call to the get() function. The get() function returns a copy of the value object.

Refinement of

Copy Constructible

Notation

PMap | A type that is a model of Readable Property Map.
pmap | An object of type PMap.
key | An object of type boost::property_traits<PMap>::key_type.

Associated Types

Value Type | boost::property_traits<PMap>::value_type | The type of the property.
Reference Type | boost::property_traits<PMap>::reference | A type that is convertible to the value type.
Key Type | boost::property_traits<PMap>::key_type | The type of the key object used to look up the property. The property map may be templated on the key type, in which case this typedef can be void.
Property Map Category | boost::property_traits<PMap>::category | The category of the property: a type convertible to readable_property_map_tag.

Valid Expressions

Name | Expression | Return Type | Description
Get Property Value | get(pmap, key) | reference | Lookup the property of the object identified by key.

Concept Checking Class

  template <class PMap, class Key>
  struct ReadablePropertyMapConcept
  {
    typedef typename property_traits<PMap>::key_type key_type;
    typedef typename property_traits<PMap>::category Category;
    typedef boost::readable_property_map_tag ReadableTag;
    void constraints() {
      function_requires< ConvertibleConcept<Category, ReadableTag> >();

      val = get(pmap, k);
    }
    PMap pmap;
    Key k;
    typename property_traits<PMap>::value_type val;
  };

See Also

Property map concepts

Design Notes

At various times the name "read-only" was considered for this concept. However, that name is inappropriate because concepts are inherently positive, not negative. This becomes obvious when the user defines the Read Write Property Map, which refines both the Readable Property Map and the Writable Property Map concept. It would not make much sense to combine "read-only" and "write-only" concepts!

Copyright © 2000
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)


ReadablePropertyMap

Name

Readable Property Map: This provides read-access to the value object associated with a given key via a call to the get() function. The get() function returns a copy of the value object.

Synopsis

template <class PMap, class Key>
  struct ReadablePropertyMapConcept
  {
    typedef typename property_traits<PMap>::key_type key_type;
    typedef typename property_traits<PMap>::category Category;
    typedef boost::readable_property_map_tag ReadableTag;
    void constraints() {
      function_requires< ConvertibleConcept<Category, ReadableTag> >();

      val = get(pmap, k);
    }
    PMap pmap;
    Key k;
    typename property_traits<PMap>::value_type val;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Read/Write Property Map

A Read/Write Property Map can be used to read property values via the get() function and to write property values via the put() function.

Refinement of

Readable Property Map and Writable Property Map

Notation

PMap

A type that is a model of Read/Write Property Map.

Associated Types

Property Map Category

boost::property_traits<PMap>::category

The category of the property map must be a type convertible to read_write_property_map_tag.

Concept Checking Class

  template <class PMap, class Key>
  struct ReadWritePropertyMapConcept
  {
    typedef typename property_traits<PMap>::category Category;
    typedef boost::read_write_property_map_tag ReadWriteTag;
    void constraints() {
      function_requires< ReadablePropertyMapConcept<PMap, Key> >();
      function_requires< WritablePropertyMapConcept<PMap, Key> >();
      function_requires< ConvertibleConcept<Category, ReadWriteTag> >();
    }
  };

See Also

Property map concepts

Copyright © 2000
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)


ReadWrite Property Map

Name

Read/Write Property Map: This can be used to read property values via the get() function and to write property values via the put() function.

Synopsis

template <class PMap, class Key>
  struct ReadWritePropertyMapConcept
  {
    typedef typename property_traits<PMap>::category Category;
    typedef boost::read_write_property_map_tag ReadWriteTag;
    void constraints() {
      function_requires< ReadablePropertyMapConcept<PMap, Key> >();
      function_requires< WritablePropertyMapConcept<PMap, Key> >();
      function_requires< ConvertibleConcept<Category, ReadWriteTag> >();
    }
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Graph Library: Reverse Graph Adaptor

reverse_graph

reverse_graph<BidirectionalGraph, GraphReference>

The reverse_graph adaptor flips the in-edges and out-edges of a BidirectionalGraph, effectively transposing the graph. The construction of the reverse_graph is constant time, providing a highly efficient way to obtain a transposed-view of a graph.

Example

The example from examples/reverse-graph-eg.cpp.
int
main()
{
  typedef boost::adjacency_list<
    boost::vecS, boost::vecS, boost::bidirectionalS,
  > Graph;

  Graph G(5);
  boost::add_edge(0, 2, G);
  boost::add_edge(1, 1, G);
  boost::add_edge(1, 3, G);
  boost::add_edge(1, 4, G);
  boost::add_edge(2, 1, G);
  boost::add_edge(2, 3, G);
  boost::add_edge(2, 4, G);
  boost::add_edge(3, 1, G);
  boost::add_edge(3, 4, G);
  boost::add_edge(4, 0, G);
  boost::add_edge(4, 1, G);

  std::cout << "original graph:" << std::endl;
  boost::print_graph(G, boost::get(boost::vertex_index, G));

  std::cout << std::endl << "reversed graph:" << std::endl;
  boost::print_graph(boost::make_reverse_graph(G),
                     boost::get(boost::vertex_index, G));

  return 0;
}

The output is:
original graph:
0 --> 2
1 --> 1 3 4
2 --> 1 3 4
3 --> 1 4
4 --> 0 1

reversed graph:
0 --> 4
1 --> 1 2 3 4
2 --> 0
3 --> 1 2
4 --> 1 2 3

Template Parameters

Parameter | Description | Default
BidirectionalGraph | The graph type to be adapted. |
GraphReference | This type should be const BidirectionalGraph& to create a const reverse graph, or BidirectionalGraph& to create a non-const reverse graph. | const BidirectionalGraph&

Model of

BidirectionalGraph and optionally VertexListGraph and PropertyGraph

Where Defined

boost/graph/reverse_graph.hpp

Associated Types

graph_traits<reverse_graph>::vertex_descriptor

The type for the vertex descriptors associated with the reverse_graph.
graph_traits<reverse_graph>::edge_descriptor

The type for the edge descriptors associated with the reverse_graph.
graph_traits<reverse_graph>::vertex_iterator

The type for the iterators returned by vertices().
graph_traits<reverse_graph>::edge_iterator

The type for the iterators returned by edges().
graph_traits<reverse_graph>::out_edge_iterator

The type for the iterators returned by out_edges().
graph_traits<reverse_graph>::adjacency_iterator

The type for the iterators returned by a djacent_vertices().
graph_traits<reverse_graph>::directed_category

Provides information about whether the graph is directed (directed_tag) or undirected (undirected_tag).
graph_traits<reverse_graph>::edge_parallel_category

This describes whether the graph class allows the insertion of parallel edges (edges with the same source and target). The two tags are allow_parallel_edge-_tag and disallow_parallel_edge_tag. The setS and hash_setS variants disallow parallel edges while the others allow parallel edges.
graph_traits<reverse_graph>::vertices_size_type

The type used for dealing with the number of vertices in the graph.
graph_traits<reverse_graph>::edge_size_type

The type used for dealing with the number of edges in the graph.
graph_traits<reverse_graph>::degree_size_type

The type used for dealing with the number of edges incident to a vertex in the graph.
property_map<reverse_graph, PropertyTag>::type
and
property_map<reverse_graph, PropertyTag>::const_type

The property map type for vertex or edge properties in the graph. The specific property is specified by the PropertyTag template argument, and must match one of the properties specified in the VertexProperty or E dgeProperty for the graph.

Member Functions

reverse_graph(BidirectionalGraph& g)
Constructor. Create a reversed (transposed) view of the graph g.

Non-Member Functions

template <class BidirectionalGraph>
reverse_graph<BidirectionalGraph, BidirectionalGraph&>
make_reverse_graph(BidirectionalGraph& g);

template <class BidirectionalGraph>
reverse_graph<BidirectionalGraph, const BidirectionalGraph&>
make_reverse_graph(const BidirectionalGraph& g)
Helper function for creating a reverse_graph.
std::pair<vertex_iterator, vertex_iterator>
vertices(const reverse_graph& g)
Returns an iterator-range providing access to the vertex set of graph g.
std::pair<out_edge_iterator, out_edge_iterator>
out_edges(vertex_descriptor v, const reverse_graph& g)
Returns an iterator-range providing access to the out-edges of vertex v in graph g. These out-edges correspond to the in-edges of the adapted graph.
std::pair<in_edge_iterator, in_edge_iterator>
in_edges(vertex_descriptor v, const reverse_graph& g)
Returns an iterator-range providing access to the in-edges of vertex v in graph g. These in-edges correspond to the out edges of the adapted graph.
std::pair<adjacency_iterator, adjacency__iterator>
adjacent_vertices(vertex_descriptor v, const reverse_graph& g)
Returns an iterator-range providing access to the adjacent vertices of vertex v in graph g.
vertex_descriptor
source(edge_descriptor e, const reverse_graph& g)
Returns the source vertex of edge e.
vertex_descriptor
target(edge_descriptor e, const reverse_graph& g)
Returns the target vertex of edge e.
degree_size_type
out_degree(vertex_descriptor u, const reverse_graph& g)
Returns the number of edges leaving vertex u.
degree_size_type
in_degree(vertex_descriptor u, const reverse_graph& g)
Returns the number of edges entering vertex u. This operation is only available if bidirectionalS was specified for the Directed template parameter.
vertices_size_type
num_vertices(const reverse_graph& g)
Returns the number of vertices in the graph g.
vertex_descriptor
vertex(vertices_size_type n, const reverse_graph& g)
Returns the nth vertex in the graph's vertex list.
std::pair<edge_descriptor, bool>
edge(vertex_descriptor u, vertex_descriptor v,
     const reverse_graph& g)
Returns the edge connecting vertex u to vertex v in graph g.
template <class PropertyTag>
property_map<reverse_graph, PropertyTag>::type
get(PropertyTag, reverse_graph& g)

template <class PropertyTag>
property_map<reverse_graph, Tag>::const_type
get(PropertyTag, const reverse_graph& g)
Returns the property map object for the vertex property specified by PropertyTag. The PropertyTag must match one of the properties specified in the graph's VertexProperty template argument.
template <class PropertyTag, class X>
typename property_traits<property_map<reverse_graph, PropertyTag>::const_type>::value_type
get(PropertyTag, const reverse_graph& g, X x)
This returns the property value for x, which is either a vertex or edge descriptor.
template <class PropertyTag, class X, class Value>
void
put(PropertyTag, const reverse_graph& g, X x, const Value& value)
This sets the property value for x to value. x is either a vertex or edge descriptor. Value must be convertible to typename property_traits<property_map<reverse_graph, PropertyTag>::type>::value_type
template <class GraphProperties, class GraphPropertyTag>
typename property_value<GraphProperties, GraphPropertyTag>::type&
get_property(reverse_graph& g, GraphPropertyTag);
Return the property specified by GraphPropertyTag that is attached to the graph object g. The property_value traits class is defined in boost/pending/property.hpp.
template <class GraphProperties, class GraphPropertyTag>
const typename property_value<GraphProperties, GraphPropertyTag>::type&
get_property(const reverse_graph& g, GraphPropertyTag);
Return the property specified by GraphPropertyTag that is attached to the graph object g. The property_value traits class is defined in boost/pending/property.hpp.
Copyright © 2000-2001
Dave Abrahams (david.abrahams@rcn.com)
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Sequential Vertex Coloring

sequential_vertex_coloring

template<class VertexListGraph, class OrderPA, class ColorMap>
typename property_traits<ColorMap>::value_type
sequential_vertex_coloring(const VertexListGraph& g, OrderPA order,
                           ColorMap color);

template<class VertexListGraph, class ColorMap>
typename property_traits<ColorMap>::value_type
sequential_vertex_coloring(const VertexListGraph& g, ColorMap color);

Computes a vertex coloring for the vertices in the graph, using a simple algorithm [59]. Given vertices ordered v1, v2, ... , vn, for k = 1, 2, ..., n the algorithm assigns vk to the smallest possible color. The algorithm does not guarantee an optimum coloring.

Here is the coloring that would be produced on a graph given the vertex ordering A, B, C, D, E.

,

Where Defined

boost/graph/sequential_vertex_coloring.hpp

Parameters

IN: const Graph& g
The graph object on which the algorithm will be applied. The type Graph must be a model of Vertex List Graph and Adjacency Graph.
Python: The parameter is named graph.
OUT: ColorMap color
This property map records the colors of each vertex. It must be a model of Writeable Property Map whose key type is the same as the vertex descriptor type of the graph and whose value type is an integral type that can store all values of the graph's vertices_size_type.
Python: Must be an vertex_int_map for the graph.
IN: OrderPA order
A mapping from integers in the range [0, num_vertices(g)) to the vertices of the graph.
Default: A property map ordering the vertices in the same way they are ordered by vertices(g).
Python: Unsupported parameter.

Complexity

The time complexity is O(V(d+k)), where V is the number of vertices, d is the maximum degree of the vertices in the graph, and k is the number of colors used.

Example

typedef adjacency_list<listS, vecS, undirectedS> Graph;
  typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
  typedef graph_traits<Graph>::vertices_size_type vertices_size_type;
  typedef property_map<Graph, vertex_index_t>::const_type vertex_index_map;

typedef std::pair<int, int> Edge;
  enum nodes {A, B, C, D, E, n};
  Edge edge_array[] = { Edge(A, C), Edge(B, B), Edge(B, D), Edge(B, E),
                        Edge(C, B), Edge(C, D), Edge(D, E), Edge(E, A),
                        Edge(E, B) };
  int m = sizeof(edge_array) / sizeof(Edge);
  Graph g(edge_array, edge_array + m, n);

// Test with the normal order
  std::vector<vertices_size_type> color_vec(num_vertices(g));
  iterator_property_map<vertices_size_type*, vertex_index_map>
    color(&color_vec.front(), get(vertex_index, g));
  vertices_size_type num_colors = sequential_vertex_coloring(g, color);

Copyright © 1997-2004
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)
Douglas Gregor, Indiana University (dgregor -at- cs.indiana.edu)) 		Top


Boost Graph Library: Sloan Ordering

sloan_ordering

Graphs: | undirected
Properties: | color, degree, current_degree, priority
Complexity: | time: O(log(m)|E|) where m = max { degree(v) | v in V }
  (1)
  template <class Graph, class OutputIterator,
                  class ColorMap, class DegreeMap,
                  class PriorityMap, class Weight>
  OutputIterator
  sloan_ordering(Graph& g,
                 typename graph_traits<Graph>::vertex_descriptor s,
                 typename graph_traits<Graph>::vertex_descriptor e,
                 OutputIterator permutation,
                 ColorMap color,
                 DegreeMap degree,
                 PriorityMap priority,
                 Weight W1,
                 Weight W2 )

  (2)
   template <class Graph, class OutputIterator,
                  class ColorMap, class DegreeMap,
                  class PriorityMap, class Weight>
  OutputIterator
  sloan_ordering(Graph& g,
                 OutputIterator permutation,
                 ColorMap color,
                 DegreeMap degree,
                 PriorityMap priority,
                 Weight W1,
                 Weight W2 )

(3)
 template <class Graph, class OutputIterator,
                  class ColorMap, class DegreeMap,
                  class PriorityMap>
  OutputIterator
  sloan_ordering(Graph& g,
                 typename graph_traits<Graph>::vertex_descriptor s,
                 typename graph_traits<Graph>::vertex_descriptor e,
                 OutputIterator permutation,
                 ColorMap color,
                 DegreeMap degree,
                 PriorityMap priority )

(4)
 template <class Graph, class OutputIterator,
                  class ColorMap, class DegreeMap,
                  class PriorityMap>
  OutputIterator
  sloan_ordering(Graph& g,
                 OutputIterator permutation,
                 ColorMap color,
                 DegreeMap degree,
                 PriorityMap priority )

The goal of the Sloan ordering algorithm[1, 2] is to reduce the profile and the wavefront of a graph by reordering the indices assigned to each vertex. The Sloan algorithm needs a start and an end vertex. These vertices can be asigned manually. But there is also an algorithm sloan_starting_nodes that provides usually quite good start and end vertices. Each vertex is asigned with a priority. This priority is a weighted sum of the distance of the vector to the end vertex (a global criterion) and is called the current degree of vertex. This current degree basically reflects the status of the renumbering in the neighborhood of a vertex (a local criterion). Therefore the Sloan algorithm (in contrast to-McKee) takes into account local as well as global criteria for the renumbering sequence. One can play around with the relative weights, but the default values proposed by Sloan (weight1/weight2=1/2) turn out to be pretty good in most cases.

Version 1 of the algorithm lets the user choose the start- and end-vertex whereas version 2 finds a good starting vertex using the already mentioned sloan_starting_node algorithm. The choice of these vertices can have a significant effect on the quality of the ordering. Version 3 and 4 are identical to version 1 and 2 respectively, except that for the weights the standard weights W1=1 and W2=2 are used.

The output of the algorithm are the vertices in the new ordering. Depending on what kind of output iterator is used, either the Sloan ordering or the reverse Sloan ordering is obtained. For example, if the output into a vector using the vector's reverse iterator is storred, then the reverse Sloan ordering is obtained.

  std::vector<vertex_descriptor> inv_perm(num_vertices(G));
  sloan_ordering(G, inv_perm.rbegin());

Either way, storing the output into a vector gives the permutation from the new ordering to the old ordering.

  inv_perm[new_index[u]] == u

Sometimes, it is the opposite permutation the permutation from the old index to the new index. This can easily be computed in the following way.

  for (size_type i = 0; i != inv_perm.size(); ++i)
    perm[old_index[inv_perm[i]]] = i;

Usually the user needs the reversed ordering with the Cuthill-McKee algorithm and the direct ordering with the Sloan algorithm.

Parameters

For version 1:
  • Graph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
  • vertex_descriptor s  (IN)
    The starting vertex.
  • vertex_descriptor e (IN)
    The ending vertex
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
  • PriorityMap priority_map  (IN)
    Used internally to store the priority for renumbering of each vertex.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
  • Weight W1 & W2  (IN)
    Heuristical weights for the Sloan algorithm.

For version 2:

  • Graph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
  • PriorityMap priority_map  (IN)
    Used internally to store the priority for renumbering of each vertex.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.
  • Weight W1 & W2  (IN)
    Heuristical weights for the Sloan algorithm.

For version 3:

  • Graph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
  • vertex_descriptor s  (IN)
    The starting vertex.
  • vertex_descriptor e (IN)
    The ending vertex
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
  • PriorityMap priority_map  (IN)
    Used internally to store the priority for renumbering of each vertex.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.

For version 4:

  • Graph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
  • OutputIterator permutation  (OUT)
    The new vertex ordering. The vertices are written to the output iterator in their new order.
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
  • PriorityMap priority_map  (IN)
    Used internally to store the priority for renumbering of each vertex.
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.

Example

See example/sloan_ordering.cpp.

See Also

sloan_start_end_vertices, bandwidth, profile, wavefront and degree_property_ma p in boost/graph/properties.hpp.

[1] S. W. Sloan, An algorithm for profile and wavefront reduction of sparse matrices, Int. j. numer. methods eng., 23, 239 - 251 (1986)

[2] S. W. Sloan, A fortran program for profile and wavefront reduction, Int. j. numer. methods eng., 28, 2651 - 2679 (1989)

Copyright © 2001-2002
Marc Wintermantel, ETH Zurich (wintermantel@imes.mavt.ethz.ch)


Boost Graph Library: Cuthill-Mckee Ordering

sloan_start_end_vertices

Graphs: | undirected
Properties: | color, degree
Complexity: |
  (1)
  template <class Graph, class ColorMap, class DegreeMap>
  typename graph_traits<Graph>::vertex_descriptor
  sloan_start_end_vertices(Graph& G,
                           typename graph_traits<Graph>::vertex_descriptor &s,
                           ColorMap color,
                           DegreeMap degree )

The goal of the sloan_start_end_vertices algorithm[1, 2] is to find good start- and end-vertices for the profile and wavefront reduction algorithm sloan_ordering. The algorithm is similar to pseudo_peripheral_pair and also based on breadth_first_search. With this breadth_first_search function a so-called rooted level structure (RLS) is formed, where the vertices with the same distance to the starting vertex are grouped together. The maximum number of vertices in one group is called the width of the RLS. Sloan_start_end_vertices tries to find a pseudoperipheral pair with a minimum RLS-width.

Parameters

For version 1:
  • Graph& g  (IN)
    An undirected graph. The graph's type must be a model of IncidenceGraph.
  • vertex_descriptor s  (IN)
    The starting vertex.
  • ColorMap color_map  (WORK)
    Used internally to keep track of the progress of the algorithm (to avoid visiting the same vertex twice).
  • DegreeMap degree_map  (IN)
    This must map vertices to their degree.

Example

See example/sloan_ordering.cpp.

See Also

sloan_start_end_vertices, bandwidth, profile, wavefront and degree_property_map in boost/graph/properties.hpp.

[1] S. W. Sloan, An algorithm for profile and wavefront reduction of sparse matrices, Int. j. numer. methods eng., 23, 239 - 251 (1986)

[2] S. W. Sloan, A fortran program for profile and wavefront reduction, Int. j. numer. methods eng., 28, 2651 - 2679 (1989)

Copyright © 2001-2002
Marc Wintermantel, ETH Zurich(wintermantel@imes.mavt.ethz.ch)


Boost Graph Library: Small World Generator

small_world_iterator

 small_world_iterator

template<typename RandomGenerator, typename Graph>
class small_world_iterator
{
public:
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<vertices_size_type, vertices_size_type> value_type;
  typedef const value_type& reference;
  typedef const value_type* pointer;
  typedef void difference_type;

  small_world_iterator();
  small_world_iterator(RandomGenerator& gen, vertices_size_type n,
                       vertices_size_type k, double probability,
                       bool allow_self_loops = false);
  // Iterator operations
  reference operator*() const;
  pointer operator->() const;
  small_world_iterator& operator++();
  small_world_iterator operator++(int);
  bool operator==(const small_world_iterator& other) const;
  bool operator!=(const small_world_iterator& other) const;
};

This class template implements a generator for small-world graphs, suitable for initializing an adjacency_list o r other graph structure with iterator-based initialization. A small-world graph consists of a ring graph (where each vertex is connected to its k nearest neighbors). Edges in the graph are randomly rewired to different vertices with a probability p. Small-world graphs exhibit a high clustering coefficient (because vertices are always connected to their closest neighbors), but rewiring ensures a small diameter.

Where Defined

boost/graph/small_world_generator.hpp

Constructors

small_world_iterator();
Constructs a past-the-end iterator.
small_world_iterator(RandomGenerator& gen, vertices_size_type n,
                     vertices_size_type k, double probability,
                     bool allow_self_loops = false);
Constructs a small-world generator iterator that creates a graph with n vertices, each connected to its k nearest neighbors. Probabilities are drawn from the random number generator gen. Self-loops are permitted only when allow_self_loops is true.

Example

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/small_world_generator.hpp>
#include <boost/random/linear_congruential.hpp>

typedef boost::adjacency_list<> Graph;
typedef boost::small_world_iterator<boost::minstd_rand, Graph> SWGen;

int main()
{
  boost::minstd_rand gen;
  // Create graph with 100 nodes
  Graph g(SWGen(gen, 100, 6, 0.03), SWGen(), 100);
  return 0;
}


Boost Graph Library: Erdös-Renyi Generator

sorted_erdos_renyi_iterator

sorted_erdos_renyi_iterator

template<;;;;;;;;;;typename RandomGenerator, typename Graph>
class sorted_erdos_renyi_iterator
{
public:
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<vertices_size_type, vertices_size_type> value_type;
  typedef const value_type& reference;
  typedef const value_type* pointer;
  typedef void difference_type;

  sorted_erdos_renyi_iterator();
  sorted_erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
                              double probability = 0.0, bool allow_self_loops = false);

  // Iterator operations
  reference operator*() const;
  pointer operator->() const;
  sorted_erdos_renyi_iterator& operator++();
  sorted_erdos_renyi_iterator operator++(int);
  bool operator==(const sorted_erdos_renyi_iterator& other) const;
  bool operator!=(const sorted_erdos_renyi_iterator& other) const;
};

This class template implements a generator for Erdös-Renyi graphs, suitable for initializing an adjacency_list or other graph structure with iterator-based initialization. An Erdös-Renyi graph G = (n, p) is a graph with n vertices that. The probability of having an edge (u, v) in G is p for any vertices u and v. Typically, there are no self-loops, but the generator can optionally introduce self-loops with probability p.

Erdös-Renyi graphs typically exhibit very little structure. For this reason, they are rarely useful in modeling real-world problems. However, they are often used when determining the theoretical complexity of complex graph algorithms.

Where Defined

boost/graph/erdos_renyi_generator.hpp

Constructors

sorted_erdos_renyi_iterator();

Constructs a past-the-end iterator.

sorted_erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
                            double probability = 0.0, bool allow_self_loops = false);

Constructs an Erdös-Renyi generator iterator that creates a graph with n vertices and a given probability of the total number of edges that a simple graph may have. probability. Random vertices and edges are selected using the random number generator gen. Self-loops are permitted only when allow_self_loops is true.

Example

#include <boost/graph//adjacency_list.hpp>
#include <boost/graph/erdos_renyi_generator.hpp>
#include <boost/random/linear_congruential.hpp>

typedef boost::adjacency_list<> Graph;
typedef boost::sorted_erdos_renyi_iterator<boost::minstd_rand, Graph> ERGen;

int main()
{
  boost::minstd_rand gen;
  // Create graph with 100 nodes and edges with probability 0.05
  Graph g(ERGen(gen, 100, 0.05), ERGen(), 100);
  return 0;
}


Sparse Matrix Ordering Example

Sparse Matrix Ordering

Graph theory was identified as a powerful tool for sparse matrix computation when Seymour Parter used undirected graphs to model symmetric Gaussian elimination more than 30 years ago [28]. Graphs can be used to model symmetric matrices, factorizations and algorithms on non-symmetric matrices, such as fill paths in Gaussian elimination, strongly connected components in matrix irreducibility, bipartite matching, and alternating paths in linear dependence and structural singularity. Not only do graphs make it easier to understand and analyze sparse matrix algorithms, but they broaden the area of manipulating sparse matrices using existing graph algorithms and techniques [13]. In this section, there is illustration as to how to use BGL in sparse matrix computation such as ordering algorithms. Review Graphs and sparse matrices before continuing with the sparse matrix algorithms.

Graphs and Sparse Matrices

A graph is fundamentally a way to represent a binary relation between objects. The nonzero pattern of a sparse matrix also describes a binary relation between unknowns. Therefore the nonzero pattern of a sparse matrix of a linear system can be modeled with a graph G(V,E), whose n vertices in V represent the n unknowns, and where there is an edge from vertex i to vertex j when Aij is nonzero. Thus, when a matrix has a symmetric nonzero pattern, the corresponding graph is undirected.

Sparse Matrix Ordering Algorithms

The process for solving a sparse symmetric positive definite linear system, Ax=b, can be divided into four stages as follows:

Ordering:
Find a permutation P of matrix A,
Symbolic factorization:
Set up a data structure for the Cholesky factor L of PAPT,
Numerical factorization:
Decompose PAPT into LLT,
Triangular system solution:
Solve LLTPx=Pb for x.
Because the choice of permutation P will directly determine the number of fill-in elements (elements present in the non-zero structure of L that are not present in the non-zero structure of A), the ordering has a significant impact on the memory and computational requirements for the latter stages. However, finding the optimal ordering for A (in the sense of minimizing fill-in) has been proven to be NP-complete [30] requiring that heuristics be used for all but simple (or specially structured) cases.

A widely used but rather simple ordering algorithm is a variant of the Cuthill-McKee orderings, the reverse Cuthill-McKee ordering algorithm. This algorithm can be used as a preordering method to improve ordering in more sophisticated methods such as minimum degree algorithms [21].

Reverse Cuthill-McKee Ordering Algorithm

The original Cuthill-McKee ordering algorithm is primarily designed to reduce the profile of a matrix [14]. George discovered that the reverse ordering often turned out to be superior to the original ordering in 1971. Here the Reverse Cuthill-McKee algorithm in terms of a graph is described:
  1. Finding a starting vertex: Determine a starting vertex r and assign r to x1.
  2. Main part: For i=1,...,N, find all the unnumbered neighbors of the vertex xi and number them in increasing order of degree.
  3. Reversing ordering: The reverse Cuthill-McKee ordering is given by y1,...,yN where yi is xN-i+1 for i = 1,...,N.
In the first step a good starting vertex needs to be determined. The study by George and Liu [14] showed that a pair of vertices which are at maximum or near maximum ``distance'' apart are good ones. They also proposed an algorithm to find such a starting vertex in [14], which is shown in Figure 1 implemented using the BGL interface.

namespace boost {
  template <class Graph, class Vertex, class Color, class Degree>
  Vertex
  pseudo_peripheral_pair(Graph& G, const Vertex& u, int& ecc,
                         Color color, Degree degree)
  {
    typename property_traits<Color>::value_type c = get(color, u);

    rcm_queue<Vertex, Degree> Q(degree);

    typename boost::graph_traits<Graph>::vertex_iterator ui, ui_end;
    for (tie(ui, ui_end) = vertices(G); ui != ui_end; ++ui)
      put(color, *ui, white(c));
    breadth_first_search(G, u, Q, bfs_visitor<>(), color);

    ecc = Q.eccentricity();
    return Q.spouse();
  }

  template <class Graph, class Vertex, class Color, class Degree>
  Vertex find_starting_node(Graph& G, Vertex r, Color c, Degree d) {
    int eccen_r, eccen_x;
    Vertex x = pseudo_peripheral_pair(G, r, eccen_r, c, d);
    Vertex y = pseudo_peripheral_pair(G, x, eccen_x, c, d);

    while (eccen_x > eccen_r) {
      r = x;
      eccen_r = eccen_x;
      x = y;
      y = pseudo_peripheral_pair(G, x, eccen_x, c, d);
    }
    return x;
  }
} // namespace boost
Figure 1: The BGL implementation of find_starting_node. The key part pseudo_peripheral_pair is BFS with a custom queue type virtually.

The key part of step one is a custom queue type with BFS as shown in Figure 1. The main Cuthill-McKee algorithm shown in Figure Figure 2 is a fenced priority queue with a generalized BFS. If inverse_permutation is a normal iterator, the result stored is the inverse permutation of original Cuthill-McKee ordering. On the other hand, if inverse_permutation is a reverse iterator, the result stored is the inverse permutation of reverse Cuthill-McKee ordering. Our implementation is quite concise because many components from BGL can be reused.

  template < class Graph, class Vertex, class OutputIterator,
             class Color, class Degree >
  inline void
  cuthill_mckee_ordering(Graph& G,
                         Vertex s,
                         OutputIterator inverse_permutation,
                         Color color, Degree degree)
  {
    typedef typename property_traits<Degree>::value_type DS;
    typename property_traits<Color>::value_type c = get(color, s);
    typedef indirect_cmp<Degree, std::greater<DS> > Compare;
    Compare comp(degree);
    fenced_priority_queue<Vertex, Compare > Q(comp);

    typedef cuthill_mckee_visitor<OutputIterator>  CMVisitor;
    CMVisitor cm_visitor(inverse_permutation);

    typename boost::graph_traits<Graph>::vertex_iterator ui, ui_end;
    for (tie(ui, ui_end) = vertices(G); ui != ui_end; ++ui)
      put(color, *ui, white(c));
    breadth_first_search(G, s, Q, cm_visitor, color);
  }
Figure 2: The BGL implementation of Cuthill-McKee algoithm.

Minimum Degree Ordering Algorithm

The pattern of another category of high-quality ordering algorithms in wide use is based on a greedy approach such that the ordering is chosen to minimize some quantity at each step of a simulated -step symmetric Gaussian elimination process. The algorithms using such an approach are typically distinguished by their greedy minimization criteria [34].

In graph terms, the basic ordering process used by most greedy algorithms is as follows:

  1. Start: Construct undirected graph G0 corresponding to matrix A
  2. Iterate: For k = 1, 2, ... until Gk = { } do:
    • Choose a vertex vk from Gk according to some criterion
    • Eliminate vk from Gk to form Gk+1
The resulting ordering is the sequence of vertices {v0, v1,...} selected by the algorithm.

One of the most important examples of such an algorithm is the Minimum Degree algorithm. At each step the minimum degree algorithm chooses the vertex with minimum degree in the corresponding graph as vk. A number of enhancements to the basic minimum degree algorithm have been developed, such as the use of a quotient graph representation, mass elimination, incomplete degree update, multiple elimination, and external degree. See [35] for a historical survey of the minimum degree algorithm.

The BGL implementation of the Minimum Degree algorithm closely follows the algorithmic descriptions of the one in [21]. The implementation presently includes the enhancements for mass elimination, incomplete degree update, multiple elimination, and external degree.

In particular, a graph representation to improve the performance of the algorithm is created. It is based on a templated ``vector of vectors.'' The vector container used is an adaptor class built on top the STL std::vector class. Particular characteristics of this adaptor class include the following:

  • Erasing elements does not shrink the associated memory. Adding new elements after erasing will not need to allocate additional memory.
  • Additional memory is allocated efficiently on demand when new elements are added (doubling the capacity every time it is increased). This property comes from STL vector.

Note that this representation is similar to that used in Liu's implementation, with some important differences due to dynamic memory allocation. With the dynamic memory allocation there is no need to over-write portions of the graph that have been eliminated, allowing for a more efficient graph traversal. More importantly, information about the elimination graph is preserved allowing for trivial symbolic factorization. Since symbolic factorization can be an expensive part of the entire solution process, improving its performance can result in significant computational savings.

The overhead of dynamic memory allocation could conceivably compromise performance in some cases. However, in practice, memory allocation overhead does not contribute significantly to run-time for our implementation as shown in [] because it is not done very often and the cost gets amortized.

Proper Self-Avoiding Walk

The finite element method (FEM) is a flexible and attractive numerical approach for solving partial differential equations since it is straightforward to handle geometrically complicated domains. However, unstructured meshes generated by FEM does not provide an obvious labeling (numbering) of the unknowns while it is vital to have it for matrix-vector notation of the underlying algebraic equations. Special numbering techniques have been developed to optimize memory usage and locality of such algorithms. One novel technique is the self-avoiding walk [].

If a mesh is a graph, a self-avoiding walk(SAW) over an arbitrary unstructured two-dimensional mesh is an enumeration of all the triangles of the mesh such that two successive triangles shares an edge or a vertex. A proper SAW is a SAW where jumping twice over the same vertex is forbidden for three consecutive triangles in the walk. it can be used to improve parallel efficiency of several irregular algorithms, in particular issues related to reducing runtime memory access (improving locality) and interprocess communications. The reference [] has proved the existence of A PSAW for any arbitrary triangular mesh by extending an existing PSAW over a small piece of mesh to a larger part. The proof effectively provides a set of rules to construct a PSAW.

The algorithm family of constructing a PSAW on a mesh is to start from any random triangle of the mesh, choose new triangles sharing an edge with the current sub-mesh and extend the existing partial PSAW over the new triangles.

Let us define a dual graph of a mesh. Let a triangle in the mesh be a vertex and two triangles sharing an edge in the mesh means there is an edge between two vertices in the dual graph. By using a dual graph of a mesh, one way to implement the algorithm family of constructing a PSAW is to reuse BGL algorithms such as BFS and DFS with a customized visitor to provide operations during traversal. The customized visitor has the function tree_edge() which is to extend partial PSAW in case by case and function start_vertex() which is to set up the PSAW for the starting vertex.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Stanford Graph Interface

Using SGB Graphs in BGL

The Boost Graph Library (BGL) header, <boost/graph/stanford_graph.hpp>, adapts a Stanford GraphBase (SGB) Graph pointer into a BGL-compatible VertexListGraph. Note that a graph adaptor class is not used; SGB's Graph* itself becomes a model of VertexListGraph. The VertexListGraph concept is fulfilled by defining the appropriate non-member functions for Graph*.

The Stanford GraphBase

"The Stanford GraphBase (SGB) is a collection of datasets and computer programs that generate and examine a wide variety of graphs and networks." The SGB was developed and published by Donald E. Knuth in 1993. The fully documented source code is available for anonymous ftp from Stanford University and in the book "The Stanford GraphBase, A Platform for Combinatorial Computing," published jointly by ACM Press and Addison-Wesley Publishing Company in 1993. (This book contains several chapters with additional information not available in the electronic distribution.)

Prerequisites

The source code of SGB is written in accordance with the rules of the Literate Programming paradigm, so you need to make sure that your computer supports the CWEB system. The CWEB sources are available for anonymous ftp from Stanford University. Bootstrapping CWEB on Unix systems is elementary and documented in the CWEB distribution; pre-compiled binary executables of the CWEB tools for Win32 systems are available from www.literateprogramming.com .

Installing the SGB

After you have acquired the SGB sources and have installed a working CWEB system (at least the "ctangle" processor is required), you're almost set for compiling the SGB sources. SGB is written in "old-style C," but the Boost Graph Library expects to handle "modern C" and C++. Fortunately, the SGB distribution comes with an appropriate set of patches that convert all the sources from "KR-C" to "ANSI-C," thus allowing for smooth integration of the Stanford GraphBase in the Boost Graph Library.
  • Unix: After extracting the SGB archive, but prior to invoking "make tests" and "make install," you should say "ln -s PROTOTYPES/*.ch ." in the root directory where you extracted the SGB files (or you can simply copy the change files next to the proper source files). The Unix Makefile coming with SGB conveniently looks for "change files" matching the SGB source files and automatically applies them with the "ctangle" processor. The resulting C files will smoothly run through the compiler.
  • Win32: The "MSVC" subdirectory of the SGB distribution contains a complete set of "Developer Studio Projects" (and a single "Workspace"), applicable with Microsoft Developer Studio 6. The installation process is documented in the accompanying file README.MSVC. The "MSVC" contribution has been updated to make use of the "PROTOTYPES" as well, so you don't need to worry about that.

Using the SGB

After you have run the installation process of the SGB, you can use the BGL graph interface with the SGB Graph*, <boost/graph/stanford_graph.hpp>, which will be described next. All you have to do is tell the C++ compiler where to look for the SGB headerfiles (by default, /usr/local/sgb/include on Unix and the "MSVC" subdirectory of the SGB installation on Win32) and the linker where to find the SGB static library file (libgb.a on Unix and libgb.lib on Win32); consult the documentation of your particular compiler about how to do that.

Technicalities

  • Headerfile selection: The two SGB modules gb_graph and gb_io use the preprocessor switch SYSV to select either the headerfile <string.h> (if SYSV is #defined) or the headerfile <strings.h> (if SYSV is not #defined). Some compilers, like gcc/g++, don't care much (gcc "knows" about the "string" functions without refering to <string.h>), but others, like MSVC on Win32, do (so all "Developer Studio Projects" in the "MSVC" subdirectory of the SGB distribution appropriately define SYSV). You should be careful to set (or not) SYSV according to the needs of your compiler.
  • Missing include guards: None of the SGB headerfiles uses "internal include guards" to protect itself from multiple inclusion. To avoid trouble, you must not #include any of the SGB headerfiles before or after the BGL wrapper in a compilation unit; it will fully suffice to use the BGL interface.
  • Preprocessor macros: The SGB headerfiles make liberal use of the preprocessor without sticking to a particular convention (like all-uppercase names or a particular prefix). At the time of writing, already three of these preprocessor macros collide with the conventions of either C++, g++, or BGL, and are fixed in the BGL wrapper. We can not guarantee that no other preprocessor-induced problems may arise (but we are willing to learn about any such collisions).

The BGL Interface for the SGB

Where Defined

<boost/graph/stanford_graph.hpp>

The main purpose of this Boost Graph Library (BGL) headerfile is to #include all global definitions for the general stuff of the Stanford GraphBase (SGB) and its various graph generator functions by reading all SGB headerfiles as in section 2 of the "test_sample" program.

On top of the SGB stuff, the BGL stanford_graph.hpp header adds and defines appropriate types and functions for using the SGB graphs in the BGL framework. Apart from the improved interface, the SGB (static) library is used "as is" in the context of BGL.

Model Of

Vertex List Graph and Property Graph. The set of property tags that can be used with the SGB graph is described in the Vertex and Edge Properties section below.

Example

The example program <example/miles_span.cpp> represents the first application of the generic framework of BGL to an SGB graph. It uses Prim's algorithm to solve the "minimum spanning tree" problem. In addition, the programs <example/girth.cpp> and <example/roget_components.cpp> have been ported from the SGB. We intend to implement more algorithms from SGB in a generic fashion and to provide the remaining example programs of SGB for the BGL framework. If you would like to help, feel free to submit your contributions!

Associated Types

graph_traits<Graph*>::vertex_descriptor

The type for the vertex descriptors associated with the Graph*. We use the type Vertex* as the vertex descriptor.
graph_traits<Graph*>::edge_descriptor

The type for the edge descriptors associated with the Graph*. This is the type boost::sgb_edge. In addition to supporting all the required operations of a BGL edge descriptor, the boost::sgb_edge class has the following constructor.
   sgb_edge::sgb_edge(Arc* arc, Vertex* source)
graph_traits<Graph*>::vertex_iterator

The type for the iterators returned by vertices().
graph_traits<Graph*>::out_edge_iterator

The type for the iterators returned by out_edges().
graph_traits<Graph*>::adjacency_iterator

The type for the iterators returned by adjacent_vertices().
graph_traits<Graph*>::vertices_size_type

The type used for dealing with the number of vertices in the graph.
graph_traits<Graph*>::edge_size_type

The type used for dealing with the number of edges in the graph.
graph_traits<Graph*>::degree_size_type

The type used for dealing with the number of edges incident to a vertex in the graph.
graph_traits<Graph*>::directed_category

Provides information about whether the graph is directed or undirected. An SGB Graph* is directed so this type is directed_tag.
graph_traits<Graph*>::traversal_category

An SGB Graph* provides traversal of the vertex set, out edges, and adjacent vertices. Therefore the traversal category tag is defined as follows:
struct sgb_traversal_tag :
  public virtual vertex_list_graph_tag,
  public virtual incidence_graph_tag,
  public virtual adjacency_graph_tag { };
graph_traits<Graph*>::edge_parallel_category

This describes whether the graph class allows the insertion of parallel edges (edges with the same source and target). The SGB Graph* does not prevent addition of parallel edges, so this type is allow_parallel_edge_tag.

Non-Member Functions

std::pair<vertex_iterator, vertex_iterator>
vertices(Graph* g)
Returns an iterator-range providing access to the vertex set of graph g.
std::pair<out_edge_iterator, out_edge_iterator>
out_edges(vertex_descriptor v, Graph* g)
Returns an iterator-range providing access to the out-edges of vertex v in graph g.
There is no corresponding in_edges function.
std::pair<adjacency_iterator, adjacency_iterator>
adjacent_vertices(vertex_descriptor v, Graph* g)
Returns an iterator-range providing access to the adjacent vertices of vertex v in graph g.
vertex_descriptor
source(edge_descriptor e, Graph* g)
Returns the source vertex of edge e.
vertex_descriptor
target(edge_descriptor e, Graph* g)
Returns the target vertex of edge e.
degree_size_type
out_degree(vertex_descriptor v, Graph* g)
Returns the number of edges leaving vertex v.
There is no corresponding in_degree function.
vertices_size_type
num_vertices(Graph* g)
Returns the number of vertices in the graph g.
edge_size_type
num_edges(Graph* g)
Returns the number of edges in the graph g.
vertex_descriptor
vertex(vertices_size_type n, Graph* g)
Returns the (0-based) nth vertex in the graph's vertex list.
template <class PropertyTag>
property_map<Graph*, PropertyTag>::type
get(PropertyTag, Graph*& g)

template <class PropertyTag>
property_map<Graph*, Tag>::const_type
get(PropertyTag, const Graph*& g)
Returns the property map object for the vertex property specified by PropertyTag. The PropertyTag must be one of the described below.

Vertex and Edge Properties

The SGB Vertex and Arc structures provide "utility" fields for storing extra information. We provide BGL wrappers that provide access to these fields through property maps. In addition, vertex index and edge length maps are provided. A property map object can be obtained from a SGB Graph* using the get() function described in the Property Graph concept.

The following list of property tags can be used to specify which utility field you would like a property map for.

// vertex properties
template <class T> u_property;
template <class T> v_property;
template <class T> w_property;
template <class T> x_property;
template <class T> y_property;
template <class T> z_property;

// edge properties
template <class T> a_property;
template <class T> b_property;

The template parameter T for these tags is limited to the types in the util union declared in the SGB header gb_graph.h, which are Vertex*, Arc*, Graph*, char*, and long. The property maps for the utility fields are models of Lvalue Property Map.

The property map for vertex indices can be obtained using the vertex_index_t tag, and this property map is a Readable Property Map. A property map for edge length's can be obtained using the edge_length_t tag, and this property map is a Lvalue Property Map whose value type is long.

Property map objects can be obtained via the get() function of the Property Graph concept. The type of the property map is given by the property_map traits class.

Copyright © 2001
Andreas Scherer, Aachen (andreas_freenet@freenet.de)
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Strongly Connected Components

strong_components

strong_components
// named parameter version
template <class Graph, class ComponentMap, class P, class T, class R>
typename property_traits<ComponentMap>::value_type
strong_components(Graph& g, ComponentMap comp,
    const bgl_named_params<P, T, R>& params = all defaults)

// there is not a non-named parameter version of this function

The strong_components() functions compute the strongly connected components of a directed graph using Tarjan's algorithm based on DFS [41].

The output of the algorithm is recorded in the component property map comp, which will contain numbers giving the component ID assigned to each vertex. The number of components is the return value of the function.

Where Defined

boost/graph/strong_components.hpp

Definitions

A strongly connected component of a directed graph G=(V,E) is a maximal set of vertices U which is in V such that for every pair of vertices u and v in U, there is both a path from u to v and path from v to u. That is to say that u and v are reachable from each other.

Parameters

IN: const Graph& g
A directed graph. The graph type must be a model of Vertex List Graph and Incidence Graph.
Python: The parameter is named graph.
OUT: ComponentMap c
The algorithm computes how many connected components are in the graph, and assigning each component an integer label. The algorithm then records which component each vertex in the graph belongs to by recording the component number in the component property map. The ComponentMap type must be a model of Writable Property Map. The value type should be an integer type, preferably the same as the vertices_size_type of the graph. The key type must be the graph's vertex descriptor type.
Python: Must be an vertex_int_map for the graph.
Python default: graph.get_vertex_int_map("component")

Named Parameters

UTIL: root_map(RootMap r_map)
This is used by the algorithm to record the candidate root vertex for each vertex. By the end of the algorithm, there is a single root vertex for each component and get(r_map, v) returns the root vertex for whichever component vertex v is a member. The RootMap must be a Read/Write Property Map, where the key type and the value type are the vertex descriptor type of the graph.
Default: an iterator_property_map created from a std::vector of vertex descriptors of size num_vertices(g) and using the i_map for the index map.
Python: Unsupported parameter.
UTIL: discover_time_map(TimeMap t_map)
This is used by the algorithm to keep track of the DFS ordering of the vertices. The TimeMap must be a model of Read/Write Property Map and its value type must be an integer type. The key type must be the vertex descriptor type of the graph.
Default:an iterator_property_map created from a std::vector of integers with size num_vertices(g) and using the i_map for the index map.
Python: Unsupported parameter.
UTIL: color_map(ColorMap c_map)
This is used by the algorithm to keep track of its progress through the graph. The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: Unsupported parameter.
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This parameter is only necessary when a default is used for one of the other named parameters. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note: if this is used as default, make sure that the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity for the strongly connected components algorithm is O(V + E).

See Also

connected_components() and incremental_components()

Example

See examples/strong_components.cpp.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Subgraph

subgraph
subgraph<Graph>

The subgraph class provides a mechanism for keeping track of a graph and its subgraphs. A graph G' is a subgraph of a graph G if the vertex set of G' is a subset of the vertex set of G and if the edge set of G' is a subset of the edge set of G. That is, if G'=(V',E') and G=(V,E), then G' is a subgraph of G if V' is a subset of V and E is a subset of E'. An induced subgraph is a subgraph formed by specifying a set of vertices V' and then selecting all of the edges from the original graph that connect two vertices in V'. So in this case E' = {(u,v) in E: u,v in V'}. Figure 1 shows a graph G0 and two subgraphs G1 and G2. The edge set for G1 is E1 = { (E,F), (C,F) } and the edge set for G2 is E2 = { (A,B) }. Edges such as (E,B) and (F,D) that cross out of a subgraph are not in the edge set of the subgraph.

Figure 1: A graph with nested subgraphs, maintained in a tree structure.

The subgraph class implements induced subgraphs. The main graph and its subgraphs are maintained in a tree data structure. The main graph is the root, and subgraphs are either children of the root or of other subgraphs. All of the nodes in this tree, including the root graph, are instances of the subgraph class. The subgraph implementation ensures that each node in the tree is an induced subgraph of its parent. The subgraph class implements the BGL graph interface, so each subgraph object can be treated as a graph.

Example

The full source code for this example is in example/subgraph.cpp. To create a graph and subgraphs, first create the root graph object. Here we use adjacency_list as the underlying graph implementation. The underlying graph type is required to have vertex_index and edge_index internal properties, so we add an edge index property to the adjacency list. We do not need to add a vertex index properety because that is built in to the adjacency_list. We will be building the graph and subgraphs in Figure 1, so we will need a total of six vertices.
typedef adjacency_list_traits Traits;
typedef subgraph< adjacency_list > > Graph;

const int N = 6;
Graph G0(N);

enum { A, B, C, D, E, F};  // for conveniently refering to vertices in G0
Next we create two empty subgraph objects, specifying G0 as their parent.
Graph& G1 = G0.create_subgraph(), G2 = G0.create_subgraph();
enum { A1, B1, C2 }; // for conveniently refering to vertices in G1
enum { A2, B2 };     // for conveniently refering to vertices in G2
We can add vertices from the root graph to the subgraphs using the add_vertex function. Since the graph implementation is adjacency_list with VertexList=vecS, we can use the integers (or in this case enums) in the range [0,6) as vertex descriptors.
add_vertex(C, G1); // global vertex C becomes local A1 for G1
add_vertex(E, G1); // global vertex E becomes local B1 for G1
add_vertex(F, G1); // global vertex F becomes local C1 for G1

add_vertex(A, G2); // global vertex A becomes local A2 for G2
add_vertex(B, G2); // global vertex B becomes local B2 for G2
Next we can add edges to the main graph using the usual add_edge function.
add_edge(A, B, G0);
add_edge(B, C, G0);
add_edge(B, D, G0);
add_edge(E, B, G0);
add_edge(E, F, G0);
add_edge(F, D, G0);
We can also add edges to subgraphs such as G1 using the add_edge function. Each subgraph has its own vertex and edge descriptors, which we call local descriptors. We refer to root graph's vertex and edge descriptors as the global descriptors. Above, we used global vertex descriptors to add vertices to the graph. However, most subgraph functions work with local descriptors. So in the following call to add_edge we add the edge (A1,C1) (or numerically (0,2)) which is the local version (for subgraph G1) of the global edge (C,F) (or numerically (2,5)). Adding an edge to a subgraph causes the edge to also be added to all of its ancestors in the subgraph tree to ensure that the subgraph property is maintained.
add_edge(A1, C1, G1); // (A1,C1) is subgraph G1 local indicess
                      // for the global edge (C,F).

Where Defined

boost/graph/subgraph.hpp

Template Parameters

Parameter | Description
Graph | A graph type modeling VertexMutableGraph and EdgeMutableGraph. Also the graph must have internal vertex_index and edge_index properties. The vertex indices must be maintained automatically by the graph, whereas the edge indices will be assigned by the subgraph class implementation.

Model Of

subgraph is a model of VertexMutableGraph. Also, if the Graph type models VertexListGraph, EdgeListGraph and/or BidirectionalGraph, then subgraph<Graph> will also models these concepts.

Associates Types

If the graph is the root of the subgraph tree, then the vertex and edge descriptors are both the local descriptors for the root graph, and they are the global descriptors. If the graph is not the root, then the descriptors are local descriptors for the subgraph. The subgraph iterators are the same iterator types as the iterators of the underlying Graph type.
graph_traits<subgraph>::vertex_descriptor
The type for the vertex descriptors. (Required by Graph.)
graph_traits<subgraph>::edge_descriptor
The type for the edge descriptors. (Required by Graph.)
graph_traits<subgraph>::vertex_iterator
The type for the iterators returned by vertices. (Required by VertexListGraph.)
graph_traits<subgraph>::edge_iterator
The type for the iterators returned by edges. (Required by EdgeListGraph.)
graph_traits<subgraph>::out_edge_iterator
The type for the iterators returned by out_edges. (Required by IncidenceGraph.)
graph_traits<subgraph>::in_edge_iterator
The in_edge_iterator is the iterator type returned by the in_edges function. (Required by BidirectionalGraph.)
graph_traits<subgraph>::adjacency_iterator
The type for the iterators returned by adjacent_vertices. (Required by AdjacencyGraph.)
graph_traits<subgraph>::directed_category
Provides information about whether the graph is directed (directed_tag) or undirected (undirected_tag). (Required by Graph.)
graph_traits<subgraph>::edge_parallel_category
This describes whether the graph class allows the insertion of parallel edges (edges with the same source and target), which depends on the underlying Graph class. The two tags are allow_parallel_edge_tag and disallow_parallel_edge_tag. (Required by Graph.)
graph_traits<subgraph>::vertices_size_type
The type used for dealing with the number of vertices in the graph. (Required by VertexListGraph.)
graph_traits<subgraph>::edges_size_type
The type used for dealing with the number of edges in the graph. (Required by EdgeListGraph.)
graph_traits<subgraph>::degree_size_type
The type used for dealing with the number of out-edges of a vertex. (Required by IncidenceGraph.)
property_map<subgraph, PropertyTag>::type
property_map<subgraph, PropertyTag>::const_type
The map type for vertex or edge properties in the graph. The specific property is specified by the PropertyTag template argument, and must match one of the properties specified in the VertexProperty or EdgeProperty for the graph. (Required by PropertyGraph.)
subgraph::children_iterator
The iterator type for accessing the children subgraphs of the graph.

Member Functions

subgraph(vertices_size_type n, const GraphProperty& p = GraphProperty())
Creates the root graph object with n vertices and zero edges.
subgraph<Graph>& create_subgraph();
Creates an empty subgraph object whose parent is this graph.
template <typename VertexIterator>
subgraph<Graph>&
create_subgraph(VertexIterator first, VertexIterator last)
Creates a subgraph object with the specified vertex set. The edges of the subgraph are induced by the vertex set. That is, every edge in the parent graph (which is this graph) that connects two vertices in the subgraph will be added to the subgraph.
vertex_descriptor local_to_global(vertex_descriptor u_local) const
Converts a local vertex descriptor to the corresponding global vertex descriptor.
vertex_descriptor global_to_local(vertex_descriptor u_global) const
Converts a global vertex descriptor to the corresponding local vertex descriptor.
edge_descriptor local_to_global(edge_descriptor e_local) const
Converts a local edge descriptor to the corresponding global edge descriptor.
edge_descriptor global_to_local(edge_descriptor u_global) const
Converts a global edge descriptor to the corresponding local edge descriptor.
std::pair<vertex_descriptor, bool> find_vertex(vertex_descriptor u_global) const
If vertex u is in this subgraph, the function returns the local vertex descriptor that corresponds to the global vertex descriptor u_global as the first part of the pair and true for the second part of the pair. If vertex u is not in the subgraph then this function returns false in the second part of the pair.
subgraph& root()
Returns the root graph of the subgraph tree.
bool is_root() const
Return true if the graph is the root of the subgraph tree, and returns false otherwise.
subgraph& parent()
Returns the parent graph.
std::pair<children_iterator, children_iterator> children() const
Return an iterator pair for accessing the children subgraphs.

Nonmember Functions

The functionality of subgraph depends on the Graph type. For example, if Graph in a BidirectionalGraph and supports in_edges, then so does subgraph. Here we list all the functions that subgraph could possibly support given a Graph type that is a model of VertexListGraph, EdgeListGraph and BidirectionalGraph. If the Graph type that you use with subgraph does not model these concepts and supports fewer functions, then the subgraph will also support fewer functions and some of the functions listed below will not be implemented.
std::pair<vertex_iterator, vertex_iterator>
vertices(const subgraph& g)
Returns an iterator range providing access to the vertex set of subgraph g. (Required by VertexListGraph.)
std::pair<edge_iterator, edge_iterator>
edges(const subgraph& g)
Returns an iterator range providing access to the edge set of subgraph g. (Required by EdgeListGraph.)
std::pair<adjacency_iterator, adjacency_iterator>
adjacent_vertices(vertex_descriptor u_local, const subgraph& g)
Returns an iterator range providing access to the vertices adjacent to vertex u in subgraph g. (Required by AdjacencyGraph.)
std::pair<out_edge_iterator, out_edge_iterator>
out_edges(vertex_descriptor u_local, const subgraph& g)
Returns an iterator range providing access to the out-edges of vertex u in subgraph g. If the graph is undirected, this iterator range provides access to all edge incident on vertex u. (Required by IncidenceGraph.)
std::pair<in_edge_iterator, in_edge_iterator>
in_edges(vertex_descriptor v_local, const subgraph& g)
Returns an iterator range providing access to the in-edges of vertex v in subgraph g. (Required by BidirectionalGraph.)
vertex_descriptor
source(edge_descriptor e_local, const subgraph& g)
Returns the source vertex of edge e in subgraph g. (Required by IncidenceGraph.)
vertex_descriptor
target(edge_descriptor e_local, const subgraph& g)
Returns the target vertex of edge e in subgraph g. (Required by IncidenceGraph.)
degree_size_type
out_degree(vertex_descriptor u_local, const subgraph& g)
Returns the number of edges leaving vertex u in subgraph g. (Required by IncidenceGraph.)
degree_size_type in_degree(vertex_descriptor u_local, const subgraph& g)
Returns the number of edges entering vertex u in subgraph g. (Required by BidirectionalGraph.)
vertices_size_type num_vertices(const subgraph& g)
Returns the number of vertices in the subgraph g. (Required by VertexListGraph.)
edges_size_type num_edges(const subgraph& g)
Returns the number of edges in the subgraph g. (Required by EdgeListGraph.)
vertex_descriptor vertex(vertices_size_type n, const subgraph& g)
Returns the nth vertex in the subgraph's vertex list.
std::pair<edge_descriptor, bool>
edge(vertex_descriptor u_local, vertex_descriptor v_local, const subgraph& g)
Returns the edge connecting vertex u to vertex v in subgraph g. (Required by AdjacencyMatrix.)
std::pair<edge_descriptor, bool>
add_edge(vertex_descriptor u_local, vertex_descriptor v_local, subgraph& g)
Adds edge (u,v) to the subgraph g and to all of the subgraph's ancestors in the subgraph tree. This function returns the edge descriptor for the new edge. If the edge is already in the graph then a duplicate will not be added and the Boolean flag will be false. (Required by EdgeMutableGraph.)
std::pair<edge_descriptor, bool>
add_edge(vertex_descriptor u_local, vertex_descriptor v_local,
         const EdgeProperty& p, subgraph& g)
Adds edge (u,v) to the graph and attaches p as the value of the edge's internal property storage. Also see the previous add_edge member function for more details.
void remove_edge(vertex_descriptor u_local, vertex_descriptor v_local,
                 subgraph& g)
Removes the edge (u,v) from the subgraph and from all of the ancestors of g in the subgraph tree. (Required by EdgeMutableGraph.)
void remove_edge(edge_descriptor e_local, subgraph& g)
Removes the edge e from the subgraph and from all of the ancestors of g in the subgraph tree. (Required by EdgeMutableGraph.)
vertex_descriptor
add_vertex(subgraph& g)
Adds a vertex to the subgraph and returns the vertex descriptor for the new vertex. The vertex is also added to all ancestors of g in the subgraph tree to maintain the subgraph property. (Required by VertexMutableGraph.)
vertex_descriptor
add_vertex(vertex_descriptor u_global, subgraph& g)
Adds the vertex u from the root graph to the subgraph g. (Required by VertexMutableGraph.)
template <class PropertyTag>
property_map<subgraph, PropertyTag>::type
get(PropertyTag, subgraph& g)

template <class PropertyTag>
property_map<subgraph, PropertyTag>::const_type
get(PropertyTag, const subgraph& g)
Returns the property map object for the vertex or edge property specified by PropertyTag. The PropertyTag must match one of the properties specified in the graph's PropertyTag template argument. Vertex and edge properties are shared by all subgraphs, so changes to a property through a local vertex descriptor for one subgraph will change the property for the global vertex descriptor, and therefore for all other subgraphs. However, the key type for a subgraph's property map is a subgraph-local vertex or edge descriptor. (Required by PropertyGraph.)
template <class PropertyTag, class Key>
typename property_traits<
  typename property_map<subgraph, PropertyTag>::const_type
>::value_type
get(PropertyTag, const subgraph& g, Key k_local)
This returns the property value for the key k_local, which is either a local vertex or local edge descriptor. See the above get function for more information about the property maps. (Required by PropertyGraph.)
template <class PropertyTag, class Key, class Value>
void
put(PropertyTag, const subgraph& g, Key k_local, const Value& value)
This sets the property value for the key k_local to value. k_local is either a local vertex or local edge descriptor. Value must be convertible to typename property_traits<property_map<adjacency_matrix, PropertyTag>::type>::value_type. (Required by PropertyGraph.)
template <class GraphProperties, class GraphPropertyTag>
typename property_value<GraphProperties, GraphPropertyTag>::type&
get_property(subgraph& g, GraphPropertyTag);
Return the property specified by GraphPropertyTag that is attached to the subgraph object g. The property_value traits class is defined in boost/pending/property.hpp.
template <class GraphProperties, class GraphPropertyTag>
const typename property_value<GraphProperties, GraphPropertyTag>::type&
get_property(const subgraph& g, GraphPropertyTag);
Return the property specified by GraphPropertyTag that is attached to the subgraph object g. The property_value traits class is defined in boost/pending/property.hpp.


Table of Contents: Boost Graph Library

Table of Contents: the Boost Graph Library

  1. Introduction to the BGL

  2. History

  3. List of BGL Users

  4. Publications

  5. Acknowledgements

  6. A Quick Tour of the Boost Graph Library.

  7. Review of Elementary Graph Theory

  8. Boost Graph Library Tutorial

    1. Property Maps

    2. The adjacency_list class

  9. Examples

    1. File Dependency Example

    2. Six Degrees of Kevin Bacon

    3. Graph Coloring

    4. Sparse Matrix Ordering

  10. Extending the Boost Graph Library

    1. Constructing graph algorithms with BGL

    2. Converting Existing Graphs to BGL

  11. The Boost Graph Interface

    1. Graph

    2. Incidence Graph

    3. Bidirectional Graph

    4. Adjacency Graph

    5. Vertex List Graph

    6. Edge List Graph

    7. Vertex and Edge List Graph

    8. Mutable Graph

    9. Property Graph

    10. Mutable Property Graph

  12. The Property Map Library (technically not part of the graph library, but used a lot here)

  13. Python bindings

  14. Visitor Concepts

    1. BFS Visitor

    2. DFS Visitor

    3. Dijkstra Visitor

    4. Bellman Ford Visitor

    5. A* Visitor

    6. Event Visitor

  15. EventVisitorList Adaptors

    1. Event Visitor List

    2. bfs_visitor

    3. dfs_visitor

    4. dijkstra_visitor

    5. bellman_visitor

    6. astar_visitor

  16. Event Visitors

    1. predecessor_recorder

    2. distance_recorder

    3. time_stamper

    4. property_writer

  17. Graph classes

    1. adjacency_list

    2. adjacency_matrix

    3. compressed_sparse_row_graph

  18. Graph Adaptors

    1. subgraph

    2. edge_list

    3. reverse_graph

    4. filtered_graph

    5. Vector as Graph *

    6. Matrix as Graph*

    7. Leda Graph *

    8. Stanford GraphBase

  19. Iterator Adaptors

    1. adjacency_iterator

    2. inv_adjacency_iterator

  20. Traits classes

    1. graph_traits

    2. adjacency_list_traits

    3. property_map

  21. Algorithms

    1. bgl_named_params

    2. Core Algorithm Patterns

      1. breadth_first_search

      2. breadth_first_visit

      3. depth_first_search

      4. depth_first_visit

      5. undirected_dfs

    3. Graph Algorithms

      1. Shortest Paths Algorithms

        1. dijkstra_shortest_paths

        2. bellman_ford_shortest_paths

        3. dag_shortest_paths

        4. johnson_all_pairs_shortest_paths

        5. floyd_warshall_all_pairs_shortest_paths

      2. Minimum Spanning Tree Algorithms

        1. kruskal_minimum_spanning_tree

        2. prim_minimum_spanning_tree

      3. Connected Components Algorithms

        1. connected_components

        2. strong_components

        3. biconnected_components

        4. articulation_points

        5. Incremental Connected Components

          1. initialize_incremental_components

          2. incremental_components

          3. same_component

          4. component_index

      4. Maximum Flow and Matching Algorithms

        1. edmunds_karp_max_flow

        2. push_relabel_max_flow

        3. edmonds_maximum_cardinality_matching

      5. Sparse Matrix Ordering Algorithms

        1. cuthill_mckee_ordering

        2. king_ordering

        3. minimum_degree_ordering

      6. topological_sort

      7. transitive_closure

      8. copy_graph

      9. transpose_graph

      10. isomorphism

      11. sequential_vertex_coloring

      12. sloan_ordering

      13. sloan_start_end_vertices

      14. ith_wavefront, max_wavefront, aver_wavefront, and rms_wavefront

      15. brandes_betweenness_centrality

      16. Layout algorithms

        1. random_graph_layout

        2. circle_layout

        3. kamada_kawai_spring_layout

        4. fruchterman_reingold_force_directed_layout

        5. gursoy_atun_layout

      17. Clustering algorithms

        1. betweenness_centrality_clustering

      18. astar_search

      19. lengauer_tarjan_dominator_tree

  22. AT&T Graphviz Read/Write Utilities

    1. write_graphviz

    2. read_graphviz

  23. Auxiliary Concepts, Classes, and Functions

    1. property

    2. ColorValue

    3. Buffer

    4. BasicMatrix

    5. incident

    6. opposite

    7. bandwidth

    8. ith_bandwidth

    9. Tools for random graphs

      1. random_vertex

      2. random_edge

      3. generate_random_graph

      4. randomize_property

      5. erdos_renyi_iterator

      6. sorted_erdos_renyi_iterator

      7. plod_iterator

      8. small_world_iterator

  24. Challenge and To-Do List

  25. Trouble Shooting

  26. Known Problems

  27. FAQ

  28. BGL Book Errata

* Items marked have not yet been documented.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: time_stamper

time_stamper

time_stamper<TimeMap, TimeT, EventTag>

This is an EventVisitor that can be used to "stamp" a time at some event-point within an algorithm. An example of this is recording the discover or finish time of a vertex during a graph search.

time_stamper can be used with graph algorithms by wrapping it with the algorithm specific adaptor, such as bfs_visitor and dfs_visitor. Also, this event visitor can be combined with other event visitors using std::pair to form an EventVisitorList.

Example

The following example shows the usage of the time_stamper.
  std::vector color(num_vertices(G));
  std::vector dtime(num_vertices(G));
  std::vector ftime(num_vertices(G));

  int time = 0;
  boost::breadth_first_search
    (G, vertex(s, G), make_bfs_visitor(
     std::make_pair(stamp_times(dtime.begin(), time, on_discover_vertex()),
                    stamp_times(ftime.begin(), time, on_finish_vertex()))),
     color.begin());

Model of

EventVisitor

Where Defined

boost/graph/visitors.hpp

Template Parameters

Parameter | Description | Default
TimeMap | A WritablePropertyMap, where the key_type is the vertex descriptor type or edge descriptor of the graph (depending on the kind of event tag), and where the TimeT type is convertible to the value_type of the time property map. |
TimeT | The type for the time counter, which should be convertible to the value_type of the time property map |
EventTag | The tag to specify when the time_stamper should be applied during the graph algorithm. |

Associated Types

Type | Description
time_stamper::event_filter | This will be the same type as the template parameter EventTag.

Member Functions

Member | Description
time_stamper(TimeMap time_pa, TimeT& t); | Construct a time stamper object with time property map time_pa and time counter t.
template <class X, class Graph>
void operator()(X x, const Graph& g); 		This increments the time count and "stamps" the time:
put(time_pa, x, ++t);

Non-Member Functions

Function Description
template <class TimeMap, class TimeT, class Tag>
time_stamper<TimeMap, TimeT, Tag>
stamp_times(TimeMap pa, TimeT& t, Tag); 		A convenient way to create a time_stamper.

See Also

Visitor concepts

The following are other event visitors: distance_recorder, time_stamper, and property_writer.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Topological Sort

topological_sort

template <typename VertexListGraph, typename OutputIterator,
          typename P, typename T, typename R>
void topological_sort(VertexListGraph& g, OutputIterator result,
    const bgl_named_params<P, T, R>& params = all defaults)

The topological sort algorithm creates a linear ordering of the vertices such that if edge (u,v) appears in the graph, then v comes before u in the ordering. The graph must be a directed acyclic graph (DAG). The implementation consists mainly of a call to depth_first_search().

Where Defined:

boost/graph/topological_sort.hpp

Parameters

IN: VertexListGraph& g
A directed acylic graph (DAG). The graph type must be a model of Vertex List Graph. If the graph is not a DAG then a not_a_dag exception will be thrown and the user should discard the contents of result range.
Python: The parameter is named graph.
OUT: OutputIterator result
The vertex descriptors of the graph will be output to the result output iterator in reverse topological order. The iterator type must model Output Iterator.
Python: This parameter is not used in Python. Instead, a Python list containing the vertices in topological order is returned.

Named Parameters

UTIL/OUT: color_map(ColorMap color)
This is used by the algorithm to keep track of its progress through the graph. The type ColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note:If this is used as default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity is O(V + E).

Example

Calculate a topological ordering of the vertices.

  typedef adjacency_list< vecS, vecS, directedS, color_property<> > Graph;
  typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
  Pair edges[6] = { Pair(0,1), Pair(2,4),
                    Pair(2,5),
                    Pair(0,3), Pair(1,4),
                    Pair(4,3) };
  Graph G(6, edges, edges + 6);

  typedef std::vector< Vertex > container;
  container c;
  topological_sort(G, std::back_inserter(c));

  cout << "A topological ordering: ";
  for ( container::reverse_iterator ii=c.rbegin(); ii!=c.rend(); ++ii)
    cout << index(*ii) << " ";
  cout << endl;
The output is:
  A topological ordering: 2 5 0 1 4 3
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Transitive Closure

transitive_closure

template <typename Graph, typename GraphTC,
  typename P, typename T, typename R>
void transitive_closure(const Graph& g, GraphTC& tc,
  const bgl_named_params<P, T, R>& params = all defaults)

template <typename Graph, typename GraphTC,
  typename G_to_TC_VertexMap, typename VertexIndexMap>
void transitive_closure(const Graph& g, GraphTC& tc,
                        G_to_TC_VertexMap g_to_tc_map, VertexIndexMap index_map)

The transitive closure of a graph G = (V,E) is a graph G* = (V,E*) such that E* contains an edge (u,v) if and only if G contains a path (of at least one edge) from u to v. The transitive_closure() function transforms the input graph g into the transitive closure graph tc.

Thanks to Vladimir Prus for the implementation of this algorithm!

Where Defined

boost/graph/transitive_closure.hpp

Parameters

IN: const Graph& g
A directed graph, where the Graph type must model the Vertex List Graph and Adjacency Graph concepts.
Python: The parameter is named graph.
OUT: GraphTC& tc
A directed graph, where the GraphTC type must model the Vertex Mutable Graph and Edge Mutable Graph concepts.
Python: This parameter is not used in Python. Instead, a new graph of the same type is returned.

Named Parameters

UTIL/OUT: orig_to_copy(G_to_TC_VertexMap g_to_tc_map)
This maps each vertex in the input graph to the new matching vertices in the output transitive closure graph.
Python: This must be a vertex_vertex_map of the graph.
IN: vertex_index_map(VertexIndexMap& index_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note:If this used as default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity (worst-case) is O(|V||E|).

Example

The following is the graph from the example example/transitive_closure.cpp and the transitive closure computed by the algorithm.

Implementation Notes

The algorithm used to implement the transitive_closure() function is based on the detection of strong components[50, 53]. The following discussion describes the algorithm (and some relevant background theory).

AA successor set of a vertex vv, denoted bySucc(v), is the set of vertices that are reachable vertex v. The set of vertices adjacent to v in the transitive closureeG* is the same as the successor set of v in the original graphhG. Computing the transitive closure is equivalent to computing the successor set for every vertex in G.

All vertices in the same strong component have the same successor set (because every vertex is reachable from all the other vertices in the component). Therefore, it is redundant to compute the successor set for every vertex in a strong component; it suffices to compute it for just one vertex per component.

The following is the outline of the algorithm:

  1. Compute strongly connected components of the graph.
  2. Construct the condensation graph. A condensation graph is a a graph G'=(V',E') based on the graph G=(V,E) where each vertex in V' corresponds to a strongly connected component in G and edge (u,v) is in E' if and only if there exists an edge ininE connecting any of the vertices in the component of u to any ofof v.
  3. Compute transitive closure on the condensation graph. This is done using the following algorithm:
     for each vertex u in G' in reverse topological order
   for each vertex v in Adj[u]
     if (v not in Succ(u))
       Succ(u) = Succ(u) U { v } U Succ(v)   // "U" means set union
    The vertices are considered in reverse topological order to ensure that the when computing the successor set for a vertex u, the successor set for each vertex in Adj[u] has already been computed.

    An optimized implementation of the set union operation improves the performance of the algorithm. Therefore this implementation uses chain decomposition [51,52]. The vertices of G are partitioned into chains Z1, ..., Zk, where each chain Zi is a path in G and the vertices in a chain have increasing topological number. A successor set S is then represented by a collection of intersections with the chains, i.e., S = Ui=1...k (Zi & S). Each intersection can be represented by the first vertex in the path Zi that is also in inS, since the rest of the path is guaranteed to also be in S. The collection of intersections is therefore represented by a vector of length k where the ith element of the vector stores the first vertex in the intersection of S with Zi.

    Computing the union of two successor sets, S3 = S1 U S2, can then be computed in O(k) time with the following operation:

    S3[i] = min(S1[i], S2[i]) // where min compares the topological number of the vertices
  4. Create the graphgraphG* based on the transitive closure of the condensation graph G'*.
Copyright © 2001
Jeremy Siek, Indiana Univ.(jsiek@cs.indiana.edu)


Boost Graph Library: Transpose Graph

transpose_graph

template <class VertexListGraph, class MutableGraph>
void transpose_graph(const VertexListGraph& G, MutableGraph& G_T,
    const bgl_named_params<P, T, R>& params = all defaults)

This function computes the transpose of a directed graph. The transpose of a directed graph G = (V, E)is the graph GT = (V, ET) , where ET = {(v, u) in V x V: (u, v) in E} . i.e., GT is G with all its edges reversed. Graph G_T passed into the algorithm must have no vertices and no edges. The vertices and edges will be added by transpose_graph() by calling add_vertex and add_edge as follows for each edge (u,v) in G.

Example

Here's an example of transposing a graph: example/transpose-example.cpp.

Where Defined

boost/graph/transpose_graph.hpp

Parameters

IN: const VertexListGraph& G
A directed graph. The graph type must be a model of Vertex List Graph.
OUT: const MutableGraph& G_T
The transposed graph. The graph type must be a model of Mutable Graph.

Named Parameters

IN: vertex_copy(VertexCopier vc)
This is a Binary Function that copies the properties of a vertex in the original graph into the corresponding vertex in the copy.
Default: vertex_copier<VertexListGraph, MutableGraph> which uses the property tag vertex_all to access a property map from the graph.
IN: edge_copy(EdgeCopier ec)
This is a Binary Function that copies the properties of an edge in the original graph into the corresponding edge in the copy.
Default: edge_copier<VertexListGraph, MutableGraph> which uses the property tag edge_all to access a property map from the graph.
IN: vertex_index_map(VertexIndexMap i_map)
The vertex index map type must be a model of Readable Property Map and must map the vertex descriptors of G to the integers from 0 to num_vertices(G).
Default: get(vertex_index, G) Note: if you use this default, make sure your graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
UTIL/OUT: orig_to_copy(Orig2CopyMap c)
This maps vertices in the original graph to vertices in the copy. Default: an iterator_property_map created from a std::vector of the output graph's vertex descriptor type of size num_vertices(g) and using the i_map for the index map.

Complexity

The time complexity is O(V + E).

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Trouble Shooting

Trouble Shooting

With GNU C++, if the following error message is seen:
boost/type_traits/arithmetic_traits.hpp:243: template instantiation depth exceeds maximum of 17
boost/type_traits/arithmetic_traits.hpp:243:  (use -ftemplate-depth-NN to increase the maximum)

then, do as the error message advises and increase the template instantiation depth. Passing the flag -ftemplate-depth-30 to g++ usually does the trick.
error C2784: 'T __cdecl source(struct std::pair,const G &)' :
could not deduce template argument for 'struct std::pair<_T1,_T1>' from
 'class boost::detail::bidir_edge'

VC++ does not support Koenig Lookup, therefore, refer to functions defined in the boost namespace using the boost:: prefix, i.e., boost::source(e, g) instead of source(e, g).
../../..\boost/property_map.hpp(283) : error C2678: binary '[' : no operator defined
 which takes a left-hand operand of type 'const struct boost::adj_list_edge_property_map,unsigned int,enum boost::edge_weight_t>' (or there is no acceptable conversion)

There is a VC++ bug that appears when using get(property, graph, edge). A workaround is to use get(get(property, graph), edge) instead.
C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\xmemory(59) : fatal
error C1001: INTERNAL COMPILER ERROR
        (compiler file 'msc1.cpp', line 1786)

There can be many reasons for this error, but sometimes it is caused by using the flag /Gm (minimal rebuild). As this flag is not really necessary, it is a good idea to turn it off.

Another reason for the error can be the use of the named-parameter interface for many of the BGL algorithms. Try using the non named-parameter version of the algorithm instead (see the HTML docs for the algorithm in question).

Yet another reason can be the use of the get() function of the PropertyGraph interface. Instead of using the get() function in a complex expression, always declare a property map variable first:
property_map<graph_t, edge_weight_t>::type w_map = get(edge_weight, g);
// use w_map ...

V:\3rdPARTY\SXL\INCLUDE\xlocnum(309) : error C2587: '_U' : illegal
 use of local variable as default parameter

Workaround from Andreas Scherer:
That's the usual problem with MSVC-- 6.0 sp[34] when compiling some (or all?) of the BGL examples. The DLL version of the run-time system should not be used. The user can succeeded in compiling file_dependencies.cpp after switching to ``[Debug] Multithreaded'' (section ``Code Generation'' on page ``C/C++'' in the ``Project Settings'').
Another reason for this error is when the iterator constructor of an adjacency_list is used. The workaround is to use add_edge() instead. Replace something like this:
Graph g(edge_array, edge_array + n_edges, N);
with something like this:
Graph g(N);
for (std::size_t j = 0; j < n_edges; ++j)
  add_edge(edge_array[j].first, edge_array[j].second, g);
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library: Depth-First Search

undirected_dfs

// named parameter version
template <typename Graph, typename P, typename T, typename R>
void undirected_dfs(Graph& G, const bgl_named_params<P, T, R>& params);

// non-named parameter version
template <typename Graph, typename DFSVisitor, typename VertexColorMap, typename EdgeColorMap>
void undirected_dfs(const Graph& g, DFSVisitor vis, VertexColorMap vertex_color, EdgeColorMap edge_color)

template <typename Graph, typename DFSVisitor, typename VertexColorMap, typename EdgeColorMap>
void undirected_dfs(const Graph& g, DFSVisitor vis,
                    VertexColorMap vertex_color, EdgeColorMap edge_color,
                    typename graph_traits<Graph>::vertex_descriptor start)

The undirected_dfs() function performs a depth-first traversal of the vertices in an undirected graph. When possible, a depth-first traversal chooses
a vertex adjacent to the current vertex to visit next. If all adjacent vertices have already been discovered, or there are no adjacent vertices, then the
algorithm backtracks to the last vertex that had undiscovered neighbors. Once all reachable vertices have been visited, the algorithm selects from any
remaining undiscovered vertices and continues the traversal. The algorithm finishes when all vertices have been visited.
Depth-first search is useful for categorizing edges in a graph, and for imposing an ordering on the vertices. Section Depth-First Search describes the
various properties of DFS and walks through an example.

Similar to BFS, color markers are used to keep track of which vertices have been discovered. White marks vertices that have yet to be discovered, gray marks a vertex that is discovered but still has vertices adjacent to it that are undiscovered. A black vertex is discovered vertex that is not adjacent to any white vertices.

Edges are also colored during the search to disambiguate tree and back edges.

The undirected_dfs() function invokes user-defined actions at certain event-points within the algorithm. This provides a mechanism for adapting the generic DFS algorithm to the many situations in which it can be used. In the pseudo-code below, the event points for DFS are indicated in by the triangles and labels on the right. The user-defined actions must be provided in the form of a visitor object, that is, an object whose type meets the requirements for a DFS Visitor. In the pseudo-code the algorithm computing predecessors p, discover time d and finish time t are shown. By default, the undirected_dfs() function does not compute these properties, however there are pre-defined visitors such as predecessor_recorder and time_stamper that can be used to do this.

DFS(G)
  for each vertex u in V
    vcolor[u] := WHITE
    p[u] := u
  end for
  for each edge e in E
    ecolor[u] := WHITE
  end for
  time := 0
  if there is a starting vertex s
    call DFS-VISIT(G, s)
  for each vertex u in V
    if vcolor[u] = WHITE
      call DFS-VISIT(G, u)
  end for
  return (p,d_time,f_time)

DFS-VISIT(G, u)
  vcolor[u] := GRAY
  d_time[u] := time := time + 1
  for each e in Out[u]
    var ec := ecolor[e]
    ecolor[e] := BLACK
    if (vcolor[v] = WHITE)
      p[v] := u
      call DFS-VISIT(G, v)
    else if (vcolor[v] = GRAY and ec = WHITE)
      ...
  end for
  vcolor[u] := BLACK
  f_time[u] := time := time + 1
-
-
initialize vertex u
-
-
-
-
-
-
-
start vertex s
-
-
start vertex u
-
-
-
-
discover vertex u
-
examine edge (u,v)
-
-
(u,v) is a tree edge
-
-
(u,v) is a back edge
-
-
finish vertex u
-

Where Defined

boost/graph/undirected_dfs.hpp

Parameters

IN: Graph& g
An undirected graph. The graph type must be a model of Incidence Graph, Vertex List Graph, and Edge List Graph.
Python: The parameter is named graph.

Named Parameters

IN: visitor(DFSVisitor vis)
A visitor object that is invoked inside the algorithm at the event-points specified by the DFS Visitor concept. The visitor object is passed by value [1].
Default: dfs_visitor<null_visitor>
Python: The parameter should be an object that derives from the DFSVisitor type of the graph.
UTIL/OUT: vertex_color_map(VertexColorMap vertex_color)
This is used by the algorithm to keep track of its progress through the graph. The type VertexColorMap must be a model of Read/Write Property Map and its key type must be the graph's vertex descriptor type and the value type of the color map must model ColorValue.
Default: an iterator_property_map created from a std::vector of default_color_type of size num_vertices(g) and using the i_map for the index map.
Python: The color map must be a vertex_color_map for the graph.
UTIL: edge_color_map(EdgeColorMap edge_color)
This is used by the algorithm to keep track of which edges have been visited. The type EdgeColorMap must be a model of Read/Write Property Map and its key type must be the graph's edge descriptor type and the value type of the color map must model ColorValue.
Default: none.
Python: The color map must be an edge_color_map for the graph.
IN: root_vertex(typename graph_traits<VertexListGraph>::vertex_descriptor start)
This specifies the vertex that the depth-first search should originate from. The type is the type of a vertex descriptor for the given graph.
Default: *vertices(g).first
IN: vertex_index_map(VertexIndexMap i_map)
This maps each vertex to an integer in the range [0, num_vertices(g)). This parameter is only necessary when the default color property map is used. The type VertexIndexMap must be a model of Readable Property Map. The value type of the map must be an integer type. The vertex descriptor type of the graph needs to be usable as the key type of the map.
Default: get(vertex_index, g) Note: if this is used as default, make sure the graph has an internal vertex_index property. For example, adjacenty_list with VertexList=listS does not have an internal vertex_index property.
Python: Unsupported parameter.

Complexity

The time complexity is O(E + V).

Visitor Event Points

  • vis.initialize_vertex(s, g) is invoked on every vertex of the graph before the start of the graph search.
  • vis.start_vertex(s, g) is invoked on the source vertex once before the start of the search.
  • vis.discover_vertex(u, g) is invoked when a vertex is encountered for the first time.
  • vis.examine_edge(e, g) is invoked on every out-edge of each vertex after it is discovered.
  • vis.tree_edge(e, g) is invoked on each edge as it becomes a member of the edges that form the search tree. To record predecessors, do so at this event point.
  • vis.back_edge(e, g) is invoked on the back edges in the graph.
  • vis.finish_vertex(u, g) is invoked on a vertex after all of its out edges have been added to the search tree and all of the adjacent vertices have been discovered (but before their out-edges have been examined).

Example

An example is in examples/undirected_dfs.cpp.

See Also

depth_first_search

Notes

[1] Since the visitor parameter is passed by value, if the visitor contains state then any changes to the state during the algorithm will be made to a copy of the visitor object, not the visitor object passed in. Therefore the user may want the visitor to hold this state by pointer or reference.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Boost Graph Library Users

This is a list of people, projects, courses, companies, and other languages that are using the Boost Graph Library in some way, shape, or form.If you are using the BGL and want to be listed here, please send mail to the Boost Users mailing list!

  • Generic Software Design Course at RPI
  • The ALPS quantum mechanics project
  • Large Graph Layout at University of Texas
  • Bioinformatics Algorithms at Concordia University
  • Algorithm Course at Polytechnic University in Brooklyn
  • BGL interface for language R.
  • CUJ Article about Electronic Design Automation
  • A BGL-inspired Ruby Graph Library
  • A BGL-inspired C# Graph Library
  • A BGL-inspired Squeak (Smalltalk) Graph Library
  • BGL course at DataSim
  • VR Juggler: Virtual Reality Tools
  • Hyperworx Platform Project


Using the Boost Graph Library

Using adjacency_list

This section describes the details of how use the adjacency_list class. The presentation is divided into the following topics:
  1. Choosing the Edgelist and VertexList
  2. Directed and Undirected Adjacency Lists
  3. Internal Properties
  4. Customizing the Adjacency List Storage s

Choosing the Edgelist and VertexList

This section focuses on how to decide which version of the adjacency_list class to use in different situations. The adjacency_list is like a swiss-army knife in that it can be configured in many ways. The parameters that we will focus on in this section are OutEdgeList and VertexList, which control the underlying data structures that will be used to represent the graph. The choice of OutEdgeList and VertexList affects the time complexity of many of the graph operations and the space complexity of the graph object.

BGL uses containers from the STL such as std::vector, std::list, and std::set to represent the set of vertices and the adjacency structure (out-edges and in-edges) of the graph. There are several selector types that are used to specify the choice of container for OutEdgeList and VertexList.

  • vecS selects std::vector.
  • listS selects std::list.
  • slistS selects std::slist.
  • setS selects std::set.
  • multisetS selects std::multiset.
  • hash_setS selects std::hash_set.

Choosing the VertexList type

The VertexList parameter determines what kind of container will be used to represent the vertex set, or two-dimensional structure of the graph. The container must model Sequence or RandomAccessContainer. In general, listS is a good choice if you need to add and remove vertices quickly. The price for this is extra space overhead compared to choosing vecS.

Space Complexity

The std::list has a higher per-vertex space overhead than the std::vector, storing three extra pointers per vertex.

Time Complexity

The choice of VertexList affects the time complexity of the following operations.

  • add_vertex()
    This operation is amortized constant time for both vecS and listS (implemented with push_back()). However, when the VertexList type is vecS the time for this operation is occasionally large because the vector will be reallocated and the whole graph copied.

  • remove_vertex()
    This operation is constant time for listS and O(V + E) for vecS. The large time complexity for vecS is because the vertex descriptors (which in this case are indices that correspond to the vertices' place in the vertex list) must be adjusted in the out-edges for the whole graph.

  • vertex()
    This operation is constant time for vecS and for listS.

Choosing the OutEdgeList type

The OutEdgeList parameter determines what kind of container will be used to store the out-edges (and possibly in-edges) for each vertex in the graph. The containers used for edge lists must either satisfy the requirements for Sequence or for AssociativeContainer.

One of the first things to consider when choosing the OutEdgeList is whether you want adjacency_list to enforce the absence of parallel edges in the graph (that is, enforce that the graph not become a multi-graph). If you want this enforced then use the setS or hash_setS selectors. If you want to represent a multi-graph, or know that you will not be inserting parallel edges into the graph, then choose one of the Sequence types: vecS, listS, or slistS. You will also want to take into account the differences in time and space complexity for the various graph operations. Below we use V for the total number of vertices in the graph and E for the total number of edges. Operations not discussed here are constant time.

Space Complexity

The selection of the OutEdgeList affects the amount of space overhead per edge in the graph object. In the order of least space to most space, the selectors are vecS, slistS, listS, and setS.

Time Complexity

In the following description of the time complexity for various operations, we use E/V inside of the ``big-O'' notation to express the length of an out-edge list. Strictly speaking this is not accurate because E/V merely gives the average number of edges per vertex in a random graph. The worst-case number of out-edges for a vertex is V (unless it is a multi-graph). For sparse graphs E/V is typically much smaller than V and can be considered a constant.

  • add_edge()
    When the OutEdgeList is a UniqueAssociativeContainer like std::set the absence of parallel edges is enforced when an edge is added. The extra lookup involved has time complexity O(log(E/V)). The OutEdgeList types that model Sequence do not perform this check and therefore add_edge() is amortized constant time. This means that it if you don't care whether the graph has parallel edges, or know that the input to the graph does not contain them, then it is better to use the sequence-based OutEdgeList. The add_edge() for the sequence-based OutEdgeList is implemented with push_front() or push_back(). However, for std::list and std::slist this operation will typically be faster than with std::vector which occasionally reallocates and copies all elements.

  • remove_edge()
    For sequence-based OutEdgeList types this operation is implemented with std::remove_if() which means the average time is E/V. For set-based OutEdgeList types this is implemented with the erase() member function, which has average time log(E/V).

  • edge()
    The time complexity for this operation is O(E/V) when the OutEdgeList type is a Sequence and it is O(log(E/V)) when the OutEdgeList type is an AssociativeContainer.

  • clear_vertex()
    For directed graphs with sequence-based OutEdgeList types the time complexity is O(V + E), while for associative container based OutEdgeList types the operation is faster, with time complexity O(V log(E/V)). For undirected graphs this operation is O(E2/V2) or O(E/V log(E/V)).

  • remove_vertex()
    The time complexity for this operation is O(V + E) regardless of the OutEdgeList type.

  • out_edge_iterator::operator++()
    This operation is constant time for all the OneD types. However, there is a significant constant factor time difference between the various types, which is important since this operation is the work-horse of most graph algorithms. The speed of this operation in order of fastest to slowest is vecS, slistS, listS, setS, hash_setS.

  • in_edge_iterator::operator++()
    This operation is constant time and exhibits a similar speed ordering as the out_edge_iterator with respect to the OutEdgeList selection.

  • vertex_iterator::operator++()
    This operation is constant time and fast (same speed as incrementing a pointer). The selection of OneD does not affect the speed of this operation.

  • edge_iterator::operator++()
    This operation is constant time and exhibits a similar speed ordering as the out_edge_iterator with respect to the OutEdgeList selection. Traversing through the whole edge set is O(V + E).

  • adjacency_iterator::operator++()
    This operation is constant time and exhibits a similar speed ordering as the out_edge_iterator with respect to the OutEdgeList selection.

Directed and Undirected Adjacency Lists

The adjacency_list class can be used to represent both directed and undirected graphs, depending on the argument passed to the Directed template parameter. Selecting directedS or bidirectionalS choose a directed graph, whereas undirectedS selects the representation for an undirected graph. See Section Undirected Graphs for a description of the difference between directed and undirected graphs in BGL. The bidirectealS selector specifies that the graph will provide the in_edges() function as well as the out_edges() function. This imposes twice as much space overhead per edge, which is why in_edges() is optional.

Internal Properties

Properties can be attached to the vertices or edges of an adjacency_list graph via the property interface. The template parameters VertexProperty and EdgeProperty of the adjacency_list class are meant to be filled by these interior properties.

NOTE: The Boost Graph Library supports two interchangeable methods for specifying interior properties: bundled properties and property lists. The former is easier to use and requires less effort, whereas the latter is compatible with older, broken compilers and is backward-compatible with Boost versions prior to 1.32.0. If you absolutely require these compatibility features, read on to learn about property lists. Otherwise, we strongly suggest that you read about the bundled properties mechanism.

One may specify internal properties via property lists, which are build from instances of the property class declared as follows.

template <class PropertyTag, class T, class NextProperty = no_property>
struct property;

The PropertyTag template parameter is a tag class that simply identifies or gives a unique name to the property. There are several predefined tags, and it is easy to add more.

  struct vertex_index_t { };
  struct vertex_index1_t { };
  struct vertex_index2_t { };
  struct edge_index_t { };
  struct graph_name_t { };
  struct vertex_name_t { };
  struct edge_name_t { };
  struct edge_weight_t { };
  struct edge_weight2_t { };
  struct edge_capacity_t { };
  struct edge_residual_capacity_t { };
  struct edge_reverse_t { };
  struct vertex_distance_t { };
  struct vertex_root_t { };
  struct vertex_all_t { };
  struct edge_all_t { };
  struct graph_all_t { };
  struct vertex_color_t { };
  struct vertex_rank_t { };
  struct vertex_predecessor_t { };
  struct vertex_isomorphism_t { };
  struct vertex_invariant_t { };
  struct vertex_invariant1_t { };
  struct vertex_invariant2_t { };
  struct vertex_degree_t { };
  struct vertex_out_degree_t { };
  struct vertex_in_degree_t { };
  struct vertex_discover_time_t { };
  struct vertex_finish_time_t { };

The T template parameter of property specifies the type of the property values. The type T must be Default Constructible, Assignable, and Copy Constructible. Like the containers of the C++ Standard Library, the property objects of type T are held by-value inside of the graph.

The NextProperty parameter allows property types to be nested, so that an arbitrary number of properties can be attached to the same graph.

The following code shows how a vertex and edge property type can be assembled and used to create a graph type. We have attached a distance property with values of type float and a name property with values of type std::string to the vertices of the graph. We have attached a weight property with values of type float to the edges of the graph.

  typedef property<vertex_distance_t, float,
            property<vertex_name_t, std::string> > VertexProperty;
  typedef property<edge_weight_t, float> EdgeProperty;

  typedef adjacency_list<mapS, vecS, undirectedS,
                         VertexProperty, EdgeProperty> Graph;

  Graph g(num_vertices); // construct a graph object

The property values are then read from and written to using property maps. See Section Interior Properties for a description of how to obtain property maps from a graph, and read Section Property Maps for how to use property maps.

Custom Edge Properties

Creating your own property types and properties is easy; just define a tag class for your new property. The property tag class will need to define num with a unique integer ID, and kind which should be either edge_property_tag, vertex_property_tag, or graph_property_tag.

struct flow_t {
  typedef edge_property_tag kind;
};

struct capacity_t {
  typedef edge_property_tag kind;
};

You can also use enum's instead of struct's to create tag types. Create an enum type for each property. The first part of the name of the enum type must be edge, vertex, or graph followed by an underscore, the new property name, and a _t at the end. Inside the enum, define a value with the same name minus the _t. Then invoke the BOOST_INSTALL_PROPERTY macro.

enum edge_flow_t { edge_flow };
enum edge_capacity_t { edge_capacity };

namespace boost {
  BOOST_INSTALL_PROPERTY(edge, flow);
  BOOST_INSTALL_PROPERTY(edge, capacity);
}

Now you can use your new property tag in the definition of properties just as you would one of the builtin tags.

  typedef property<capacity_t, int> Cap;
  typedef property<flow_t, int, Cap> EdgeProperty;
  typedef adjacency_list<vecS, vecS, no_property, EdgeProperty> Graph;

Just as before, the property maps for these properties can be obtained from the graph via the get(Property, g) function.

  property_map<Graph, capacity_t>::type capacity
    = get(capacity_t(), G);
  property_map<Graph, flow_t>::type flow
    = get(flow_t(), G);

The file edge_property.cpp shows the complete source code for this example.

Custom Vertex Properties

Creating your own properties to attach to vertices is just as easy as for edges. Here we want to attach people's first names to the vertices in the graph.

  struct first_name_t {
    typedef vertex_property_tag kind;
  };

Now we can use the new tag in the property class and use that in the assembly of a graph type. The following code shows creating the graph type, and then creating the graph object. We fill in the edges and also assign names to the vertices. The edges will represent ``who owes who''.

  typedef property<first_name_t, std::string> FirstNameProperty;
  typedef adjacency_list<vecS, vecS, directedS,
                         FirstNameProperty> MyGraphType;

  typedef pair<int,int> Pair;
  Pair edge_array[11] = { Pair(0,1), Pair(0,2), Pair(0,3),
                          Pair(0,4), Pair(2,0), Pair(3,0),
                          Pair(2,4), Pair(3,1), Pair(3,4),
                          Pair(4,0), Pair(4,1) };

  MyGraphType G(5);
  for (int i = 0; i < 11; ++i)
    add_edge(edge_array[i].first, edge_array[i].second, G);

  property_map<MyGraphType, first_name_t>::type
    name = get(first_name_t(), G);

  boost::put(name, 0, "Jeremy");
  boost::put(name, 1, "Rich");
  boost::put(name, 2, "Andrew");
  boost::put(name, 3, "Jeff");
  name[4] = "Kinis"; // you can use operator[] too

  who_owes_who(edges(G).first, edges(G).second, G);

The who_owes_who() function written for this example was implemented in a generic style. The input is templated so we do not know the actual graph type. To find out the type of the property map for our first-name property, we need to use the property_map traits class. The const_type is used since the graph parameter is const. Once we have the property map type, we can deduce the value type of the property using the property_traits class. In this example, we know that the property's value type will be std::string, but written in this generic fashion the who_owes_who() function could work with other property value types.

  template <class EdgeIter, class Graph>
  void who_owes_who(EdgeIter first, EdgeIter last, const Graph& G)
  {
    // Access the propety acessor type for this graph
    typedef typename property_map<Graph,
      first_name_t>::const_type NameMap;
    NameMap name = get(first_name, G);

    typedef typename boost::property_traits<NameMap>
      ::value_type NameType;

    NameType src_name, targ_name;

    while (first != last) {
      src_name = boost::get(name, source(*first, G));
      targ_name = boost::get(name, target(*first, G));
      cout << src_name << " owes "
           << targ_name << " some money" << endl;
      ++first;
    }
The output is:
Jeremy owes Rich some money
Jeremy owes Andrew some money
Jeremy owes Jeff some money
Jeremy owes Kinis some money
Andrew owes Jeremy some money
Andrew owes Kinis some money
Jeff owes Jeremy some money
Jeff owes Rich some money
Jeff owes Kinis some money
Kinis owes Jeremy some money
Kinis owes Rich some money
The complete source code to this example is in the file interior_property_map.cpp.

Customizing the Adjacency List Storage

The adjacency_list is constructed out of two kinds of containers. One type of container to hold all the vertices in the graph, and another type of container for the out-edge list (and potentially in-edge list) for each vertex. BGL provides selector classes that allow the user to choose between several of the containers from the STL. It is also possible to use your own container types. When customizing the VertexList you need to define a container generator as described below. When customizing the OutEdgeList you will need to define a container generator and the parallel edge traits. The file container_gen.cpp has an example of how to use a custom storage type.

Container Generator

The adjacency_list class uses a traits class called container_gen to map the OutEdgeList and VertexList selectors to the actual container types used for the graph storage. The default version of the traits class is listed below, along with an example of how the class is specialized for the listS selector.

namespace boost {
  template <class Selector, class ValueType>
  struct container_gen { };

  template <class ValueType>
  struct container_gen<listS, ValueType> {
    typedef std::list<ValueType> type;
  };
}

To use some other container of your choice, define a selector class and then specialize the container_gen for your selector.

  struct custom_containerS { }; // your selector

  namespace boost {
    // the specialization for your selector
    template <class ValueType>
    struct container_gen<custom_containerS, ValueType> {
      typedef custom_container<ValueType> type;
    };
  }

There may also be situations when you want to use a container that has more template parameters than just ValueType. For instance, you may want to supply the allocator type. One way to do this is to hard-code in the extra parameters within the specialization of container_gen. However, if you want more flexibility then you can add a template parameter to the selector class. In the code below we show how to create a selector that lets you specify the allocator to be used with the std::list.

  template <class Allocator>
  struct list_with_allocatorS { };

  namespace boost {
    template <class Alloc, class ValueType>
    struct container_gen<list_with_allocatorS<Alloc>, ValueType>
    {
      typedef typename Alloc::template rebind<ValueType>::other Allocator;
      typedef std::list<ValueType, Allocator> type;
    };
  }

  // now you can define a graph using std::list
  //   and a specific allocator
  typedef adjacency_list< list_with_allocatorS< std::allocator<int> >, vecS, directedS> MyGraph;

Push and Erase for the Custom Container

You must also tell the adjacency_list how elements can be efficiently added and removed from the custom container. This is accomplished by overloading the push() and erase() functions for the custom container type. The push() function should return an iterator pointing to the newly inserted element and a bool flag saying whether the edge was inserted.

  template <class T>
  std::pair<typename custom_container<T>::iterator, bool>
  push(custom_container<T>& c, const T& v)
  {
    // this implementation may need to change for your container
    c.push_back(v);
    return std::make_pair(boost::prior(c.end()), true);
  }

  template <class T>
  void erase(custom_container<T>& c, const T& x)
  {
    // this implementation may need to change for your container
    c.erase(std::remove(c.begin(), c.end(), x), c.end());
  }

There are default push() and erase() functions implemented for the STL container types.

Parallel Edge Traits

When customizing the OutEdgeList, you must also specialize the parallel_edge_traits class to specify whether the container type allows parallel edges (and is a Sequence) or if the container does not allow parallel edges (and is an AssociativeContainer).

  template <>
  struct parallel_edge_traits<custom_containerS> {
    typedef allow_parallel_edge_tag type;
  };
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


Boost Graph Library: Using Property Maps

Property Maps

The main link between the abstract mathematical nature of graphs and the concrete problems they are used to solve is the properties that are attached to the vertices and edges of a graph, things like distance, capacity, weight, color, etc. There are many ways to attach properties to graph in terms of data-structure implementation, but graph algorithms should not have to deal with the implementation details of the properties. The property map interface defined in Section Property Map Concepts provides a generic method for accessing properties from graphs. This is the interface used in the BGL algorithms to access properties.

Property Map Interface

The property map interface specifies that each property is accessed using a separate property map object. In the following example shows implementation of the relax() function used inside of Dijkstra's shortest paths algorithm. In this function, the user needs to access the weight property of an edge, and the distance property of a vertex. Write relax() as a template function so that it can be used in many difference situations. Two of the arguments to the function, weight and distance, are the property map objects. In general, BGL algorithms explicitly pass property map objects for every property that a function will need. The property map interface defines several functions, two of which are: get() and put(). The get() function takes a property map object, such as distance and a key object. In the case of the distance property, the vertex objects u and v as keys are used. The get() function then returns the property value for the vertex.

  template <class Edge, class Graph,
            class WeightPropertyMap,
            class DistancePropertyMap>
  bool relax(Edge e, const Graph& g,
             WeightPropertyMap weight,
             DistancePropertyMap distance)
  {
    typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
    Vertex u = source(e,g), v = target(e,g);
    if ( get(distance, u) + get(weight, e) < get(distance, v)) {
      put(distance, v, get(distance, u) + get(weight, e));
      return true;
    } else
      return false;
  }
The function get() returns a copy of the property value. There is a third function in the property map interface, at(), that returns a reference to the property value (a const reference if the map is not mutable).

Similar to the iterator_traits class of the STL, there is a property_traits class that can be used to deduce the types associated with a property map type: the key and value types, and the property map category (which is used to tell whether the map is readable, writeable, or both). In the relax() function, property_traits to declare local variables of the distance property type can be used.

  {
    typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
    Vertex u = source(e,g), v = target(e,g);
    typename property_traits<DistancePropertyMap>::value_type
      du, dv; // local variables of the distance property type
    du = get(distance, u);
    dv = get(distance, v);
    if (du + get(weight, e) < dv) {
      put(distance, v, du + get(weight, e));
      return true;
    } else
      return false;
  }

There are two kinds of graph properties: interior and exterior.

Interior Properties
are stored ``inside'' the graph object in some way, and the lifetime of the property value objects is the same as that of the graph object.

Exterior Properties
are stored ``outside'' of the graph object and the lifetime of the property value objects is independent of the graph. This is useful for properties that are only needed temporarily, perhaps for the duration of a particular algorithm such as the color property used in breadth_first_search(). When using exterior properties with a BGL algorithm a property map object for the exterior property must be passed as an argument to the algorithm.

Interior Properties

A graph type that supports interior property storage (such as adjacency_list) provides access to its property map objects through the interface defined in PropertyGraph. There is a function get(Property, g) that get property map objects from a graph. The first argument is the property type to specify which property you want to access and the second argument is the graph object. A graph type must document which properties (and therefore tags) it provides access to. The type of the property map depends on the type of graph and the property being mapped. A trait class is defined that provides a generic way to deduce the property map type: property_map. The following code shows how one can obtain the property map for the distance and weight properties of some graph type.

  property_map<Graph, vertex_distance_t>::type d
    = get(vertex_distance, g);

  property_map<Graph, edge_weight_t>::type w
    = get(edge_weight, g);

In general, the BGL algorithms require all property maps needed by the algorithm to be explicitly passed to the algorithm. For example, the BGL Dijkstra's shortest paths algorithm requires four property maps: distance, weight, color, and vertex ID.

Often times some or all of the properties will be interior to the graph, so one would call Dijkstra's algorithm in the following way (given some graph g and source vertex src).

  dijkstra_shortest_paths(g, src,
    distance_map(get(vertex_distance, g)).
    weight_map(get(edge_weight, g)).
    color_map(get(vertex_color, g)).
    vertex_index_map(get(vertex_index, g)));

Since it is somewhat cumbersome to specify all of the property maps, BGL provides defaults that assume some of the properties are interior and can be accessed via get(Property, g) from the graph, or if the property map is only used internally, then the algorithm will create a property map for itself out of an array and using the graph's vertex index map as the offset into the array. Below is the call to dijkstra_shortest_paths algorithm using all defaults for the named parameters. This call is equivalent to the previous call to Dijkstra's algorithm.

  dijkstra_shortest_paths(g, src);

The next question is: how do interior properties become attached to a graph object in the first place? This depends on the graph class that you are using. The adjacency_list graph class of BGL uses a property mechanism (see Section Internal Properties) to allow an arbitrary number of properties to be stored on the edges and vertices of the graph.

Exterior Properties

This section describes two methods for constructing exterior property maps, however there is an unlimited number of ways that one could create exterior properties for a graph.

The first method uses the adaptor class random_access_iterator_property_map. This class wraps a random access iterator, creating a property map out of it. The random access iterator must point to the beginning of a range of property values, and the length of the range must be the number of vertices or edges in the graph (depending on whether it is a vertex or edge property map). The adaptor must also be supplied with an ID property map, which will be used to map the vertex or edge descriptor to the offset of the property value (offset from the random access iterator). The ID property map will typically be an interior property map of the graph. The following example shows how the random_access_iterator_property_map can be used to create exterior property maps for the capacity and flow properties, which are stored in arrays. The arrays are indexed by edge ID. The edge ID is added to the graph using a property, and the values of the ID's are given when each edge is added to the graph. The complete source code for this example is in example/exterior_edge_properties.cpp. The print_network() function prints out the graph with the flow and capacity values.

  typedef adjacency_list<vecS, vecS, bidirectionalS,
    no_property, property<edge_index_t, std::size_t> > Graph;

  const int num_vertices = 9;
  Graph G(num_vertices);

  int capacity_array[] = { 10, 20, 20, 20, 40, 40, 20, 20, 20, 10 };
  int flow_array[] = { 8, 12, 12, 12, 12, 12, 16, 16, 16, 8 };

  // Add edges to the graph, and assign each edge an ID number.
  add_edge(0, 1, 0, G);
  // ...

  typedef graph_traits<Graph>::edge_descriptor Edge;
  typedef property_map<Graph, edge_index_t>::type EdgeID_Map;
  EdgeID_Map edge_id = get(edge_index, G);

  random_access_iterator_property_map
    <int*, int, int&, EdgeID_Map>
      capacity(capacity_array, edge_id),
      flow(flow_array, edge_id);

  print_network(G, capacity, flow);

The second method uses a pointer type (a pointer to an array of property values) as a property map. This requires the key type to be an integer so that it can be used as an offset to the pointer. The adjacency_list class with template parameter VertexList=vecS uses integers for vertex descriptors (indexed from zero to the number of vertices in the graph), so they are suitable as the key type for a pointer property map. When the VertexList is not vecS, then the vertex descriptor is not an integer, and cannot be used with a pointer property map. Instead the method described above of using a random_access_iterator_property_map with an ID property must be used. The edge_list class may also use an integer type for the vertex descriptor, depending on how the adapted edge iterator is defined. The example in example/bellman_ford.cpp shows edge_list being used with pointers as vertex property maps.

The reason that pointers can be used as property maps is that there are several overloaded functions and a specialization of property_traits in the header boost/property_map.hpp that implement the property map interface in terms of pointers. The definition of those functions is listed here.

namespace boost {
  template <class T>
  struct property_traits<T*> {
    typedef T value_type;
    typedef ptrdiff_t key_type;
    typedef lvalue_property_map_tag category;
  };

  template <class T>
  void put(T* pa, std::ptrdiff_t key, const T& value) { pa[key] = value;  }

  template <class T>
  const T& get(const T* pa, std::ptrdiff_t key) { return pa[key]; }

  template <class T>
  const T& at(const T* pa, std::ptrdiff_t key) { return pa[key]; }

  template <class T>
  T& at(T* pa, std::ptrdiff_t key) { return pa[key]; }
}

In the following example, an array to store names of cities for each vertex in the graph is used, and a std::vector to store vertex colors which will be needed in a call to breadth_first_search(). Since the iterator of a std::vector (obtained with a call to begin()) is a pointer, the pointer property map method also works for std::vector::iterator. The complete source code for this example is in example/city_visitor.cpp.

// Definition of city_visitor omitted...

int main(int,char*[])
{
  enum { SanJose, SanFran, LA, SanDiego, Fresno, LosVegas, Reno,
         Sacramento, SaltLake, Pheonix, N };

  // An array of vertex name properties
  std::string names[] = { "San Jose", "San Francisco",  "San Jose",
                          "San Francisco", "Los Angeles", "San Diego",
                          "Fresno", "Los Vegas", "Reno", "Sacramento",
                          "Salt Lake City", "Pheonix" };

  // Specify all the connecting roads between cities.
  typedef std::pair<int,int> E;
  E edge_array[] = { E(Sacramento, Reno), ... };

  // Specify the graph type.
  typedef adjacency_list<vecS, vecS, undirectedS> Graph;
  // Create the graph object, based on the edges in edge_array.
  Graph G(N, edge_array, edge_array + sizeof(edge_array)/sizeof(E));

  // DFS and BFS need to "color" the vertices.
  // Here std::vector is used as exterior property storage.
  std::vector<default_color_type> colors(N);

  cout << "*** Depth First ***" << endl;
  depth_first_search(G, city_visitor(names), colors.begin());
  cout << endl;

  // Get the source vertex
  boost::graph_traits<Graph>::vertex_descriptor
    s = vertex(SanJose, G);

  cout << "*** Breadth First ***" << endl;
  breadth_first_search(G, s, city_visitor(names), colors.begin());

  return 0;
}

Constructing an Exterior Property Map

Implementing your own exterior property maps is not very difficult. Simply overload the functions required by the property map concept that the class should model. At most, this means overloading the put() and get() functions and implementing operator[]. Also, the property map class needs nested typedefs for all the types defined in property_traits, or you can create a specialization of property_traits for your new property map type.

The implementation of the random_access_iterator_property_map class serves as a good example for how to build and exterior property map. Here is a simplified implementation of the random_access_iterator_property_map class which is named iterator_pa.

Start with the definition of the iterator_map class itself. This adaptor class is templated on the adapted Iterator type and the ID property map. The job of the ID property map is to map the key object (which will typically be a vertex or edge descriptor) to an integer offset. The iterator_map class will need the three necessary typedefs for a property map: key_type, value_type, and category. property_traits to find the key type of IDMap and iterator_traits to determine the value type of Iterator can be used. Choose boost::lvalue_property_map_tag for the category since for implementing the at() function.

  template <class Iterator, class IDMap>
  class iterator_map
  {
  public:
    typedef typename boost::property_traits<IDMap>::key_type key_type;
    typedef typename std::iterator_traits<Iterator>::value_type value_type;
    typedef boost::lvalue_property_map_tag category;

    iterator_map(Iterator i = Iterator(),
                const IDMap& id = IDMap())
      : m_iter(i), m_id(id) { }
    Iterator m_iter;
    IDMap m_id;
  };

Implement the three property map functions, get(), put(), and at(). In each of the functions, the key object is converted to an integer offset using the m_id property map, and then that is used as an offset to the random access iterator m_iter.

  template <class Iter, class ID>
  typename std::iterator_traits<Iter>::value_type
  get(const iterator_map<Iter,ID>& i,
      typename boost::property_traits<ID>::key_type key)
  {
    return i.m_iter[i.m_id[key]];
  }
  template <class Iter, class ID>
  void
  put(const iterator_map<Iter,ID>& i,
      typename boost::property_traits<ID>::key_type key,
      const typename std::iterator_traits<Iter>::value_type& value)
  {
    i.m_iter[i.m_id[key]] = value;
  }
  template <class Iter, class ID>
  typename std::iterator_traits<Iter>::reference
  at(const iterator_map<Iter,ID>& i,
      typename boost::property_traits<ID>::key_type key)
  {
    return i.m_iter[i.m_id[key]];
  }

That is it. The iterator_map class is complete and could be used just like the random_access_iterator_property_map in the previous section.

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Vector Property Map

template<typename T, typename IndexMap = identity_property_map>
class vector_property_map;

This property map is used to efficiently store properties for a variable number of elements. It's somewhere between associative_property_map and iterator_property_map. The latter is very fast, but requires that the number of stored elements is known when creating property map. The former does not have this requirement, but is slower, and requires stored elements to be comparable.

The vector_property_map uses mapping from key to indices, and allows to add new elements. It stores values in a vector, which is resized on demand.

Note: The vector_property_map does not provide reference/pointer stability for stored values.

Example

#include <boost/vector_property_map.hpp>
#include <string>
#include <iostream>

int main()
{
    boost::vector_property_map<std::string> m;

    // Assign string to '4'.
    m[4] = "e";
    std::cout << "'" << m[4] << "'\n";

    // Grab string from '10'. Since none is associated,
    // "" will be returned.
    std::cout << "'" << m[10] << "'\n";
}

Where Defined

boost/vector_property_map.hpp

Model Of

Lvalue Property Map

Template Parameters

Parameter | Description | Default
T | The type of property value. Must be both Assignable and DefaultConstructible. |
IndexMap | Must be a model of Readable Property Map and the value type must be convertible to std::vector<T>::size_type. | identity_property_map

Members

In addition to the methods and functions required by Lvalue Property Map, this class has the following members.

vector_property_map(const IndexMap& index = IndexMap())

Constructor, which takes an index map.

vector_property_map(unsigned initial_size, const IndexMap& index = IndexMap())

This constructor version allows to specify maximum index of element that will be stored. Correct number will improve performance, but semantic is always the same.

vector_property_map(const vector_property_map&)

Copy constructor. The copy will share the same data and changes made to it will affect original property map.

vector_property_map& operator=(const vector_property_map&)

Assignment operator. The semantic is the same as for copy constructor.

reference operator[](const key_type& v) const

The operator bracket for property access.

std::vector<T>::iterator storage_begin()
std::vector<T>::iterator storage_end()
std::vector<T>::const_iterator storage_begin()
std::vector<T>::const_iterator storage_end()

This group of methods gives access to begin and end iterators of the underlying vector.

Rationale: The methods are handy, for example, to specify a single value for all elements in a freshly created property map. The methods are not called simply "begin" and "end" since conceptually, vector_property_map is unbounded map, and has no end iterator. The direct access to the underlying method is not provided, since it would decrease encapsulation and make future performance tuning dangerous.

Acknowledgements: Matthias Troyer suggested adding those functions.

void reserve(unsigned size)

Reserve the space for storing elements with maximum index of 'size'. Unless element with greater index is accesses, all accesses will be take O(1) time.

Non-Member functions

template
vector_property_map
make_vector_property_map(IndexMap index)
{
  return vector_property_map(index);
}

A function for conveniently creating a vector property map.

Copyright © 2002
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee1@osl.iu.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)
Copyright © 2003
Vladimir Prus


VertexAndEdgeListGraph

The VertexAndEdgeListGraph concept refines the VertexListGraph and the EdgeListGraph concepts. No further requirements are added.

Refinement of

VertexListGraph, EdgeListGraph

Models

  • adjacency_list

See Also

Graph concepts

Concept Checking Class

  template <class G>
  struct VertexAndEdgeListGraphConcept
  {
    void constraints() {
      function_requires< VertexListGraphConcept<G> >();
      function_requires< EdgeListGraphConcept<G> >();
    }
  };

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)


VertexAndEdgeListGraph

Name

VertexAndEdgeListGraph: This concept refines the VertexListGraph and the EdgeListGraph concepts.

Synopsis

template <class G>
  struct VertexAndEdgeListGraphConcept
  {
    void constraints() {
      function_requires< VertexListGraphConcept<G> >();
      function_requires< EdgeListGraphConcept<G> >();
    }
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


VertexListGraph

The VertexListGraph concept refines the Graph concept, and adds the requirement for efficient traversal of all the vertices in the graph.

Refinement of

Graph

Associated Types

boost::graph_traits<G>::traversal_category

This tag type must be convertible to vertex_list_graph_tag.

boost::graph_traits<G>::vertex_iterator

A vertex iterator (obtained via vertices(g)) provides access to all of the vertices in a graph. A vertex iterator type must meet the requirements of MultiPassInputIterator. The value type of the vertex iterator must be the vertex descriptor of the graph.

boost::graph_traits<G>::vertices_size_type

The unsigned integer type used to represent the number of vertices in the graph.

Valid Expressions

Name | Expression | Return Type | Description
Vertex Set of the Graph | vertices(g) | std::pair<vertex_iterator, vertex_iterator> | Returns an iterator-range providing access to all the vertices in the graph g.
Number of Vertices in the Graph | num_vertices(g) | vertices_size_type | Returns the number of vertices in the graph g.

Complexity guarantees

The vertices() function must return in constant time.

See Also

Graph concepts

Design Rationale

One issue in the design of this concept is whether to include the refinement from the IncidenceGraph and AdjacencyGraph concepts. The ability to traverse the vertices of a graph is orthogonal to traversing out-edges, so it would make sense to have a VertexListGraph concept that only includes vertex traversal. However, such a concept would no longer really be a graph, but would just be a set, and the STL already has concepts for dealing with such things. However, there are many BGL algorithms that need to traverse the vertices and out-edges of a graph, so for convenience a concept is needed that groups these requirements together, hence the VertexListGraph concept.

Concept Checking Class

  template <class G>
  struct VertexListGraphConcept
  {
    typedef typename boost::graph_traits<G>::vertex_iterator
      vertex_iterator;
    void constraints() {
      function_requires< IncidenceGraphConcept<G> >();
      function_requires< AdjacencyGraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<vertex_iterator> >();

      p = vertices(g);
      V = num_vertices(g);
      v = *p.first;
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = vertices(g);
      V = num_vertices(g);
      v = *p.first;
    }
    std::pair<vertex_iterator, vertex_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor v;
    typename boost::graph_traits<G>::vertices_size_type V;
    G g;
  };

Copyright © 2000-2001
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)


VertexListGraph

Name

VertexListGraph: This concept refines the Graph concept, and adds the requirement for efficient traversal of all the vertices in the graph.

Synopsis

template <class G>
  struct VertexListGraphConcept
  {
    typedef typename boost::graph_traits<G>::vertex_iterator
      vertex_iterator;
    void constraints() {
      function_requires< IncidenceGraphConcept<G> >();
      function_requires< AdjacencyGraphConcept<G> >();
      function_requires< MultiPassInputIteratorConcept<vertex_iterator> >();

      p = vertices(g);
      V = num_vertices(g);
      v = *p.first;
      const_constraints(g);
    }
    void const_constraints(const G& g) {
      p = vertices(g);
      V = num_vertices(g);
      v = *p.first;
    }
    std::pair<vertex_iterator, vertex_iterator> p;
    typename boost::graph_traits<G>::vertex_descriptor v;
    typename boost::graph_traits<G>::vertices_size_type V;
    G g;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Vertex Mutable Graph

A vertex mutable graph can be changed by adding or removing vertices. The memory management is the responsibility of the graph implementation. The graph user need only make calls to add_vertex and remove_vertex and the graph implementation does the rest.

Refinement of

Graph and DefaultConstructible

Associated Types

No additional associated types.

Valid Expressions

  • add_vertex(g) returns vertex_descriptor

    Semantics: Add a new vertex to the graph. The vertex_descriptor for the new vertex is returned.
  • remove_vertex(u, g) returns void

    Semantics:: Remove u from the vertex set of the graph.
    u is a valid vertex descriptor of graph g and there are no edges incident to vertex u. The function clear_vertex can be used to remove all incident edges.
    Postconditions: num_vertices(g) is one less; u no longer appears in the vertex set of the graph and it is no longer a valid vertex descriptor.

Complexity guarantees
  • Vertex insertion is guaranteed to be amortized constant time.
  • Vertex removal is at most O(|E| + |V|).
Copyright © 2000-20010101
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) Lie-Quan Lee, Indiana University (llee@cs.indiana.edu) 		Top


Boost Graph Library: Visitor Concepts

Visitor Concepts

The visitor concepts plays the same role in BGL as functors play in the STL. Functors provide a mechanism for extending an algorithm; for customizing what is done at each step of the algorithm. Visitors allow the user to insert their own operations at various steps within a graph algorithm. Unlike the STL algorithms, graph algorithms typically have multiple event points where one may want to insert a call-back via a functor. Therefore visitors do not have a single operator() method like a functor, but instead have several methods that correspond to the various event points. Each algorithm has a different set of event points, which are described by the following visitor concepts:

  • BFS Visitor
  • DFS Visitor
  • Dijkstra Visitor
  • Bellman Ford Visitor
  • A* Visitor
  • Event Visitor

Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu) 		Top


Boost Graph Library: Wavefront

ith_wavefront

  (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  ith_wavefront(typename graph_traits<Graph>::vertex_descriptor i,
                const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  ith_wavefront(typename graph_traits<Graph>::vertex_descriptor i,
                const Graph& g,
                VertexIndexMap index)

Calculates the wavefront of the ith-vertex.

Defined in

boost/graph/wavefront.hpp

max_wavefront

  (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  max_wavefront(const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  max_wavefront(const Graph& g, VertexIndexMap index)
Calculates the maximum wavefront a graph.

Defined in

boost/graph/wavefront.hpp

aver_wavefront

  (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  aver_wavefront(const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  aver_wavefront(const Graph& g, VertexIndexMap index)
Calculates the average wavefront of a graph (sum of all wavefronts devided by the number ob vertices).

Defined in

boost/graph/wavefront.hpp

rms_wavefront

  (1)
  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  rms_wavefront(const Graph& g)

  (2)
  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  rms_wavefront(const Graph& g, VertexIndexMap index)
Calculates the root mean square of all wavefronts.

Defined in

boost/graph/wavefront.hpp
Copyright © 2001-2002
Marc Wintermantel, ETH Zurich(wintermantel@imes.mavt.ethz.ch) 		Top


Writable Property Map

A Writable Property Map has the capability of setting the value object associated with the given key object via the put() function.

Refinement of

Copy Constructible

Notation

PMap | A type that is a model of Writable Property Map.
pmap | An object of type PMap.
key | An object of type boost::property_traits<PMap>::key_type.
val | An object of type boost::property_traits<PMap>::value_type.

Associated Types

Value Type | boost::property_traits<PMap>::value_type | The type of the property.
Key Type | boost::property_traits<PMap>::key_type | The type of the key object used to look up the property. The property map may be templated on the key type, in which case this typedef can be void.
Property Map Category | boost::property_traits<PMap>::category | The category of the property: a type convertible to writable_property_map_tag.

Valid Expressions

Name | Expression | Return Type | Description
Put Property Value[1] | put(pmap, key, val) | void | Assign val to the property associated with k.

Notes

[1] The function put() was originally named set(), but was changed to avoid name conflicts with the std::set class when using a compiler (Microsoft Visual C++) with non-standard name lookup rules. The following example demonstrates the problem.

#include <set>
using namespace std;
namespace boost {
  void set() { }
}

Concept Checking Class

  template <class PMap, class Key>
  struct WritablePropertyMapConcept
  {
    typedef typename property_traits<PMap>::key_type key_type;
    typedef typename property_traits<PMap>::category Category;
    typedef boost::writable_property_map_tag WritableTag;
    void constraints() {
      function_requires< ConvertibleConcept<Category, WritableTag> >();
      put(pmap, k, val);
    }
    PMap pmap;
    Key k;
    typename property_traits<PMap>::value_type val;
  };

See Also

Property map concepts

Copyright © 2000
Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)


Writable Property Map

Name

Writable Property Map: This has the capability of setting the value object associated with the given key object via the put() function.

Synopsis

template <class PMap, class Key>
  struct WritablePropertyMapConcept
  {
    typedef typename property_traits<PMap>::key_type key_type;
    typedef typename property_traits<PMap>::category Category;
    typedef boost::writable_property_map_tag WritableTag;
    void constraints() {
      function_requires< ConvertibleConcept<Category, WritableTag> >();
      put(pmap, k, val);
    }
    PMap pmap;
    Key k;
    typename property_traits<PMap>::value_type val;
  };

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Graph Library: write graphviz

write_graphviz

// Output graph structure without properties.
template < typename VertexListGraph >
void
write_graphviz(std::ostream& out, const VertexListGraph& g);

// Graph structure with customized property output
template < typename VertexListGraph, typename VertexPropertyWriter >
void
write_graphviz(std::ostream& out, const VertexListGraph& g,
               VertexPropertyWriter vpw);

template < typename VertexListGraph, typename VertexPropertyWriter,
           typename EdgePropertyWriter >
void
write_graphviz(std::ostream& out, const VertexListGraph& g,
               VertexPropertyWriter vpw, EdgePropertyWriter epw);

template < typename VertexListGraph, typename VertexPropertyWriter,
           typename EdgePropertyWriter, typename GraphPropertyWriter >
void
write_graphviz(std::ostream& out, const VertexListGraph& g,
               VertexPropertyWriter vpw, EdgePropertyWriter epw,
               GraphPropertyWriter gpw);

template < typename VertexListGraph, typename VertexPropertyWriter,
           typename EdgePropertyWriter, typename GraphPropertyWriter,
           typename VertexID >
void
write_graphviz(std::ostream& out, const VertexListGraph& g,
               VertexPropertyWriter vpw, EdgePropertyWriter epw,
               GraphPropertyWriter gpw, VertexID vertex_id);

// Graph structure with dynamic property output
template<typename Graph>
void
write_graphviz(std::ostream& out, const Graph& g,
               const dynamic_properties& dp,
               const std::string& node_id = "node_id");

template<typename Graph, typename VertexID>
void
write_graphviz(std::ostream& out, const Graph& g,
               const dynamic_properties& dp, const std::string& node_id,
               VertexID vertex_id);

This is to write a BGL graph object into an output stream in graphviz dot format so that users can make use of AT&T graphviz to draw a picture with nice layout.

The first version with two parameters will write the graph into a std::ostream where each vertex is represented by its numerical vertex ID. If users have either interior or exterior properties for each vertex in the graph, the second version above provides a way to print those properties into the graphviz format file. For example, if each vertex in the graph has its label through property map object name, users can use the second version:

write_graph(out, g, make_label_writer(name));

The utility function make_label_writer returns a predefined PropertyWriter for vertex labels. Similarly, the third version and fourth version require vertex PropertyWriter, edge PropertyWriter, and graph PropertyWriter, respectively.

The final two overloads of write_graphviz will emit all of the properties stored in the dynamic_properties object, thereby retaining the properties that have been read in through the dual function read_graphviz. With these overloads, node_id is the name of the property map that stores the IDs of each node for use in the output (if it is stored in the dynamic_properties structure); alternatively, one may provide an arbitrary property map for vertex_id giving the vertex identifiers.

Where Defined

boost/graph/graphviz.hpp

PropertyWriter

PropertyWriter is used in the write_graphviz function to print properties of vertex, edge or graph. There are two types of PropertyWriter. One is for a vertex or edge. The other is for a graph. Thus, users could easily extend the write_graphviz function by creating their own PropertyWriter only.

A PropertyWriter for vertices or edges is a functor which can be called with two parameters: std::ostream and either a vertex or an edge descriptor. It should output a pair of brackets with a series of assigments "name=value" inside. Each assignment should be separated either with space, with comma, or with semicolon. The following functor, provided by BGL, is the example of PropertyWriter for vertices or edges. It is used to print the label of each vertex or edge.

  template < class Name >
  class label_writer {
  public:
    label_writer(Name _name) : name(_name) {}
    template
    void operator()(std::ostream& out, const VertexOrEdge& v) const {
      out << "[label=\"" << name[v] << "\"]";
    }
  private:
    Name name;
  };

A function to conveniently create this writer is provided:

template < class Name >
label_writer<Name>
make_label_writer(Name n);

A PropertyWriter for graphs is a functor which is called with one parameter of type std::ostream and should print a series of graph properties. The following code excerpt is an example of a PropertyWriter for a graph.

  struct sample_graph_writer {
    void operator()(std::ostream& out) const {
      out << "graph [bgcolor=lightgrey]" << std::endl;
      out << "node [shape=circle color=white]" << std::endl;
      out << "edge [style=dashed]" << std::endl;
    }
  };
}

There exists a class default_writer, which can be used as both vertex/edge and graph property writer, and does nothing. It comes handy when only edge properties must be written, but function interface requries to pass vertex property writer as well.

Parameters

OUT: std::ostream& out
A standard std::ostream object.
IN: VertexListGraph& g
A directed or undirected graph. The graph's type must be a model of VertexListGraph. Also the graph must have an internal vertex_index property map.
IN: VertexPropertyWriter vpw
A functor that models PropertyWriter concept to print properties of a vertex.
Default: default_writer()
IN: EdgePropertyWriter epw
A functor that models PropertyWriter concept to print properties of an edge.
Default: default_writer()
IN: GraphPropertyWriter epw
A functor that models PropertyWriter concept to print properties of a graph.
Default: default_writer()
IN: dynamic_properties& dp
Contains all of the vertex and edge properties that should be emitted by the GraphViz writer.
IN: const std::string& node_id
The name of the property map that provides identifiers for the vertices in the graph.
Default: "node_id"
IN: VertexID vertex_id
A property map that models Readable Property Map whose key type is the vertex descriptor of the graph and whose value type can be written to a stream. The value should be a unique descriptor that can be used to name a node in a Graphviz file (so it should not, for instance, have any spaces in it).
Default: If no dynamic_properties object is provided, get(vertex_index, g). Otherwise, a dynamic property map that accesses the property map named node_id.

Example

This example demonstrates using BGL-graphviz interface to write a BGL graph into a graphviz format file.
#include <boost/graph/graphviz.hpp>

enum files_e { dax_h, yow_h, boz_h, zow_h, foo_cpp,
               foo_o, bar_cpp, bar_o, libfoobar_a,
               zig_cpp, zig_o, zag_cpp, zag_o,
                 libzigzag_a, killerapp, N };
const char* name[] = { "dax.h", "yow.h", "boz.h", "zow.h", "foo.cpp",
                       "foo.o", "bar.cpp", "bar.o", "libfoobar.a",
                       "zig.cpp", "zig.o", "zag.cpp", "zag.o",
                       "libzigzag.a", "killerapp" };

int main(int,char*[])
{

  typedef pair<int,int> Edge;
  Edge used_by[] = {
    Edge(dax_h, foo_cpp), Edge(dax_h, bar_cpp), Edge(dax_h, yow_h),
    Edge(yow_h, bar_cpp), Edge(yow_h, zag_cpp),
    Edge(boz_h, bar_cpp), Edge(boz_h, zig_cpp), Edge(boz_h, zag_cpp),
    Edge(zow_h, foo_cpp),
    Edge(foo_cpp, foo_o),
    Edge(foo_o, libfoobar_a),
    Edge(bar_cpp, bar_o),
    Edge(bar_o, libfoobar_a),
    Edge(libfoobar_a, libzigzag_a),
    Edge(zig_cpp, zig_o),
    Edge(zig_o, libzigzag_a),
    Edge(zag_cpp, zag_o),
    Edge(zag_o, libzigzag_a),
    Edge(libzigzag_a, killerapp)
  };
  const int nedges = sizeof(used_by)/sizeof(Edge);
  int weights[nedges];
  std::fill(weights, weights + nedges, 1);

  using namespace boost;

  typedef adjacency_list< vecS, vecS, directedS,
      property< vertex_color_t, default_color_type >,
      property< edge_weight_t, int >
    > Graph;
  Graph g(used_by, used_by + nedges, weights, N);

  write_graphviz(std::cout, g, make_label_writer(name));
}
The output will be:
digraph G {
0 -> 4;
0 -> 6;
0 -> 1;
0 [label="dax.h"];
1 -> 6;
1 -> 11;
1 [label="yow.h"];
2 -> 6;
2 -> 9;
2 -> 11;
2 [label="boz.h"];
3 -> 4;
3 [label="zow.h"];
4 -> 5;
4 [label="foo.cpp"];
5 -> 8;
5 [label="foo.o"];
6 -> 7;
6 [label="bar.cpp"];
7 -> 8;
7 [label="bar.o"];
8 -> 13;
8 [label="libfoobar.a"];
9 -> 10;
9 [label="zig.cpp"];
10 -> 13;
10 [label="zig.o"];
11 -> 12;
11 [label="zag.cpp"];
12 -> 13;
12 [label="zag.o"];
13 -> 14;
13 [label="libzigzag.a"];
14;
14 [label="killerapp"];
edge[style="dotted"];
6 -> 0;
}

The examples directory contains an example using dynamic_properties.

See Also

read_graphviz

Notes

Note that Graphviz dot file can be used to write facilities without the library libbglviz.a.
Copyright © 2000-2001
Lie-Quan Lee, Indiana University (llee@cs.indiana.edu)
Jeremy Siek, Indiana University (jsiek@osl.iu.edu) 		Top


Multidimensional Array Library

Name

const_multi_array_ref: This is a multi-dimensional container adaptor.

Synopsis

namespace boost {

template <typename ValueType,
          std::size_t NumDims,
          typename Allocator = std::allocator<ValueType> >
class const_multi_array_ref {
public:
// types:
  typedef ValueType                             element;
  typedef *unspecified*                         value_type;
  typedef *unspecified*                         reference;
  typedef *unspecified*                         const_reference;
  typedef *unspecified*                         difference_type;
  typedef *unspecified*                         iterator;
  typedef *unspecified*                         const_iterator;
  typedef *unspecified*                         reverse_iterator;
  typedef *unspecified*                         const_reverse_iterator;
  typedef multi_array_types::size_type          size_type;
  typedef multi_array_types::index              index;
  typedef multi_array_types::index_gen          index_gen;
  typedef multi_array_types::index_range        index_range;
  typedef multi_array_types::extent_gen         extent_gen;
  typedef multi_array_types::extent_range       extent_range;
  typedef *unspecified*                         storage_order_type;

  // template typedefs
  template <std::size_t Dims> struct            subarray;
  template <std::size_t Dims> struct            const_subarray;
  template <std::size_t Dims> struct            array_view;
  template <std::size_t Dims> struct            const_array_view;

  // structors

  template <typename ExtentList>
  explicit const_multi_array_ref(const element* data, const ExtentList& sizes,
                       const storage_order_type& store = c_storage_order());
  explicit const_multi_array_ref(const element* data, const extents_tuple& ranges,
                       const storage_order_type& store = c_storage_order());
  const_multi_array_ref(const const_multi_array_ref& x);
  ~const_multi_array_ref();

  // iterators:
  const_iterator			begin() const;
  const_iterator			end() const;
  const_reverse_iterator		rbegin() const;
  const_reverse_iterator		rend() const;

  // capacity:
  size_type				size() const;
  size_type				num_elements() const;
  size_type				num_dimensions() const;

  // element access:
  template <typename IndexList>
    const element&		operator()(const IndexList& indices) const;
  const_reference		operator[](index i) const;
  const_array_view<Dims>::type	operator[](const indices_tuple& r) const;

  // queries
  const element*		data() const;
  const element*		origin() const;
  const size_type*		shape() const;
  const index*			strides() const;
  const index*			index_bases() const;
  const storage_order_type&     storage_order() const;

  // comparators
  bool operator==(const const_multi_array_ref& rhs);
  bool operator!=(const const_multi_array_ref& rhs);
  bool operator<(const const_multi_array_ref& rhs);
  bool operator>(const const_multi_array_ref& rhs);
  bool operator>=(const const_multi_array_ref& rhs);
  bool operator<=(const const_multi_array_ref& rhs);

  // modifiers:
  template <typename SizeList>
  void			reshape(const SizeList& sizes)
  template <typename BaseList>	void reindex(const BaseList& values);
  void				reindex(index value);
};

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


MultiDimensional Array Libary

Boost.MultiArray

Boost.MultiArray provides a generic N-dimensional array concept definition and common implementations of that interface.

  • User Documentation and Tutorial

  • Reference Documentation

  • Test Cases and their Descriptions

  • Miscellaneous Notes

Copyright © 2000-2001
Ronald Garcia, Indiana University (garcia@osl.iu.edu)
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)
Andrew Lumsdaine, Indiana University (lums@osl.iu.edu)


Improved Iterator Categories and Requirements

Introduction

The standard iterator categories and requirements are flawed because they use a single hierarchy of requirements to address two orthogonal issues: iterator traversal and dereference return type. The current iterator requirement hierarchy is mainly geared towards iterator traversal (hence the category names), while requirements that address dereference return type sneak in at various places. The following table gives a summary of the current dereference return type requirements in the iterator categories.

Table 1. Summary of current dereference return type requirements.
Output Iterator | *i = a
Input Iterator | *i is convertible to T
Forward Iterator | *i is T& (or const T& once issue 200 is resolved)
Random Access Iterator | i[n] is convertible to T (which is odd because the operational semantics say i[n] is equivalent to *(i + n) which would have a return type of T&)

Examples of useful iterators that do not ``fit''

Because of the mixing of iterator traversal and dereference return type, many useful iterators can not be appropriately categorized. For example, vector<bool>::iterator is almost a random access iterator, but the return type is not bool& (see issue 96 and Herb Sutter's paper J16/99-0008 = WG21 N1185). Therefore, the iterators only meet the requirements of input iterator and output iterator. This is so nonintuitive that at least one implementation erroneously assigns random_access_iterator_tag as its iterator_category. Also, vector<bool> is not the only example of useful iterators that do not return true references: there is the often cited example of disk-based collections.

Another example is a counting iterator, an iterator the returns a sequence of integers when incremented and dereferenced (see boost::counting_iterator). There are two ways to implement this iterator, 1) make the reference type be a true reference (a reference to an integer data member of the counting iterator) or 2) make the reference type be the same as the value_type. Option 1) runs into the problems discussed in Issue 198, the reference will not be valid after the iterator is destroyed. Option 2) is therefore a better choice, but then we have a counting iterator that cannot be a random access iterator.

Yet another example is a transform iterator, an iterator adaptor that applies a unary function object to the dereference value of the wrapped iterator (see boost::transform_iterator). For unary functions such as std::times t he return type of operator* c learly needs to be the result_type of the function object, which is typically not a reference. However, with the current iterator requirements, if the user wraps int* with a transform iterator,the user will not get a random access iterator as expected, but an input iterator.

A fourth example is found in the vertex and edge iterators of the Boost Graph Library. These iterators return vertex and edge descriptors, which are lightweight handles created on-the-fly. They must be returned by-value. As a result, their current standard iterator category is std::input_iterator_tag, which means that, strictly speaking, the user should not use these iterators with algorithms like std::min_element(). As a temporary solution, we introduced the concept Multi-Pass Input Iterator to describe the vertex and edge descriptors, but as the design notes for concept suggest, a better solution is needed.

In short, there are many useful iterators that do not fit into the current standard iterator categories. As a result, the following bad things happen:

  • Iterators are often miss-categorized.
  • Algorithm requirements are more strict than necessary, because they can not separate out the need for random-access from the need for a true reference return type.

Proposal for new iterator categories and requirements

The iterator requirements should be separated into two hierarchies. One set of concepts handles the return type semantics:
  • Readable Iterator
  • Writable Iterator
  • Swappable Iterator
  • Constant Lvalue Iterator
  • Mutable Lvalue Iterator
The other set of concepts handles iterator traversal:
  • Forward Traversal Iterator
  • Bidirectional Traversal Iterator
  • Random Access Traversal Iterator
The current Input Iterator and Output Iterator requirements will continue to be used as is. Note that Input Iterator implies Readable Iterator and Output Iterator implies Writable Iterator.

Note: we considered defining a Single-Pass Iterator, which could be combined with Readable or Writable Iterator to replace the Input and Output Iterator requirements. We rejected this idea because there are several differences between Input and Output Iterators that make it hard to merge them: Input Iterator requires Equality Comparable while Output Iterator does not and Input Iterator requires Assignable while Output Iterator does not.

New categories and traits classes

Each of the new iterator requirements will need a category tag.

namespace std {

  // Return Type Categories
  struct readable_iterator_tag { };
  struct writable_iterator_tag { };
  struct swappable_iterator_tag { };
  struct mutable_lvalue_iterator_tag : virtual public writable_iterator_tag,
    virtual public readable_iterator_tag { };
  struct constant_lvalue_iterator_tag : public readable_iterator_tag { };

  // Traversal Categories
  struct forward_traversal_tag { };
  struct bidirectional_traversal_tag : public forward_traversal_tag { };
  struct random_access_traversal_tag : public bidirectional_traversal_tag { };

}

And there will need to be a way to access these category tags using a traits mechanism. Adding new typedefs to std::iterator_traits is not an acceptable solution because that would break every existing iterator. Instead, we propose two new traits classes. It is important that these traits classes are backward compatible, that is, they should work with any iterator for which there is a valid definition of std::iterator_traits. This can be accomplished by making the default behavior of the traits classes map the iterator_category of the iterator to the appropriate return or traversal category. For new iterators, either specializations of these traits classes can be defined, or the iterator can provide nested typedefs, and inherit from new_iterator_base (which is just a signal to the traits class that it is a new iterator). As with std::iterator_traits, specializations for T* are provided.

namespace std {

  struct new_iterator_base { };

  template <typename Iterator>
  struct return_category
  {
    // Pseudo-code
    if (Iterator inherits from new_iterator_base) {
      typedef typename Iterator::return_category type;
    } else {
      typedef std::iterator_traits<Iterator> OldTraits;
      typedef typename OldTraits::iterator_category Cat;
      if (Cat inherits from std::forward_iterator_tag)
if (is-const(T))
  typedef boost::constant_lvalue_iterator_tag type;
else
typedef boost::mutable_lvalue_iterator_tag type;
      else if (Cat inherits from std::input_iterator_tag)
typedef boost::readable_iterator_tag type;
      else if (Cat inherits from std::output_iterator_tag)
typedef boost::writable_iterator_tag type;
    }
  };

template <typename T>
  struct return_category<T*>
  {
    // Pseudo-code
    if (is-const(T))
      typedef boost::constant_lvalue_iterator_tag type;
    else
      typedef boost::mutable_lvalue_iterator_tag type;
  };

template <typename Iterator>
  struct traversal_category
  {
    // Pseudo-code
    if (Iterator inherits from new_iterator_base) {
      typedef typename Iterator::traversal_category type;
    } else {
      typedef std::iterator_traits<Iterator> OldTraits;
      typedef typename OldTraits::iterator_category Cat;

      if (Cat inherits from std::random_access_iterator_tag)
typedef boost::random_access_traversal_tag type;
      else if (Cat inherits from std::bidirectional_iterator_tag)
typedef boost::bidirectional_traversal_tag type;
      else if (Cat inherits from std::forward_iterator_tag)
typedef boost::forward_traversal_tag type;
    }
  };

  template <typename T>
  struct traversal_category<T*>
  {
    typedef boost::random_access_traversal_tag type;
  };

}

Impact on the Standard Algorithms

Many of the standard algorithms place more requirements than necessary on their iterator parameters due to the coarseness of the current iterator categories. By using the new iterator categories a better fit can be achieved, thereby increasing the reusability of the algorithms. These changes will not affect user-code, though they will require changes by standard implementers: dispatching should be based on the new categories, and in places return values may need to be handled more carefully. In particular, uses of std::swap() will need to be replaced with std::iter_swap(), and std::iter_swap() will need to call std::swap().

Table 2. Requirement changes for standard algorithms.
Algorithm | Requirement Change
find_end | Forward Iterator -> Forward Traversal Iterator and Readable Iterator
find_first_of
adjacent_find
search
search_n
rotate_copy
lower_bound
upper_bound
equal_range
binary_search
min_element
max_element
iter_swap | Forward Iterator -> Swappable Iterator
fill | Forward Iterator -> Forward Traversal Iterator and Writable Iterator
generate
swap_ranges | Forward Iterator -> Forward Traversal Iterator and Swappable Iterator
rotate
replace | Forward Iterator -> Forward Traversal Iterator and Readable Iterator and Writable Iterator
replace_if
remove
remove_if
unique
reverse | Bidirectional Iterator -> Bidirectional Traversal Iterator and Swappable Iterator
partition
copy_backwards | Bidirectional Iterator -> Bidirectional Traversal Iterator and Readable Iterator Bidirectional Iterator -> Bidirectional Traversal Iterator and Writable Iterator
next_permutation | Bidirectional Iterator -> Bidirectional Traversal Iterator and Swappable Iterator and Readable Iterator
prev_permutation
stable_partition | Bidirectional Iterator -> Bidirectional Traversal Iterator and Readable Iterator and Writable Iterator
inplace_merge
reverse_copy | Bidirectional Iterator -> Bidirectional Traversal Iterator and Readable Iterator
random_shuffle | Random Access Iterator -> Random Access Traversal Iterator and Swappable Iterator
sort
stable_sort
partial_sort
nth_element
push_heap
pop_heap
make_heap
sort_heap

The New Iterator Requirements

Notation

X | The iterator type.
T | The value type of X, i.e., std::iterator_traits<X>::value_type.
x, y | An object of type X.
t | An object of type T.

Readable Iterator

A Readable Iterator is an iterator that dereferences to produce an rvalue that is convertible to the value_type of the iterator.

Associated Types

Value type | std::iterator_traits<X>::value_type | The type of the objects pointed to by the iterator.
Reference type | std::iterator_traits<X>::reference | The return type of dereferencing the iterator. This type must be convertible to T.
Return Category | std::return_category<X>::type | A type convertible to std::readable_iterator_tag

Refinement of

Copy Constructible

Valid expressions

Name | Expression | Type requirements | Return type
Dereference | *x |  | std::iterator_traits<X>::reference
Member access | x->m | T is a type with a member named m. | If m is a data member, the type of m. If m is a member function, the return type of m.

Writable Iterator

A Writable Iterator is an iterator that can be used to store a value using the dereference-assignment expression.

Definitions

If x is an Writable Iterator of type X, then the expression *x = a; stores the value a into x. Note that operator=, like other C++ functions, may be overloaded; it may, in fact, even be a template function. In general, then, a may be any of several different types. A type A belongs to the set of value types of X if, for an object a of type A, *x = a; is well-defined and does not require performing any non-trivial conversions on a.

Associated Types

Return Category std::return_category<X>::type A type convertible to std::writable_iterator_tag

Refinement of

Copy Constructible

Valid expressions

Name | Expression | Return type
Dereference assignment | *x = a | unspecified

Swappable Iterator

A Swappable Iterator is an iterator whose dereferenced values can be swapped.

Note: the requirements for Swappable Iterator are dependent on the issues surrounding std::swap() being resolved. Here we assume that the issue will be resolved by allowing the overload of std::swap() for user-defined types.

Note: Readable Iterator and Writable Iterator combined implies Swappable Iterator because of the fully templated std::swap(). However, Swappable Iterator does not imply Readable Iterator nor Writable Iterator.

Associated Types

Return Category std::return_category<X>::type A type convertible to std::swappable_iterator_tag

Valid expressions

Of the two valid expressions listed below, only one OR the other is required. If std::iter_swap() is overloaded for X t hen std::swap() is not required. If std::iter_swap() is not overloaded for X then the default (fully templated) version is used, which will call std::swap() (this means changing the current requirements for std::iter_swap()).

Name | Expression | Return type
Iterator Swap | std::iter_swap(x, y) | void
Dereference and Swap | std::swap(*x, *y) | void

Constant Lvalue Iterator

A Constant Lvalue Iterator is an iterator that dereferences to produce a const reference to the pointed-to object, i.e., the associated reference type is const T&. Changing the value of or destroying an iterator that models Constant Lvalue Iterator does not invalidate pointers and references previously obtained from that iterator.

Refinement of

Readable Iterator

Associated Types

Reference type | std::iterator_traits<X>::reference | The return type of dereferencing the iterator, which must be const T&.
Return Category | std::return_category<X>::type | A type convertible to std::constant_lvalue_iterator_tag

Mutable Lvalue Iterator

A Mutable Lvalue Iterator is an iterator that dereferences to produce a reference to the pointed-to object. The associated reference type is T&. Changing the value of or destroying an iterator that models Mutable Lvalue Iterator does not invalidate pointers and references previously obtained from that iterator.

Refinement of

Readable Iterator, Writable Iterator, and Swappable Iterator.

Associated Types

Reference type | std::iterator_traits<X>::reference | The return type of dereferencing the iterator, which must be T&.
Return Category | std::return_category<X>::type | A type convertible to std::mutable_lvalue_iterator_tag

Forward Traversal Iterator

The Forward Iterator is an iterator that can be incremented. Also, it is permissible to make multiple passes through the iterator's range.

Refinement of

Copy Constructible, Assignable, Default Constructible, and Equality Comparable

Associated types

Difference Type | std::iterator_traits<X>::difference_type | A signed integral type used for representing distances between iterators that point into the same range.
Traversal Category | std::traversal_category<X>::type | A type convertible to std::forward_traversal_tag

Valid expressions

Name | Expression | Type requirements | Return type
Preincrement | ++i |  | X&
Postincrement | i++ |  | convertible to const X&

Bidirectional Traversal Iterator

An iterator that can be incremented and decremented.

Refinement of

Forward Traversal Iterator

Associated types

Traversal Category std::traversal_category<X>::type A type convertible to std::bidirectional_traversal_tag

Valid expressions

Name | Expression | Type requirements | Return type
Predecrement | --i |  | X&
Postdecrement | i-- |  | convertible to const X&

Random Access Traversal Iterator

An iterator that provides constant-time methods for moving forward and backward in arbitrary-sized steps.

Refinement of

Bidirectional Traversal Iterator and Less Than Comparable where < is a total ordering

Associated types

Traversal Category std::traversal_category<X>::type A type convertible to std::random_access_traversal_tag

Valid expressions

Name | Expression | Type requirements | Return type
Iterator addition | i += n |  | X&
Iterator addition | i + n or n + i |  | X
Iterator subtraction | i -= n |  | X&
Iterator subtraction | i - n |  | X
Difference | i - j |  | std::iterator_traits<X>::difference_type
Element operator | i[n] | X must also be a model of Readable Iterator. | std::iterator_traits<X>::reference
Element assignment | i[n] = t | X must also be a model of Writable Iterator. | unspecified

Jeremy Siek, University of Notre Dame
jsiek@lsc.nd.edu 		Top


MultiDimensional Array Library

Name

Boost.MultiArray: It provides a generic N-dimensional array concept definition and common implementations of that interface.

Synopsis

namespace boost {

  namespace multi_array_types {
    typedef *unspecified* index;
    typedef *unspecified* size_type;
    typedef *unspecified* difference_type;
    typedef *unspecified* index_range;
    typedef *unspecified* extent_range;
    typedef *unspecified* index_gen;
    typedef *unspecified* extent_gen;
  }

  template <typename ValueType,
            std::size_t NumDims,
            typename Allocator = std::allocator<ValueType> >
  class multi_array;

  template <typename ValueType,
            std::size_t NumDims>
  class multi_array_ref;

  template <typename ValueType,
            std::size_t NumDims>
  class const_multi_array_ref;

  multi_array_types::extent_gen extents;
  multi_array_types::index_gen  indices;

  template <typename Array, int N> class subarray_gen;
  template <typename Array, int N> class const_subarray_gen;
  template <typename Array, int N> class array_view_gen;
  template <typename Array, int N> class const_array_view_gen;

  class c_storage_order;
  class fortran_storage_order;
  template <std::size_t NumDims> class general_storage_order;

}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Multidimensional Array Library

Name

Boost.MultiArray: It provides a generic N-dimensional array concept definition and common implementations of that interface.

Synopsis

namespace boost {

  namespace multi_array_types {
    typedef *unspecified* index;
    typedef *unspecified* size_type;
    typedef *unspecified* difference_type;
    typedef *unspecified* index_range;
    typedef *unspecified* extent_range;
    typedef *unspecified* index_gen;
    typedef *unspecified* extent_gen;
  }

  template <typename ValueType,
            std::size_t NumDims,
            typename Allocator = std::allocator<ValueType> >
  class multi_array;

  template <typename ValueType,
            std::size_t NumDims>
  class multi_array_ref;

  template <typename ValueType,
            std::size_t NumDims>
  class const_multi_array_ref;

  multi_array_types::extent_gen extents;
  multi_array_types::index_gen  indices;

  template <typename Array, int N> class subarray_gen;
  template <typename Array, int N> class const_subarray_gen;
  template <typename Array, int N> class array_view_gen;
  template <typename Array, int N> class const_array_view_gen;

  class c_storage_order;
  class fortran_storage_order;
  template <std::size_t NumDims> class general_storage_order;

}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost.MultiArray: Extra Notes

Compiler Support

Boost.MultiArray was designed with compiler support in mind. Interface compromises were kept to a minimum, but efforts were made to account for C++ Standard compliance issues in compilers that were popular at the time the library was written. That said, Boost.MultiArray has been tested with the following compilers:
  1. Gnu C++ Compiler v2.95.2 (compile with -ftemplate-depth-50)
  2. Gnu C++ Compiler v3.0.2
  3. Kuck and Associates C++ Compiler v4.0f
  4. Microsoft Visual C++ v6.0 sp5
  5. Comeau C++ Compiler v4.2.45 beta 2 (libcomo beta 14)
  6. Intel C++ Compiler v5.0 (with MS lib v6.0 sp5)
  7. Metrowerks CodeWarrior C++ Compiler v7.1
Boost.MultiArray is known to NOT work with Borland C++ v5.5.1. Support for this compiler is forthcoming.

Future Work

The following is a list of work that is intended for the near future -- following the initial addition of the library to the Boost distribution:
  1. Pursue and document performance issues related to compilers and implementation.
  2. More comprehensive examples of using the library components.
  3. Extensive rewrite of the user documentation to make it more accessible.


Name

multi_array_ref: This is a multi-dimensional container adaptor.

Synopsis

namespace boost {

template <typename ValueType,
          std::size_t NumDims,
          typename Allocator = std::allocator<ValueType> >
class multi_array_ref {
public:
// types:
  typedef ValueType                             element;
  typedef *unspecified*                         value_type;
  typedef *unspecified*                         reference;
  typedef *unspecified*                         const_reference;
  typedef *unspecified*                         difference_type;
  typedef *unspecified*                         iterator;
  typedef *unspecified*                         const_iterator;
  typedef *unspecified*                         reverse_iterator;
  typedef *unspecified*                         const_reverse_iterator;
  typedef multi_array_types::size_type          size_type;
  typedef multi_array_types::index              index;
  typedef multi_array_types::index_gen          index_gen;
  typedef multi_array_types::index_range        index_range;
  typedef multi_array_types::extent_gen         extent_gen;
  typedef multi_array_types::extent_range       extent_range;
  typedef *unspecified*                         storage_order_type;

  // template typedefs
  template <std::size_t Dims> struct            subarray;
  template <std::size_t Dims> struct            const_subarray;
  template <std::size_t Dims> struct            array_view;
  template <std::size_t Dims> struct            const_array_view;

  // structors

  template <typename ExtentList>
  explicit multi_array_ref(element* data, const ExtentList& sizes,
                       const storage_order_type& store = c_storage_order());
  explicit multi_array_ref(element* data, const extents_tuple& ranges,
                       const storage_order_type& store = c_storage_order());
  multi_array_ref(const multi_array_ref& x);
  ~multi_array_ref();

  // modifiers

  multi_array_ref& operator=(const multi_array_ref& x);
  template <class Array> multi_array_ref& operator=(const Array& x);

  // iterators:
  iterator				begin();
  iterator				end();
  const_iterator			begin() const;
  const_iterator			end() const;
  reverse_iterator			rbegin();
  reverse_iterator			rend();
  const_reverse_iterator		rbegin() const;
  const_reverse_iterator		rend() const;

  // capacity:
  size_type				size() const;
  size_type				num_elements() const;
  size_type				num_dimensions() const;

  // element access:
  template <typename IndexList>
    element&			operator()(const IndexList& indices);
  template <typename IndexList>
    const element&		operator()(const IndexList& indices) const;
  reference			operator[](index i);
  const_reference		operator[](index i) const;
  array_view<Dims>::type	operator[](const indices_tuple& r);
  const_array_view<Dims>::type	operator[](const indices_tuple& r) const;

  // queries
  element*			data();
  const element*		data() const;
  element*			origin();
  const element*		origin() const;
  const size_type*		shape() const;
  const index*			strides() const;
  const index*			index_bases() const;
  const storage_order_type&     storage_order() const;

  // comparators
  bool operator==(const multi_array_ref& rhs);
  bool operator!=(const multi_array_ref& rhs);
  bool operator<(const multi_array_ref& rhs);
  bool operator>(const multi_array_ref& rhs);
  bool operator>=(const multi_array_ref& rhs);
  bool operator<=(const multi_array_ref& rhs);

  // modifiers:
  template <typename InputIterator>
    void			assign(InputIterator begin, InputIterator end);
  template <typename SizeList>
    void			reshape(const SizeList& sizes)
  template <typename BaseList>	void reindex(const BaseList& values);
  void				reindex(index value);
};

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost.MultiArray Reference Manual

Table of Contents

Library Synopsis
MultiArray Concept
Notation
Associated Types
Valid expressions
Complexity guarantees
Invariants
Associated Types for Views
Models
Array Components
multi_array
multi_array_ref
const_multi_array_ref
Auxiliary Components
multi_array_types
extent_range
extent_gen
Global Objects
View and SubArray Generators
Memory Layout Specifiers
Range Checking

Boost.MultiArray is composed of several components. The MultiArray concept defines a generic interface to multidimensional containers. multi_array is a general purpose container class that models MultiArray. multi_array_ref and const_multi_array_ref are adapter classes. Using them, manipulate any block of contiguous data as though it were a multi_array. const_multi_array_ref differs from multi_array_ref in that its elements cannot be modified through its interface. Finally, several auxiliary classes are used to create and specialize arrays and some global objects are defined as part of the library interface.

Library Synopsis

To use Boost.MultiArray, include the header boost/multi_array.hpp in the source. This file brings the following declarations into scope:

namespace boost {

  namespace multi_array_types {
    typedef *unspecified* index;
    typedef *unspecified* size_type;
    typedef *unspecified* difference_type;
    typedef *unspecified* index_range;
    typedef *unspecified* extent_range;
    typedef *unspecified* index_gen;
    typedef *unspecified* extent_gen;
  }

  template <typename ValueType,
            std::size_t NumDims,
            typename Allocator = std::allocator<ValueType> >
  class multi_array;

  template <typename ValueType,
            std::size_t NumDims>
  class multi_array_ref;

  template <typename ValueType,
            std::size_t NumDims>
  class const_multi_array_ref;

  multi_array_types::extent_gen extents;
  multi_array_types::index_gen  indices;

  template <typename Array, int N> class subarray_gen;
  template <typename Array, int N> class const_subarray_gen;
  template <typename Array, int N> class array_view_gen;
  template <typename Array, int N> class const_array_view_gen;

  class c_storage_order;
  class fortran_storage_order;
  template <std::size_t NumDims> class general_storage_order;

}

MultiArray Concept

The MultiArray concept defines an interface to hierarchically nested containers. It specifies operations for accessing elements, traversing containers, and creating views of array data. MultiArray defines a flexible memory model that accomodates a variety of data layouts.

At each level (or dimension) of a MultiArray's container hierarchy lie a set of ordered containers, each of which contains the same number and type of values. The depth of this container hierarchy is the MultiArray's dimensionality. MultiArray is recursively defined; the containers at each level of the container hierarchy model MultiArray as well. While each dimension of a MultiArray has its own size, the list of sizes for all dimensions defines the shape of the entire MultiArray. At the base of this hierarchy lie 1-dimensional MultiArrays. Their values are the contained objects of interest and not part of the container hierarchy. These are the MultiArray's elements.

Like other container concepts, MultiArray exports iterators to traverse its values. In addition, values can be addressed directly using the familiar bracket notation.

MultiArray also specifies routines for creating specialized views. A view lets to treat a subset of the underlying elements in a MultiArray as though it were a separate MultiArray. Since a view refers to the same underlying elements, changes made to a view's elements will be reflected in the original MultiArray. For example, given a 3-dimensional "cube" of elements, a 2-dimensional slice can be viewed as if it were an independent MultiArray. Views are created using index_gen and index_range objects. index_ranges denote elements from a certain dimension that are to be included in a view. index_gen aggregates range data and performs bookkeeping to determine the view type to be returned. MultiArray's operator[] must be passed the result of N chained calls to index_gen::operator[], i.e.

indices[a0][a1]...[aN];

where N is the MultiArray's dimensionality and indices an object of type index_gen. The view type is dependent upon the number of degenerate dimensions specified to index_gen. A degenerate dimension occurs when a single-index is specified to index_gen for a certain dimension. For example, if indices is an object of type index_gen, then the following example:

indices[index_range(0,5)][2][index_range(0,4)];

has a degenerate second dimension. The view generated from the above specification will have 2 dimensions with shape 5 x 4. If the "2" above were replaced with another index_range object, for example:

indices[index_range(0,5)][index_range(0,2)][index_range(0,4)];

then the view would have 3 dimensions.

MultiArray exports information regarding the memory layout of its contained elements. Its memory model for elements is completely defined by 4 properties: the origin, shape, index bases, and strides. The origin is the address in memory of the element accessed as a[0][0]...[0], where a is a MultiArray. The shape is a list of numbers specifying the size of containers at each dimension. For example, the first extent is the size of the outermost container, the second extent is the size of its subcontainers, and so on. The index bases are a list of signed values specifying the index of the first value in a container. All containers at the same dimension share the same index base. Note that since positive index bases are possible, the origin need not exist in order to determine the location in memory of the MultiArray's elements. The strides determine how index values are mapped to memory offsets. They accomodate a number of possible element layouts. For example, the elements of a 2 dimensional array can be stored by row (i.e., the elements of each row are stored contiguously) or by column (i.e., the elements of each column are stored contiguously).

Notation

What follows are the descriptions of symbols that will be used to describe the MultiArray interface.

Table 1. Notation

A | A type that is a model of MultiArray
a,b | Objects of type A
NumDims | The numeric dimension parameter associated with A.
Dims | Some numeric dimension parameter such that 0<Dims<NumDims.
indices | An object created by some number of chained calls to index_gen::operator[](index_range).
index_list | An object whose type models Collection
idx | A signed integral value.
tmp | An object of type boost::array<index,NumDims>

Associated Types

Table 2. Associated Types

Type | Description
value_type | This is the value type of the container. If NumDims == 1, then this is element. Otherwise, this is the value type of the immediately nested containers.
reference | This is the reference type of the contained value. If NumDims == 1, then this is element&. Otherwise, this is the same type as template subarray<NumDims-1>::type.
const_reference | This is the const reference type of the contained value. If NumDims == 1, then this is const element&. Otherwise, this is the same type as template const_subarray<NumDims-1>::type.
size_type | This is an unsigned integral type. It is primarily used to specify array shape.
difference_type | This is a signed integral type used to represent the distance between two iterators. It is the same type as std::iterator_traits<iterator>::difference_type.
iterator | This is an iterator over the values of A. If NumDims == 1, then it models Random Access Iterator. Otherwise it models Random Access Traversal Iterator, Readable Iterator, and Writable Iterator.
const_iterator | This is the const iterator over the values of A.
reverse_iterator | This is the reversed iterator, used to iterate backwards over the values of A.
const_reverse_iterator | This is the reversed const iterator. A.
element | This is the type of objects stored at the base of the hierarchy of MultiArrays. It is the same as template subarray<1>::value_type
index | This is a signed integral type used for indexing into A. It is also used to represent strides and index bases.
index_gen | This type is used to create a tuple of index_ranges passed to operator[] to create an array_view<Dims>::type object.
index_range | This type specifies a range of indices over some dimension of a MultiArray. This range will be visible through an array_view<Dims>::type object.
template subarray<Dims>::type | This is subarray type with Dims dimensions. It is the reference type of the (NumDims - Dims) dimension of A and also models MultiArray.
template const_subarray<Dims>::type | This is the const subarray type.
template array_view<Dims>::type | This is the view type with Dims dimensions. It is returned by calling operator[](indices). It models MultiArray.
template const_array_view<Dims>::type | This is the const view type with Dims dimensions.

Valid expressions

Table 3. Valid Expressions

Expression | Return type | Semantics
a.shape() | const size_type* | This returns a list of NumDims elements specifying the extent of each array dimension.
a.strides() | const index* | This returns a list of NumDims elements specifying the stride associated with each array dimension. When accessing values, strides is used to calculate an element's location in memory.
a.index_bases() | const index* | This returns a list of NumDims elements specifying the numeric index of the first element for each array dimension.
a.origin() | element* if a is mutable, const element* otherwise. | This returns the address of the element accessed by the expression a[0][0]...[0].. If the index bases are positive, this element won't exist, but the address can still be used to locate a valid element given its indices.
a.num_dimensions() | size_type | This returns the number of dimensions of the array (note that a.num_dimensions() == NumDims).
a.num_elements() size_type This returns the number of elements contained in the array. It is equivalent to the following code:

std::accumulate(a.shape(),a.shape+a.num_dimensions(),
    size_type(1),std::multiplies<size_type>());

a.size() | size_type | This returns the number of values contained in a. It is equivalent to a.shape()[0];
a(index_list) element&; if a is mutable, const element& otherwise. This expression accesses a specific element of a.index_list is the unique set of indices that address the element returned. It is equivalent to the following code (disregarding intermediate temporaries):

// multiply indices by strides
    std::transform(index_list.begin(), index_list.end(),
      a.strides(), tmp.begin(), std::multiplies<index>()),

    // add the sum of the products to the origin
    *std::accumulate(tmp.begin(), tmp.end(), a.origin());

a.begin() | iterator if a is mutable, const_iterator otherwise. | This returns an iterator pointing to the beginning of a.
a.end() | iterator if a is mutable, const_iterator otherwise. | This returns an iterator pointing to the end of a.
a.rbegin() | reverse_iterator if a is mutable, const_reverse_iterator otherwise. | This returns a reverse iterator pointing to the beginning of a reversed.
a.rend() | reverse_iterator if a is mutable, const_reverse_iterator otherwise. | This returns a reverse iterator pointing to the end of a reversed.
a[idx] | reference if a is mutable, const_reference otherwise. | This returns a reference type that is bound to the index idx value of a. Note that if i is the index base for this dimension, the above expression returns the (idx-i)th element (counting from zero). The expression is equivalent to *(a.begin()+idx-a.index_bases()[0]);.
a[indices] | array_view<Dims>::type if a is mutable, const_array_view<Dims>::type otherwise. | This expression generates a view of the array determined by the index_range and index values used to construct indices.
a == b | bool | This performs a lexicographical comparison of the values of a and b. The element type must model EqualityComparable for this expression to be valid.
a < b | bool | This performs a lexicographical comparison of the values of a and b. The element type must model LessThanComparable for this expression to be valid.
a <= b | bool | This performs a lexicographical comparison of the values of a and b. The element type must model EqualityComparable and LessThanComparable for this expression to be valid.
a > b | bool | This performs a lexicographical comparison of the values of a and b. The element type must model EqualityComparable and LessThanComparable for this expression to be valid.
a >= b | bool | This performs a lexicographical comparison of the values of a and b. The element type must model LessThanComparable for this expression to be valid.

Complexity guarantees

begin() and end() execute in amortized constant time. size() executes in at most linear time in the MultiArray's size.

Invariants

Table 4. Invariants

Valid range | [a.begin(),a.end()) is a valid range.
Range size | a.size() == std::distance(a.begin(),a.end());.
Completeness | Iteration through the range [a.begin(),a.end()) will traverse across every value_type of a.
Accessor Equivalence | Calling a[a1][a2]...[aN] where N==NumDims yields the same result as calling a(index_list), where index_list is a Collection containing the values a1...aN.

Associated Types for Views

The following MultiArray associated types define the interface for creating views of existing MultiArrays. Their interfaces and roles in the concept are described below.

index_range

index_range objects represent half-open strided intervals. They are aggregated (using an index_gen object) and passed to a MultiArray's operator[] to create an array view. When creating a view, each index_range denotes a range of valid indices along one dimension of a MultiArray. Elements that are accessed through the set of ranges specified will be included in the constructed view. In some cases, an index_range is created without specifying start or finish values. In those cases, the object is interpreted to start at the beginning of a MultiArray dimension and end at its end.

index_range objects can be constructed and modified several ways in order to allow convenient and clear expression of a range of indices. To specify ranges, index_range supports a set of constructors, mutating member functions, and a novel specification involving inequality operators. Using inequality operators, a half open range [5,10) can be specified as follows:

5 <= index_range() < 10;

or

4 < index_range() <= 9;

and so on. The following describes the index_range interface.

Table 5. Notation

i | An object of type index_range.
idx,idx1,idx2,idx3 | Objects of type index.

Table 6. Associated Types

Type | Description
index | This is a signed integral type. It is used to specify the start, finish, and stride values.
size_type | This is an unsigned integral type. It is used to report the size of the range an index_range represents.

Table 7. Valid Expressions

Expression | Return type | Semantics
index_range(idx1,idx2,idx3) | index_range | This constructs an index_range representing the interval [idx1,idx2) with stride idx3.
index_range(idx1,idx2) | index_range | This constructs an index_range representing the interval [idx1,idx2) with unit stride. It is equivalent to index_range(idx1,idx2,1).
index_range() | index_range | This construct an index_range with unspecified start and finish values.
i.start(idx1) | index& | This sets the start index of i to idx.
i.finish(idx) | index& | This sets the finish index of i to idx.
i.stride(idx) | index& | This sets the stride length of i to idx.
i.start() | index | This returns the start index of i.
i.finish() | index | This returns the finish index of i.
i.stride() | index | This returns the stride length of i.
i.get_start(idx) | index | If i specifies a start value, this is equivalent to i.start(). Otherwise it returns idx.
i.get_finish(idx) | index | If i specifies a finish value, this is equivalent to i.finish(). Otherwise it returns idx.
i.size(idx) | size_type | If i specifies a both finish and start values, this is equivalent to (i.finish()-i.start())/i.stride(). Otherwise it returns idx.
i < idx | index | This is another syntax for specifying the finish value. This notation does not include idx in the range of valid indices. It is equivalent to index_range(r.start(), idx, r.stride())
i <= idx | index | This is another syntax for specifying the finish value. This notation includes idx in the range of valid indices. It is equivalent to index_range(r.start(), idx + 1, r.stride())
idx < i | index | This is another syntax for specifying the start value. This notation does not include idx in the range of valid indices. It is equivalent to index_range(idx + 1, i.finish(), i.stride()).
idx <= i | index | This is another syntax for specifying the start value. This notation includes idx1 in the range of valid indices. It is equivalent to index_range(idx, i.finish(), i.stride()).
i + idx | index | This expression shifts the start and finish values of i up by idx. It is equivalent to index_range(r.start()+idx1, r.finish()+idx, r.stride())
i - idx | index | This expression shifts the start and finish values of i up by idx. It is equivalent to index_range(r.start()-idx1, r.finish()-idx, r.stride())

index_gen

index_gen aggregates index_range objects in order to specify view parameters. Chained calls to operator[] store range and dimension information used to instantiate a new view into a MultiArray.

Table 8. Notation

Dims,Ranges | Unsigned integral values.
x | An object of type template gen_type<Dims,Ranges>::type.
i | An object of type index_range.
idx | Objects of type index.

Table 9. Associated Types

Type | Description
index | This is a signed integral type. It is used to specify degenerate dimensions.
size_type | This is an unsigned integral type. It is used to report the size of the range an index_range represents.
template gen_type::<Dims,Ranges>::type | This type generator names the result of Dims chained calls to index_gen::operator[]. The Ranges parameter is determined by the number of degenerate ranges specified (i.e. calls to operator[](index)). Note that index_gen and gen_type<0,0>::type are the same type.

Table 10. Valid Expressions

Expression | Return type | Semantics
index_gen() | gen_type<0,0>::type | This constructs an index_gen object. This object can then be used to generate tuples of index_range values.
x[i] | gen_type<Dims+1,Ranges+1>::type | Returns a new object containing all previous index_range objects in addition to i. Chained calls to operator[] are the means by which index_range objects are aggregated.
x[idx] | gen_type<Dims,Ranges>::type | Returns a new object containing all previous index_range objects in addition to a degenerate range, index_range(idx,idx). Note that this is NOT equivalent to x[index_range(idx,idx)]., which will return an object of type gen_type<Dims+1,Ranges+1>::type.

Models

  • multi_array
  • multi_array_ref
  • const_multi_array_ref
  • template array_view<Dims>::type
  • template const_array_view<Dims>::type
  • template subarray<Dims>::type
  • template const_subarray<Dims>::type

Array Components

Boost.MultiArray defines an array class, multi_array, and two adapter classes, multi_array_ref and const_multi_array_ref. The three classes model MultiArray and so they share a lot of functionality. multi_array_ref differs from multi_array in that the multi_array manages its own memory, while multi_array_ref is passed a block of memory that it expects to be externally managed. const_multi_array_ref differs from multi_array_ref in that the underlying elements it adapts cannot be modified through its interface, though some array properties, including the array shape and index bases, can be altered. Functionality the classes have in common is described below.

Note: Preconditions, Effects, and Implementation. Throughout the following sections, small pieces of C++ code are used to specify constraints such as preconditions, effects, and postconditions. These do not necessarily describe the underlying implementation of array components; rather, they describe the expected input to and behavior of the specified operations. Failure to meet preconditions results in undefined behavior. Not all effects (i.e. copy constructors, etc.) must be mimicked exactly. The code snippets for effects intend to capture the essence of the described operation.

Queries.

element* data();
const element* data() const;

This returns a pointer to the beginning of the contiguous block that contains the array's data. If all dimensions of the array are 0-indexed and stored in ascending order, this is equivalent to origin(). Note that const_multi_array_ref only provides the const version of this function.

element* origin();
const element* origin() const;

This returns the origin element of the multi_array. Note that const_multi_array_ref only provides the const version of this function. (Required by MultiArray)

c onst index* index_bases();

This returns the index bases for the multi_array. (Required by MultiArray)

const index* strides();

This returns the strides for the multi_array. (Required by MultiArray)

const size_type* shape();

This returns the shape of the multi_array. (Required by MultiArray)

Comparators.

bool operator==(const *array-type*& rhs);
bool operator!=(const *array-type*& rhs);
bool operator<(const *array-type*& rhs);
bool operator>(const *array-type*& rhs);
bool operator>=(const *array-type*& rhs);
bool operator<=(const *array-type*& rhs);

Each comparator executes a lexicographical compare over the value types of the two arrays. (Required by MultiArray)

Preconditions. element must support the comparator corresponding to that called on multi_array.

Complexity. O(num_elements()).

Modifiers.

template <typename SizeList>
void reshape(const SizeList& sizes)

This changes the shape of the multi_array. The number of elements and the index bases remain the same, but the number of values at each level of the nested container hierarchy may change.

SizeList Requirements. SizeList must model Collection.

Preconditions.

std::accumulate(sizes.begin(),sizes.end(),size_type(1),std::times<size_type>()) == this->num_elements();
sizes.size() == NumDims;

Postconditions. std::equal(sizes.begin(),sizes.end(),this->shape) == true;

template <typename BaseList>
void reindex(const BaseList& values);

This changes the index bases of the multi_array to correspond to the the values in values.

BaseList Requirements. BaseList must model Collection.

Preconditions. values.size() == NumDims;

Postconditions. std::equal(values.begin(),values.end(),this->index_bases());

void reindex(index value);

This changes the index bases of all dimensions of the multi_array to value.

Postconditions.

std::count_if(this->index_bases(),this->index_bases()+this->num_dimensions(),
              std::bind_2nd(std::equal_to<index>(),value)) ==
              this->num_dimensions();

multi_array

multi_array is a multi-dimensional container that supports random access iteration. Its number of dimensions is fixed at compile time, but its shape and the number of elements it contains are specified during its construction. The number of elements will remain fixed for the duration of a multi_array's lifetime, but the shape of the container can be changed. A multi_array manages its data elements using a replaceable allocator.

Model Of. MultiArray, CopyConstructible. Depending on the element type, it may also model EqualityComparable and LessThanComparable.

Synopsis.

namespace boost {

template <typename ValueType,
          std::size_t NumDims,
          typename Allocator = std::allocator<ValueType> >
class multi_array {
public:
// types:
  typedef ValueType                             element;
  typedef *unspecified*                         value_type;
  typedef *unspecified*                         reference;
  typedef *unspecified*                         const_reference;
  typedef *unspecified*                         difference_type;
  typedef *unspecified*                         iterator;
  typedef *unspecified*                         const_iterator;
  typedef *unspecified*                         reverse_iterator;
  typedef *unspecified*                         const_reverse_iterator;
  typedef multi_array_types::size_type          size_type;
  typedef multi_array_types::index              index;
  typedef multi_array_types::index_gen          index_gen;
  typedef multi_array_types::index_range        index_range;
  typedef multi_array_types::extent_gen         extent_gen;
  typedef multi_array_types::extent_range       extent_range;
  typedef *unspecified*                         storage_order_type;

  // template typedefs
  template <std::size_t Dims> struct            subarray;
  template <std::size_t Dims> struct            const_subarray;
  template <std::size_t Dims> struct            array_view;
  template <std::size_t Dims> struct            const_array_view;

  // constructors and destructors

  multi_array();

  template <typename ExtentList>
  explicit multi_array(const ExtentList& sizes,
                       const storage_order_type& store = c_storage_order(),
                       const Allocator& alloc = Allocator());
  explicit multi_array(const extents_tuple& ranges,
                       const storage_order_type& store = c_storage_order(),
                   const Allocator& alloc = Allocator());
  multi_array(const multi_array& x);
  multi_array(const const_multi_array_ref<ValueType,NumDims>& x);
  multi_array(const const_subarray<NumDims>::type& x);
  multi_array(const const_array_view<NumDims>::type& x);

  multi_array(const multi_array_ref<ValueType,NumDims>& x);
  multi_array(const subarray<NumDims>::type& x);
  multi_array(const array_view<NumDims>::type& x);

  ~multi_array();

  // modifiers

  multi_array& operator=(const multi_array& x);
  template <class Array> multi_array& operator=(const Array& x);

  // iterators:
  iterator				begin();
  iterator				end();
  const_iterator			begin() const;
  const_iterator			end() const;
  reverse_iterator			rbegin();
  reverse_iterator			rend();
  const_reverse_iterator		rbegin() const;
  const_reverse_iterator		rend() const;

  // capacity:
  size_type				size() const;
  size_type				num_elements() const;
  size_type				num_dimensions() const;

  // element access:
  template <typename IndexList>
    element&			operator()(const IndexList& indices);
  template <typename IndexList>
    const element&		operator()(const IndexList& indices) const;
  reference			operator[](index i);
  const_reference		operator[](index i) const;
  array_view<Dims>::type	operator[](const indices_tuple& r);
  const_array_view<Dims>::type	operator[](const indices_tuple& r) const;

  // queries
  element*			data();
  const element*		data() const;
  element*			origin();
  const element*		origin() const;
  const size_type*		shape() const;
  const index*			strides() const;
  const index*			index_bases() const;
  const storage_order_type&     storage_order() const;

  // comparators
  bool operator==(const multi_array& rhs);
  bool operator!=(const multi_array& rhs);
  bool operator<(const multi_array& rhs);
  bool operator>(const multi_array& rhs);
  bool operator>=(const multi_array& rhs);
  bool operator<=(const multi_array& rhs);

  // modifiers:
  template <typename InputIterator>
    void			assign(InputIterator begin, InputIterator end);
  template <typename SizeList>
    void			reshape(const SizeList& sizes)
  template <typename BaseList>	void reindex(const BaseList& values);
    void			reindex(index value);
  template <typename ExtentList>
    multi_array&		resize(const ExtentList& extents);
  multi_array&                  resize(extents_tuple& extents);
};

Constructors.

template <typename ExtentList>
explicit multi_array(const ExtentList& sizes,
                     const storage_order_type& store = c_storage_order(),
                     const Allocator& alloc = Allocator());

This constructs a multi_array using the specified parameters. sizes specifies the shape of the constructed multi_array. store specifies the storage order or layout in memory of the array dimensions. alloc is used to allocate the contained elements.

ExtentList Requirements. ExtentList must model Collection.

Preconditions. sizes.size() == NumDims;

explicit multi_array(extent_gen::gen_type<NumDims>::type ranges,
                     const storage_order_type& store = c_storage_order(),
                     const Allocator& alloc = Allocator());

This constructs a multi_array using the specified parameters. ranges specifies the shape and index bases of the constructed multi_array. It is the result of NumDims chained calls to extent_gen::operator[]. store specifies the storage order or layout in memory of the array dimensions. alloc is the allocator used to allocate the memory used to store multi_array elements.

multi_array(const multi_array& x);
multi_array(const const_multi_array_ref<ValueType,NumDims>& x);
multi_array(const const_subarray<NumDims>::type& x);
multi_array(const const_array_view<NumDims>::type& x);
multi_array(const multi_array_ref<ValueType,NumDims>& x);
multi_array(const subarray<NumDims>::type& x);
multi_array(const array_view<NumDims>::type& x);

These constructors all constructs a multi_array and perform a deep copy of x.

Complexity. This performs O(x.num_elements()) calls to element's copy constructor.

multi_array();

This constructs a multi_array whose shape is (0,...,0) and contains no elements.

Note on Constructors. The multi_array construction expressions,

     multi_array<int,3> A(boost::extents[5][4][3]);

and

     boost::array<multi_array_base::index,3> my_extents = {{5, 4, 3}};
     multi_array<int,3> A(my_extents);

are equivalent.

Modifiers.

multi_array& operator=(const multi_array& x);
template <class Array> multi_array& operator=(const Array& x);

This performs an element-wise copy of x into the current multi_array.

Array Requirements. Array must model MultiArray.

Preconditions.

std::equal(this->shape(),this->shape()+this->num_dimensions(),
x.shape());

Postconditions.

(*.this) == x;

Complexity. The assignment operators perform O(x.num_elements()) calls to element's copy constructor.

template <typename InputIterator>
void assign(InputIterator begin, InputIterator end);

This copies the elements in the range [begin,end) into the array. It is equivalent to std::copy(begin,end,this->data()).

Preconditions. std::distance(begin,end) == this->num_elements();

Complexity. The assign member function performs O(this->num_elements()) calls to ValueType's copy constructor.

multi_array& resize(extent_gen::gen_type<NumDims>::type extents);
template <typename ExtentList>
  multi_array& resize(const ExtentList& extents);

This function resizes an array to the shape specified by extents, which is either a generated list of extents or a model of the Collection concept. The contents of the array are preserved whenever possible; if the new array size is smaller, then some data will be lost. Any new elements created by resizing the array are initialized with the element default constructor.

Queries.

storage_order_type& storage_order() const;

This query returns the storage order object associated with the multi_array in question. It can be used to construct a new array with the same storage order.

multi_array_ref

multi_array_ref is a multi-dimensional container adaptor. It provides the MultiArray interface over any contiguous block of elements. multi_array_ref exports the same interface as multi_array, with the exception of the constructors.

Model Of. multi_array_ref models MultiArray, CopyConstructible. and depending on the element type, it may also model EqualityComparable and LessThanComparable. Detailed descriptions are provided here only for operations that are not described in the multi_array reference.

Synopsis.

namespace boost {

template <typename ValueType,
          std::size_t NumDims,
          typename Allocator = std::allocator<ValueType> >
class multi_array_ref {
public:
// types:
  typedef ValueType                             element;
  typedef *unspecified*                         value_type;
  typedef *unspecified*                         reference;
  typedef *unspecified*                         const_reference;
  typedef *unspecified*                         difference_type;
  typedef *unspecified*                         iterator;
  typedef *unspecified*                         const_iterator;
  typedef *unspecified*                         reverse_iterator;
  typedef *unspecified*                         const_reverse_iterator;
  typedef multi_array_types::size_type          size_type;
  typedef multi_array_types::index              index;
  typedef multi_array_types::index_gen          index_gen;
  typedef multi_array_types::index_range        index_range;
  typedef multi_array_types::extent_gen         extent_gen;
  typedef multi_array_types::extent_range       extent_range;
  typedef *unspecified*                         storage_order_type;

  // template typedefs
  template <std::size_t Dims> struct            subarray;
  template <std::size_t Dims> struct            const_subarray;
  template <std::size_t Dims> struct            array_view;
  template <std::size_t Dims> struct            const_array_view;

  // structors

  template <typename ExtentList>
  explicit multi_array_ref(element* data, const ExtentList& sizes,
                       const storage_order_type& store = c_storage_order());
  explicit multi_array_ref(element* data, const extents_tuple& ranges,
                       const storage_order_type& store = c_storage_order());
  multi_array_ref(const multi_array_ref& x);
  ~multi_array_ref();

  // modifiers

  multi_array_ref& operator=(const multi_array_ref& x);
  template <class Array> multi_array_ref& operator=(const Array& x);

  // iterators:
  iterator				begin();
  iterator				end();
  const_iterator			begin() const;
  const_iterator			end() const;
  reverse_iterator			rbegin();
  reverse_iterator			rend();
  const_reverse_iterator		rbegin() const;
  const_reverse_iterator		rend() const;

  // capacity:
  size_type				size() const;
  size_type				num_elements() const;
  size_type				num_dimensions() const;

  // element access:
  template <typename IndexList>
    element&			operator()(const IndexList& indices);
  template <typename IndexList>
    const element&		operator()(const IndexList& indices) const;
  reference			operator[](index i);
  const_reference		operator[](index i) const;
  array_view<Dims>::type	operator[](const indices_tuple& r);
  const_array_view<Dims>::type	operator[](const indices_tuple& r) const;

  // queries
  element*			data();
  const element*		data() const;
  element*			origin();
  const element*		origin() const;
  const size_type*		shape() const;
  const index*			strides() const;
  const index*			index_bases() const;
  const storage_order_type&     storage_order() const;

  // comparators
  bool operator==(const multi_array_ref& rhs);
  bool operator!=(const multi_array_ref& rhs);
  bool operator<(const multi_array_ref& rhs);
  bool operator>(const multi_array_ref& rhs);
  bool operator>=(const multi_array_ref& rhs);
  bool operator<=(const multi_array_ref& rhs);

  // modifiers:
  template <typename InputIterator>
    void			assign(InputIterator begin, InputIterator end);
  template <typename SizeList>
    void			reshape(const SizeList& sizes)
  template <typename BaseList>	void reindex(const BaseList& values);
  void				reindex(index value);
};

Constructors.

template <typename ExtentList>
explicit multi_array_ref(element* data,
                     const ExtentList& sizes,
                     const storage_order& store = c_storage_order(),
                     const Allocator& alloc = Allocator());

This constructs a multi_array_ref using the specified parameters. sizes specifies the shape of the constructed multi_array_ref. store specifies the storage order or layout in memory of the array dimensions. alloc is used to allocate the contained elements.

ExtentList Requirements. ExtentList must model Collection.

Preconditions. sizes.size() == NumDims;

explicit multi_array_ref(element* data,
                     extent_gen::gen_type<NumDims>::type ranges,
                     const storage_order& store = c_storage_order());

This constructs a multi_array_ref using the specified parameters. ranges specifies the shape and index bases of the constructed multi_array_ref. It is the result of NumDims chained calls to extent_gen::operator[]. store specifies the storage order or layout in memory of the array dimensions.

multi_array_ref(const multi_array_ref& x);

This constructs a shallow copy of x.

Complexity. Constant time (for contrast, compare this to the multi_array class copy constructor.

Modifiers.

multi_array_ref& operator=(const multi_array_ref& x);
template <class Array> multi_array_ref& operator=(const Array& x);

This performs an element-wise copy of x into the current multi_array_ref.

Array Requirements. Array must model MultiArray.

Preconditions.

std::equal(this->shape(),this->shape()+this->num_dimensions(),
x.shape());

Postconditions.

(*.this) == x;

Complexity. The assignment operators perform O(x.num_elements()) calls to element's copy constructor.

const_multi_array_ref

const_multi_array_ref is a multi-dimensional container adaptor. It provides the MultiArray interface over any contiguous block of elements. const_multi_array_ref exports the same interface as multi_array, with the exception of the constructors.

Model Of. const_multi_array_ref models MultiArray, CopyConstructible. and depending on the element type, it may also model EqualityComparable and LessThanComparable. Detailed descriptions are provided here only for operations that are not described in the multi_array reference.

Synopsis.

namespace boost {

template <typename ValueType,
          std::size_t NumDims,
          typename Allocator = std::allocator<ValueType> >
class const_multi_array_ref {
public:
// types:
  typedef ValueType                             element;
  typedef *unspecified*                         value_type;
  typedef *unspecified*                         reference;
  typedef *unspecified*                         const_reference;
  typedef *unspecified*                         difference_type;
  typedef *unspecified*                         iterator;
  typedef *unspecified*                         const_iterator;
  typedef *unspecified*                         reverse_iterator;
  typedef *unspecified*                         const_reverse_iterator;
  typedef multi_array_types::size_type          size_type;
  typedef multi_array_types::index              index;
  typedef multi_array_types::index_gen          index_gen;
  typedef multi_array_types::index_range        index_range;
  typedef multi_array_types::extent_gen         extent_gen;
  typedef multi_array_types::extent_range       extent_range;
  typedef *unspecified*                         storage_order_type;

  // template typedefs
  template <std::size_t Dims> struct            subarray;
  template <std::size_t Dims> struct            const_subarray;
  template <std::size_t Dims> struct            array_view;
  template <std::size_t Dims> struct            const_array_view;

  // structors

  template <typename ExtentList>
  explicit const_multi_array_ref(const element* data, const ExtentList& sizes,
                       const storage_order_type& store = c_storage_order());
  explicit const_multi_array_ref(const element* data, const extents_tuple& ranges,
                       const storage_order_type& store = c_storage_order());
  const_multi_array_ref(const const_multi_array_ref& x);
  ~const_multi_array_ref();

  // iterators:
  const_iterator			begin() const;
  const_iterator			end() const;
  const_reverse_iterator		rbegin() const;
  const_reverse_iterator		rend() const;

  // capacity:
  size_type				size() const;
  size_type				num_elements() const;
  size_type				num_dimensions() const;

  // element access:
  template <typename IndexList>
    const element&		operator()(const IndexList& indices) const;
  const_reference		operator[](index i) const;
  const_array_view<Dims>::type	operator[](const indices_tuple& r) const;

  // queries
  const element*		data() const;
  const element*		origin() const;
  const size_type*		shape() const;
  const index*			strides() const;
  const index*			index_bases() const;
  const storage_order_type&     storage_order() const;

  // comparators
  bool operator==(const const_multi_array_ref& rhs);
  bool operator!=(const const_multi_array_ref& rhs);
  bool operator<(const const_multi_array_ref& rhs);
  bool operator>(const const_multi_array_ref& rhs);
  bool operator>=(const const_multi_array_ref& rhs);
  bool operator<=(const const_multi_array_ref& rhs);

  // modifiers:
  template <typename SizeList>
  void			reshape(const SizeList& sizes)
  template <typename BaseList>	void reindex(const BaseList& values);
  void				reindex(index value);
};

Constructors.

template <typename ExtentList>
explicit const_multi_array_ref(const element* data,
                     const ExtentList& sizes,
                     const storage_order& store = c_storage_order());

This constructs a const_multi_array_ref using the specified parameters. sizes specifies the shape of the constructed const_multi_array_ref. store specifies the storage order or layout in memory of the array dimensions.

ExtentList Requirements. ExtentList must model Collection.

Preconditions. sizes.size() == NumDims;

explicit const_multi_array_ref(const element* data,
                     extent_gen::gen_type<NumDims>::type ranges,
                     const storage_order& store = c_storage_order());

Effects. This constructs a const_multi_array_ref using the specified parameters. ranges specifies the shape and index bases of the constructed const_multi_array_ref. It is the result of NumDims chained calls to extent_gen::operator[]. store specifies the storage order or layout in memory of the array dimensions.

const_multi_array_ref(const const_multi_array_ref& x);

Effects. This constructs a shallow copy of x.

Auxiliary Components

multi_array_types

namespace multi_array_types {
  typedef *unspecified* index;
  typedef *unspecified* size_type;
  typedef *unspecified* difference_type;
  typedef *unspecified* index_range;
  typedef *unspecified* extent_range;
  typedef *unspecified* index_gen;
  typedef *unspecified* extent_gen;
}

Namespace multi_array_types defines types associated with multi_array, multi_array_ref, and const_multi_array_ref that are not dependent upon template parameters. These types find common use with all Boost.Multiarray components. They are defined in a namespace from which they can be accessed conveniently. With the exception of extent_gen and extent_range, these types fulfill the roles of the same name required by MultiArray and are described in its concept definition. extent_gen and extent_range are described below.

extent_range

extent_range objects define half open intervals. They provide shape and index base information to multi_array, multi_array_ref, and const_multi_array_ref constructors. extent_ranges are passed in aggregate to an array constructor (see extent_gen for more details).

Synopsis.

class extent_range {
public:
  typedef multi_array_types::index      index;
  typedef multi_array_types::size_type  size_type;

  // Structors
  extent_range(index start, index finish);
  extent_range(index finish);
  ~extent_range();

  // Queries
  index start();
  index finish();
  size_type size();
};

Model Of. DefaultConstructible,CopyConstructible

Methods and Types.

extent_range(index start, index finish)
This constructor defines the half open interval [start,finish). The expression finish must be greater than start.
extent_range(index finish)
This constructor defines the half open interval [0,finish). The value of finish must be positive.
index start()

This function returns the first index represented by the range

index finish()

This function returns the upper boundary value of the half-open interval. Note that the range does not include this value.

size_type size()
This function returns the size of the specified range. It is equivalent to finish()-start().

extent_gen

The extent_gen class defines an interface for aggregating array shape and indexing information to be passed to a multi_array, multi_array_ref, or const_multi_array_ref constructor. Its interface mimics the syntax used to declare built-in array types in C++. For example, while a 3-dimensional array of int values in C++ would be declared as:
int A[3][4][5],
a similar multi_array would be declared:
multi_array<int,3> A(extents[3][4][5]).

Synopsis.

template <std::size_t NumRanges>
class *implementation_defined* {
public:
  typedef multi_array_types::index index;
  typedef multi_array_types::size_type size_type;

  template <std::size_t NumRanges> class gen_type;

  gen_type<NumRanges+1>::type  operator[](const range& a_range) const;
  gen_type<NumRanges+1>::type  operator[](index idx) const;
};

typedef *implementation_defined*<0> extent_gen;

Methods and Types.

template gen_type::<Ranges>::type
This type generator is used to specify the result of Ranges chained calls to extent_gen::operator[]. The types extent_gen and gen_type<0>::type are the same.
gen_type<NumRanges+1>::type operator[](const extent_range& a_range) const;
This function returns a new object containing all previous extent_range objects in addition to a_range. extent_range objects are aggregated by chained calls to operator[].
gen_type<NumRanges+1>::type operator[](index idx) const;
This function returns a new object containing all previous extent_range objects in addition to extent_range(0,idx). This function gives the array constructors a similar syntax to traditional C multidimensional array declaration.

Global Objects

For syntactic convenience, Boost.MultiArray defines two global objects as part of its interface. These objects play the role of object generators; expressions involving them create other objects of interest.

Under some circumstances, the two global objects may be considered excessive overhead. Their construction can be prevented by defining the preprocessor symbol BOOST_MULTI_ARRAY_NO_GENERATORS before including boost/multi_array.hpp.

extents

namespace boost {
  multi_array_base::extent_gen extents;
}
Boost.MultiArray's array classes use the extents global object to specify
array shape during their construction.
For example,
a 3 by 3 by 3 multi_array is constructed as follows:
multi_array<int,3> A(extents[3][3][3]);
The same array could also be created by explicitly declaring an extent_gen object locally,, but the global object makes this declaration unnecessary.

indices

namespace boost {
  multi_array_base::index_gen  indices;
}
The MultiArray concept specifies an index_gen associated type that is used to create views. indices is a global object that serves the role of index_gen for all array components provided by this library and their associated subarrays and views.

For example, using the indices object, a view of an array A is constructed as follows:

A[indices[index_range(0,5)][2][index_range(2,4)]];

View and SubArray Generators

Boost.MultiArray provides traits classes, subarray_gen, const_subarray_gen, array_view_gen, and const_array_view_gen, for naming of array associated types within function templates. In general this is no more convenient to use than the nested type generators, but the library author found that some C++ compilers do not properly handle templates nested within function template parameter types. These generators constitute a workaround for this deficit. The following code snippet illustrates the correspondence between the array_view_gen traits class and the array_view template <typename Array> void my_function() { typedef typename Array::template array_view<3>::type view1_t; typedef typename boost::array_view_gen<Array,3>::type view2_t; // ... }

In the above example view1_t and view2_t have the same type. .

Memory Layout pecifiers

While a multidimensional array represents a hierarchy of containers of elements, at some point the elements must be laid out in memory. As a result, a single multidimensional array can be represented in memory more than one way.

Here is how the above array is expressed in C++:

int a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

This is an example of row-major storage, where elements of each row are stored contiguously. While C++ transparently handles accessing elements of an array, it is also possible to manage the array and its indexing manually. One way that this may be expressed in memory is as follows:

int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
int s[] = { 4, 1 };

With the latter declaration of a and strides s, element a(i,j) of the array can be accessed using the expression

*a+i*s[0]+j*s[1]

The same two dimensional array could be laid out by column as follows:
int a[] = { 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11 };
int s[] = { 3, 1 };

Notice that the strides here are different. As a result, The expression given above to access values will work with this pair of data and strides as well.

int data[] = { 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3 };
int *a = data + 8;
int s[] = { -4, 1 };

Note that in this example a must be explicitly set to the origin. In the previous examples, the first element stored in memory was the origin; here this is no longer the case.

int data[] = { 3, 2, 1, 0,  7, 6, 5, 4, 11, 10, 9, 8 };
int *a = data + 3;
int s[] = { 4, -1 };

Finally, both dimensions could be stored in descending order:

int data[] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
int *a = data + 11;
int s[] = { -4, -1 };

a(i,j) will yield the same value regardless of the memory layout. Boost.MultiArray arrays can be created with customized storage parameters as described above. Thus, existing data can be adapted (with multi_array_ref or const_multi_array_ref

c_storage_order

class c_storage_order {
  c_storage_order();
};

c_storage_order is used to specify that an
array should store its elements using the same layout as that used byy
primitive C++ multidimensional arrays, that is, from last dimension
to first. This is the default storage order for the arrays provided bythis library.

fortran_storage_order

class fortran_storage_order {
  fortran_storage_order();
};

fortran_storage_order is used to specify that an array should store its elements using the same memory layout as a Fortran multidimensional array would, that is, from first dimension to last..

general_storage_order

template <std::size_t NumDims>
class general_storage_order {

  template <typename OrderingIter, typename AscendingIter>
  general_storage_order(OrderingIter ordering, AscendingIter ascending);};

general_storage_order allows the user to specify an arbitrary memory layout for the contents of an array. The constructed object is passed to the array constructor in order to specify storage order.

OrderingIter and AscendingIter must model the InputIteratorconcept. Both iterators must refer to a range of NumDims elements. AscendingIter points to objects convertible to bool. A value of true means that a dimension is stored in ascending order while false means that a dimension is stored in descending order. OrderingIter specifies the order in which dimensions are stored.

Range Checking

By default, the array access methods operator() and operator[] perform range checking. If a supplied index is out of the range defined for an array, an assertion will abort the program. To disable range checking (for performance reasons in production releases), define the BOOST_DISABLE_ASSERTS preprocessor macro prior to including multi_array.hpp in an application

Ronaldd Garcia

Indiana University
Open Systems Lab

Copyright © 2002 The Trustees of Indiana University 		  Top


Boost.MultiArray: Test Descriptions

The following is a description of the test cases that are included with Boost.Multi_Array (B.M).

Terminology

The following list is to clarify the use of terms in the following descriptions:
  • B.M - abbreviation for Boost.MultiArray.
  • primary components - refers to
 boost::multi_array,

boost::const_multi_array_ref,
and boost::multi_array_ref.
  • array types -
refers to all the primary components, plus for each
primary component "array", the types array::const_subarray,
array::subarray, array::const_array_view, and
array::array_view

Acceptance Tests

The following tests exercise various features of Boost.Multi_Array to ensure proper operation at run time.
Program | Description
libs/multi_array/test/constructors.cpp | Exercises all of the constructors for B.M primary components.
libs/multi_array/test/access.cpp | Tests operator[] and operator() on all B.M array types.
libs/multi_array/test/compare.cpp | Tests all comparison operators for the B.M primary components.
libs/multi_array/test/iterators.cpp | Test all iterator traversal and access functionality for all B.M array types.
libs/multi_array/test/slice.cpp | Test all variations of subview generation for all B.M array types.
libs/multi_array/test/assign.cpp | Tests out operator=() on the various B.M array types.
libs/multi_array/test/assign_to_array.cpp | Ensure that a multi_array can be constructed from any other array type.
libs/multi_array/test/index_bases.cpp | Test re-indexing functionality for the B.M primary components.
libs/multi_array/test/storage_order.cpp | Test variations on storage_order for the B.M primary components.
libs/multi_array/test/reshape.cpp | Test re-shaping functionality for the B.M primary components.
libs/multi_array/test/range1.cpp | Test the various syntaxes for specifying index ranges using array::index_range.
libs/multi_array/test/idxgen1.cpp | Test the array::index_gen objects.
libs/multi_array/test/stl_interaction.cpp | Test interaction between array types and STL containers.
libs/multi_array/test/resize.cpp | Test the multi_array class' resizing functionality.
libs/multi_array/test/concept_checks.cpp | Ensure that all the array types meet the defined Concepts.
libs/multi_array/test/generative_tests.hpp | A test harness used to simplify testing operations upon all array types. Used by slice.cpp, iterators.cpp, and access.cpp.

Compile-Fail Tests

The following tests check to make sure that various constructs not accepted by the library fail to compile. Each test checks only one fault in order to more easily ensure the cause of the compilation failure.

Program Description
libs/multi_array/test/fail_cbracket.cpp
libs/multi_array/test/fail_ref_cbracket.cpp 		operator[] on a const array must not modify elements.
libs/multi_array/test/fail_cdata.cpp
libs/multi_array/test/fail_ref_cdata.cpp 		array::data() const must return a pointer to const data.
libs/multi_array/test/fail_citerator.cpp
libs/multi_array/test/fail_ref_citerator.cpp 		const_iterator must not be convertible to iterator.
libs/multi_array/test/fail_cparen.cpp
libs/multi_array/test/fail_ref_cparen.cpp 		operator() on a const array must not modify elements.
libs/multi_array/test/fail_criterator.cpp
libs/multi_array/test/fail_ref_criterator.cpp 		const_reverse_iterator must not be convertible to reverse_iterator.
libs/multi_array/test/fail_csubarray.cpp
libs/multi_array/test/fail_ref_csubarray.cpp 		const_subarray must not be convertible to subarray.
libs/multi_array/test/fail_csubarray2.cpp
libs/multi_array/test/fail_ref_csubarray2.cpp 		array::operator[] const must not be convertible to subarray.
libs/multi_array/test/fail_csubarray3.cpp
libs/multi_array/test/fail_ref_csubarray3.cpp 		const_subarray into an array must not modify elements.
libs/multi_array/test/fail_cview.cpp
libs/multi_array/test/fail_ref_cview.cpp 		const_array_view of an array must not modify elements.
libs/multi_array/test/fail_cview2.cpp
libs/multi_array/test/fail_ref_cview2.cpp 		array::operator[] const must not be convertible to array_view.
libs/multi_array/test/fail_cview3.cpp
libs/multi_array/test/fail_ref_cview3.cpp 		const_array_view of an array must not modify elements.

Copyright © 2001
Ronald Garcia, Indiana University (garcia@cs.indiana.edu)
Jeremy Siek, Indiana University (jsiek@cs.indiana.edu)
Andrew Lumsdaine, Indiana University (lums@cs.indiana.edu) 		Top


The Boost Multidimensional Array Library (Boost.MultiArray)

The Boost Multidimensional Array Library
(Boost.MultiArray)

Synopsis

The Boost Multidimensional Array Library provides a class template for multidimensional arrays, as well as semantically equivalent adaptors for arrays of contiguous data. The classes in this library implement a common interface, formalized as a generic programming concept. The interface design is in line with the precedent set by the C++ Standard Library containers. Boost MultiArray is a more efficient and convenient way to express N-dimensional arrays than existing alternatives (especially the std::vector<std::vector<...>> formulation of N-dimensional arrays). The arrays provided by the library may be accessed using the familiar syntax of native C++ arrays. Additional features, such as resizing, reshaping, and creating views are available (and described below).

Table of Contents

  1. Introduction
  2. Short Example
  3. MultiArray Components
  4. Construction and Assignment
  5. Array View and Subarray Type Generators
  6. Specifying Array Dimensions
  7. Accessing Elements
  8. Creating Views
  9. Storage Ordering
  10. Setting the Array Base
  11. Changing an Array's Shape
  12. Resizing an Array
  13. MultiArray Concept
  14. Test Cases
  15. Related Work
  16. Credits

Introduction

The C++ standard library provides several generic containers, but it does not provide any multidimensional array types. The std::vector class template can be used to implement N-dimensional arrays, for example expressing a 2-dimensional array of double elements using the type std::vector<std::vector<double>>, but the resulting interface is unwieldy and the memory overhead can be quite high. Native C++ arrays (i.e. int arr[2][2][2];) do not immediately interoperate well with the C++ Standard Library, and they also lose information at function call boundaries (specifically the extent of the last dimension). Finally, a dynamically allocated contiguous block of elements can be treated as an array, though this method requires manual bookkeeping that is error prone and obfuscates the intent of the programmer.

The Boost MultiArray library enhances the C++ standard containers with versatile multi-dimensional array abstractions. It includes a general array class template and native array adaptors that support idiomatic array operations and interoperate with C++ Standard Library containers and algorithms. The arrays share a common interface, expressed as a generic programming in terms of which generic array algorithms can be implemented.

This document is meant to provide an introductory tutorial and user's guide for the most basic and common usage patterns of MultiArray components. The reference manual provides more complete and formal documentation of library features.

Short Example

What follows is a brief example of the use of multi_array:
#include "boost/multi_array.hpp"
#include <cassert>

int
main () {
  // Create a 3D array that is 3 x 4 x 2
  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::index index;
  array_type A(boost::extents[3][4][2]);

  // Assign values to the elements
  int values = 0;
  for(index i = 0; i != 3; ++i)
    for(index j = 0; j != 4; ++j)
      for(index k = 0; k != 2; ++k)
        A[i][j][k] = values++;

  // Verify values
  int verify = 0;
  for(index i = 0; i != 3; ++i)
    for(index j = 0; j != 4; ++j)
      for(index k = 0; k != 2; ++k)
        assert(A[i][j][k] == verify++);

  return 0;
}

MultiArray Components

Boost.MultiArray's implementation (boost/multi_array.hpp) provides three user-level class templates:
  1. multi_array,
  2. multi_array_ref, and
  3. const_multi_array_ref
multi_array is a container template. When instantiated, it allocates space for the number of elements corresponding to the dimensions specified at construction time. A multi_array may also be default constructed and resized as needed.

multi_array_ref adapts an existing array of data to provide the multi_array interface. multi_array_ref does not own the data passed to it.

const_multi_array_ref is similar to multi_array_ref but guarantees that the contents of the array are immutable. It can thus wrap pointers of type T const*.

The three components exhibit very similar behavior. Aside from constructor parameters, multi_array and multi_array_ref export the same interface. const_multi_array_ref provides only the constness-preserving portions of the multi_array_ref interface.

Construction and Assignment

Each of the array types - multi_array, multi_array_ref, and const_multi_array_ref - provides a specialized set of constructors. For further information, consult their reference pages.

All of the non-const array types in this library provide assignment operatorsoperator=(). Each of the array types multi_array, multi_array_ref, subarray, and array_view can be assigned from any of the others, so long as their shapes match. The const variants, const_multi_array_ref, const_subarray, and const_array_view, can be the source of a copy to an array with matching shape. Assignment results in a deep (element by element) copy of the data contained within an array.

Array View and Subarray Type Generators

In some situations, the use of nested generators for array_view and subarray types is inconvenient. For example, inside a function template parameterized upon array type, the extra "template" keywords can be obfuscating. More likely though, some compilers cannot handle templates nested within template parameters. For this reason the type generators, subarray_gen, const_subarray_gen, array_view_gen, and const_array_view_gen are provided. Thus, the two typedefs in the following example result in the same type:
template <typename Array>
void my_function() {
  typedef typename Array::template array_view<3>::type view1_t;
  typedef typename boost::array_view_gen<Array,3>::type view2_t;
  // ...
}

Specifying Array Dimensions

When creating most of the Boost.MultiArray components, it is necessary to specify both the number of dimensions and the extent of each (boost::multi_array also provides a default constructor). Though the number of dimensions is always specified as a template parameter, two separate mechanisms have been provided to specify the extent of each.

The first method involves passing a Collection of extents to a constructor, most commonly a boost::array. The constructor will retrieve the beginning iterator from the container and retrieve N elements, corresponding to extents for the N dimensions. This is useful for writing dimension-independent code.

Example

  typedef boost::multi_array<double, 3> array_type;
  boost::array<array_type::index, 3> shape = {{ 3, 4, 2 }};
  array_type A(shape);

The second method involves passing the constructor an extent_gen object, specifying the matrix dimensions. By default, the library constructs a global extent_gen object boost::extents. In case of concern about memory used by these objects, defining BOOST_MULTI_ARRAY_NO_GENERATORS before including the library header inhibits its construction.

Example

  typedef boost::multi_array<double, 3> array_type;
  array_type A(boost::extents[3][4][2]);

Accessing Elements

The Boost.MultiArray components provide two ways of accessing specific elements within a container. The first uses the traditional C array notation, provided by operator[].

Example

  typedef boost::multi_array<double, 3> array_type;
  array_type A(boost::extents[3][4][2]);
  A[0][0][0] = 3.14;
  assert(A[0][0][0] == 3.14);

The second method involves passing a Collection of indices to operator(). N indices will be retrieved from the Collection for the N dimensions of the container.

Example

  typedef boost::multi_array<double, 3> array_type;
  array_type A(boost::extents[3][4][2]);
  boost::array<array_type::index,3> idx = {{0,0,0}};
  A(idx) = 3.14;
  assert(A(idx) == 3.14);
This can be useful for writing dimension-independent code, and under some compilers may yield higher performance than operator[].

By default, both of the above element access methods perform range checking. If a supplied index is out of the range defined for an array, an assertion will abort the program. To disable range checking (for performance reasons in production releases), define the BOOST_DISABLE_ASSERTS preprocessor macro prior to including multi_array.hpp in the application.

Creating Views

Boost.MultiArray provides the facilities for creating a sub-view of an already existing array component. It allows to create a sub-view that retains the same number of dimensions as the original array or one that has less dimensions than the original as well.

Sub-view creation occurs by placing a call to operator[], passing it an index_gen type. The index_gen is populated by passing index_range objects to its operator[]. Similar to boost::extents, the library by default constructs the object boost::indices. The user can suppress this object by defining BOOST_MULTI_ARRAY_NO_GENERATORS before including the library header. A simple sub-view creation example follows.

Example

  // yarray = 2 x 3 x 4

  //
  // array_view dims: [base,bound) (dimension striding default = 1)
  // dim 0: [0,2)
  // dim 1: [1,3)
  // dim 2: [0,4) (strided by 2),
  //

  typedef array_type::index_range range;
  array_type::array_view<3>::type myview =
    myarray[ boost::indices[range(0,2)][range(1,3)][range(0,4,2)] ];

  for (array_type::index i = 0; i != 2; ++i)
    for (array_type::index j = 0; j != 2; ++j)
      for (array_type::index k = 0; k != 2; ++k)
        assert(myview[i][j][k] == myarray[i][j+1][k*2]);

By passing an integral value to the index_gen, one may create a subview with fewer dimensions than the original array component (also called slicing).

Example

  // myarray = 2 x 3 x 4

  //
  // array_view dims:
  // [base,stride,bound)
  // [0,1,2), 1, [0,2,4)
  //

  typedef array_type::index_range range;
  array_type::index_gen indices;
  array_type::array_view<2>::type myview =
    myarray[ indices[range(0,2)][1][range(0,4,2)] ];

  for (array_type::index i = 0; i != 2; ++i)
    for (array_type::index j = 0; j != 2; ++j)
    assert(myview[i][j] == myarray[i][1][j*2]);

More on index_range

The index_range type provides several methods of specifying ranges for subview generation. Here are a few range instantiations that specify the same range.

Example

  // [[base,stride,bound)
  // [0,2,4)

  typedef array_type::index_range range;
  range a_range;
  a_range = range(0,4,2);
  a_range = range().start(0).finish(4).stride(2);
  a_range = range().start(0).stride(2).finish(4);
  a_range = 0 <= range().stride(2) < 4;
  a_range = 0 <= range().stride(2) <= 3;
An index_range object passed to a slicing operation will inherit its start and/or finish value from the array being sliced if the user do not supply one. This conveniently prevents from having to know the bounds of the array dimension in certain cases. For example, the default-constructed range will take the full extent of the dimension it is used to specify.

Example

  typedef array_type::index_range range;
  range a_range;

  // All elements in this dimension
  a_range = range();

  // indices i where 3 <= i
  a_range = range().start(3)
  a_range = 3 <= range();
  a_range = 2 < range();

  // indices i where i < 7
  a_range = range().finish(7)
  a_range = range() < 7;
  a_range = range() <= 6;
The following example slicing operations exhibit some of the alternatives shown above
    // take all of dimension 11
    // take i < 5 for dimension 2
    // take 4 <= j <= 7 for dimension 3 with stride 2
    myarray[ boost::indices[range()][range() < 5 ][4 <= range().stride(2) <= 7] ];

Storage Ordering

Each array class provides constructors that accept a storage ordering parameter. This is most useful when interfacing with legacy codes that require an ordering different from standard C, such as FORTRAN. The possibilities are c_storage_order, fortran_storage_order, and general_storage_order.

c_storage_order, which is the default, will store elements in memory in the same order as a C array would, that is, the dimensions are stored from last to first.

fortran_storage_order will store elements in memory in the same order as FORTRAN would: from the first dimension to the last. Note that with use of this parameter, the array indices will remain zero-based.

Example

  typedef boost::multi_array<double,3> array_type;
  array_type A(boost::extents[3][4][2],boost::fortran_storage_order);
  call_fortran_function(A.data());

general_storage_order allows one to customize both the order in which dimensions are stored in memory and whether dimensions are stored in ascending or descending order.

Example

  typedef boost::general_storage_order<3> storage;
  typedef boost::multi_array<int,3> array_type;

  // Store last dimension, then first, then middle
  array_type::size_type ordering[] = {2,0,1};

  // Store the first dimension(dimension 0) in descending order
  bool ascending[] = {false,true,true};

   array_type A(extents[3][4][2],storage(ordering,ascending));

Setting The Array Base

In some situations, it may be inconvenient or awkward to use an array that is zero-based. the Boost.MultiArray components provide two facilities for changing the bases of an array. One may specify a pair of range values to the extent_gen constructor in order to set the base value.

Example

  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::extent_range range;

  array_type::extent_gen extents;

  // dimension 0: 0-based
  // dimension 1: 1-based
  // dimension 2: -1 - based
  array_type A(extents[2][range(1,4)][range(-1,3)]);

An alternative is to first construct the array normally then reset the bases. To set all bases to the same value, use the reindex member function, passing it a single new index value.

Example

  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::extent_range range;

  array_type::extent_gen extents;

  array_type A(extents[2][3][4]);
  // change to 1-based
  A.reindex(1)

An alternative is to set each base separately using the reindex member function, passing it a Collection of index bases.

Example

  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::extent_range range;

  array_type::extent_gen extents;

  // dimension 0: 0-based
  // dimension 1: 1-based
  // dimension 2: (-1)-based
  array_type A(extents[2][3][4]);
  boost::array<array_type::index,ndims> bases = {{0, 1, -1}};
  A.reindex(bases);

Changing an Array's Shape

The Boost.MultiArray arrays provide a reshape operation. While the number of dimensions must remain the same, the shape of the array may change so long as the total number of elements contained remains the same.

Example

  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::extent_range range;

  array_type::extent_gen extents;
  array_type A(extents[2][3][4]);
  boost::array<array_type::index,ndims> dims = {{4, 3, 2}};
  A.reshape(dims);

Note that reshaping an array does not affect the indexing.

Resizing an Array

The boost::multi_array class provides an element-preserving resize operation. The number of dimensions must remain the same, but the extent of each dimension may be increased and decreased as desired. When an array is made strictly larger, the existing elements will be preserved by copying them into the new underlying memory and subsequently destructing the elements in the old underlying memory. Any new elements in the array are default constructed. However, if the new array size shrinks some of the dimensions, some elements will no longer be available.

Example

  typedef boost::multi_array<int, 3> array_type;
  typedef array_type::extent_range range;

  array_type::extent_gen extents;
  array_type A(extents[3][3][3]);
  A[0][0][0] = 4;
  A[2][2][2] = 5;
  A.resize(extents[2][3][4]);
  assert(A[0][0][0] == 4);
  // A[2][2][2] is no longer valid.

MultiArray Concept

Boost.MultiArray defines and uses the MultiArray concept. It specifies an interface for N-dimensional containers.

Test Cases

Boost.MultiArray comes with a suite of test cases meant to exercise the features and semantics of the library. A description of the test cases can be found here.

Related Work

boost::array and std::vector are one-dimensional containers of user data. Both manage their own memory. std::valarray is a low-level C++ Standard Library component meant to provide portable high performance for numerical applications. Blitz++ is an array library developed by Todd Veldhuizen. It uses advanced C++ techniques to provide near-Fortran performance for array-based numerical applications. array_traits is a beta library, formerly distributed with Boost, that provides a means to create iterators over native C++ arrays. This library is analogous to boost::array in that it augments C style N-dimensional arrays, as boost::array does for C one-dimensional arrays.

Credits

  • Ronald Garcia is the primary author of the library.
  • Jeremy Siek helped with the library and provided a sounding board for ideas, advice, and assistance porting to Microsoft Visual C++.
  • Giovanni Bavestrelli provided an early implementation of an N-dimensional array which inspired feedback from the Boost mailing list members. Some design decisions in this work were based upon this implementation and the comments it elicited.
  • Todd Veldhuizen wrote Blitz++, which inspired some aspects of this design. In addition, he supplied feedback on the design and implementation of the library.
  • Jeremiah Willcock provided feedback on the implementation and design of the library and some suggestions for features.
  • Beman Dawes helped immensely with porting the library to Microsoft Windows compilers.
Ronald Garcia


Boost.MultiIndex Documentation - Acknowledgements

Boost.MultiIndex Acknowledgements

Fernando Cacciola, Darren Cook, Beman Dawes, Jeremy Maitin-Shepard and Daryle Walker from the Boost mailing list provided useful suggestions for improvement on the first alpha releases of the library. Gang Wang discovered several bugs in the code. Thomas Wenisch brought out the idea of "sequence sets" from which sequenced indices were designed. Giovanni Bajo, Chris Little and Maxim Yegorushkin tested the library on several platforms. Daniel Wallin contributed fixes for MSVC++ 7.0. Ron Liechty and the support staff at Metrowerks provided assistance during the porting of the library to CW 8.3. Porting to VisualAge 6.0 counted on Toon Knapen's help. Markus Schöpflin aided with Compaq C++ 6.5 and GCC for Tru64 UNIX. Rosa Bernárdez proofread the last versions of the tutorial.

Pavel Voženílek has been immensely helpful in thoroughly reviewing every single bit of the library, and he also suggested several extra functionalities, most notably range querying, safe mode, polymorphic key extractors and MPL support. Thank you!

The Boost acceptance review took place between March 20th and 30th 2004. Pavel Voženílek was the review manager. Thanks to all the people who participated and specially to those who submitted reviews: Fredrik Blomqvist, Tom Brinkman, Paul A Bristow, Darren Cook, Jeff Garland, David B. Held, Brian McNamara, Gary Powell, Rob Stewart, Arkadiy Vertleyb, Jörg Walter. Other Boost members also contributed ideas, particularly in connection with the library's naming scheme: Pavol Droba, Dave Gomboc, Jeremy Maitin-Shepard, Thorsten Ottosen, Matthew Vogt, Daryle Walker. My apologies if I inadvertently left somebody out of this list.

Boost.MultiIndex could not have been written without Aleksey Gurtovoy et al. superb Boost MPL Library. Also, Aleksey's techniques for dealing with ETI-related problems in MSVC++ 6.0 helped solve some internal issues of the library.

The internal implementation of red-black trees is based on that of SGI STL stl_tree.h file:

Copyright (c) 1996,1997 Silicon Graphics Computer Systems, Inc.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Silicon Graphics makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

Copyright (c) 1994 Hewlett-Packard Company
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Hewlett-Packard Company makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

I would like to dedicate this piece of work to Rosa Bernárdez, my very first C++ teacher, for her unconditional support in many endeavors of which programming is by no means the most important. In memory of my cat López (2001-2003): he lived too fast, died too young.

Boost 1.33 release

Many thanks again to Pavel Voženílek, who has carefully reviewed the new material and suggested many improvements. The design of hashed indices has benefited from discussions with several Boost members, most notably Howard Hinnant and Daniel James. Daniel has also contributed Boost.Hash to the community: hashed indices depend on this library as their default hash function provider. Robert Ramey's Boost Serialization Library provides the very solid framework upon which Boost.MultiIndex serialization capabilities are built. Toon Knapen helped adjust the library for VisualAge 6.0. Markus Schöpflin provided a Jamfile tweak for GCC under Tru64 UNIX.

Boost 1.34 release

Thanks go to Pavel Voženílek for his useful comments and suggestions during the development of this release, and to Rosa Bernárdez for reviewing the new material in the documentation. Alo Sarv suggested a notational improvement in the specification of partial searches with composite keys. Maxim Yegorushkin proposed a valuable spatial optimization for ordered indices and provided figures of its impact on performance for containers with large numbers of elements. Caleb Epstein performed the tests under MSVC++ 8.0 described in the performance section. The following people have reported bugs and problems with previous versions and prereleases of the library: Alexei Alexandrov, Matías Capeletto, John Eddy, Martin Eigel, Guillaume Lazzara, Felipe Magno de Almeida, Julien Pervillé, Hubert Schmid, Toby Smith.

New member in the family! Thanks to Héctor for his patience during long development sessions and his occasional contributions to the source codebase.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Compiler specifics

Boost.MultiIndex Compiler specifics

Boost.MultiIndex has been tried in different compilers, with various degrees of success. We list the limitations encountered, along with suitable workarounds when available.

Contents

  • Borland C++ Builder 6.4 and later
  • Comeau C/C++ 4.3.3 for Windows (VC++ 7.0/7.1 backend)
  • Compaq C++ 6.5-042 for Tru64 UNIX and later
  • GNU GCC 3.2 and later
    • GNU GCC for Tru64 UNIX
    • Darwin GCC 4.0
  • HP aC++ A.06.12 for HP-UX
  • IBM VisualAge C++ V6.0 for AIXaaa
  • Intel C++ Compiler for Linux 9.0 and later
  • Intel C++ Compiler for Windows 32-bit 7.0/7.1
  • Intel C++ Compiler for Windows 32-bit 8.0 and later
  • Intel C++ Compiler Extended Memory 64 Technology 9.1 for Windows
  • Metrowerks CodeWarrior 8.3
  • Metrowerks CodeWarrior 9 and later
  • Microsoft Visual C++ 6.0 Service Pack 5
    • Microsoft Visual C++ 6.0 Service Pack 5 + STLport 4.5.3 and later
  • Microsoft Visual C++ 7.0
    • Microsoft Visual C++ 7.0 + STLport 5.0.1
  • Microsoft Visual C++ 7.1
    • Microsoft Visual C++ 7.1 + STLport 4.6.2
  • Microsoft Visual C++ 8.0
    • Microsoft Visual C++ 8.0 + STLport 5.0.1
  • Microsoft Visual C++ 8.0 x64 cross-compiler
  • Sun Studio 11 for Solaris
  • Portability techniques
    • Use of member_offset
    • Use of const_mem_fun_ex plicit and mem_fun_explicit
    • composite_key in compilers without partial template specialization
    • Reduction of symbol name lengths
      • Limitation of maximum number of arguments
      • Type hiding

Borland C++ Builder 6.4 and later

Currently, Boost.MultiIndex cannot be used with any of BCB 6.4 up to BCB 2006. The number of problems encountered during the tests makes it unlikely that future versions of the library can be made to work under these compilers.

Comeau C/C++ 4.3.3 for Windows (VC++ 7.0/7.1 backend)

No problems have been detected with this compiler. The library fails to compile, however, when Microsoft Visual C++ 6.0 is used as the backend.

Compaq C++ 6.5-042 for Tru64 UNIX and later

No problems have been detected with this compiler. Versions 6.5-042, 7.1-005 and 7.1-006 have been tested.

GNU GCC 3.2 and later

No problems have been detected with several versions of this compiler starting from 3.2. The following versions have been explicitly tested:

  • GCC 3.2 20020927 (prerelease) under Cygwin 1.5.7,
  • GCC 3.2.3 under Linux,
  • GCC 3.3.5 (qnx-nto) under QNX 6.3.0 and QNX 6.3.2,
  • GCC 3.3.6 under Linux,
  • GCC 3.4.2 (mingw-special) under Win32, (mingw-special) + STLport 5.0.1 under Win32,
  • GCC 3.4.3 under Solaris,
  • GCC 3.4.4 under Linux and Tru64 (see below),
  • GCC 3.4.5 under Linux and Linux x86-64, (mingw-special) under Win32, (mingw-special) + STLport 5.0.1 under Win32, (mingw-special) + STLport 5.1.0 under Win32,
  • GCC 4.0.1 (Apple build 5250) under Mac OS,
  • GCC 4.0.3 under Linux and Tru64 (see below),
  • GCC 4.1.0 under Linux and Linux x86-64,
  • GCC 4.1.1 (OpenPKG-2-STABLE) under Solaris.
Boost.MultiIndex does not work with versions 3.1 and prior of GCC.

GNU GCC for Tru64 UNIX

On this platform, GCC is not able to handle debug symbol names whose length exceeds 32,768 bytes, resulting in the error mips-tfile, ... string too big. You may encounter this issue with heavily templatized code like Boost.MultiIndex, which typically produces long symbol names. The problem can be overcome by omitting the compiler option -g (generate debugging information.) Alternatively, consult the section on reduction of symbol name lengths for various applicable workarounds.

Darwin GCC 4.0

Build 4061 of GCC 4.0, shipped with Darwin 8.2 and prior (Mac OS X 10.4.2 and prior), corresponds to a prerelease version of GNU GCC 4.0.0 which introduces a regression bug related to binding of references to temporary objects. This bug precludes the usage of Boost.MultiIndex invariant-checking mode; other than this, Boost.MultiIndex works correctly. The bug is corrected in GCC 4.0 Apple build 5026, which is in sync with the official release of GNU GCC 4.0.0, so the invariant-checking mode is available from this upgrade.

HP aC++ A.06.12 for HP-UX

No problems have been detected with this compiler.

IBM VisualAge C++ V6.0 for AIX

Note: This information was last checked for Boost 1.33.1. There is a possibility that changes in Boost since that release have caused problems with this platform.

member not supported, refer to the section on use of member_offset for workarounds. member_offset causes the compiler to emit warnings about the use of offsetof with non-POD types: these warnings can be suppressed by setting the compiler option -qsuppress=1540-1281, or, alternatively, by inserting the following preprocessor directive:

#pragma info(nolan)

This latter pragma, however, may also eliminate other warnings not related to the use of offsetof.

Serialization capabilities are not available as Boost.Serialization is not supported on this platform.

Intel C++ Compiler for Linux 9.0 and later

No problems have been detected with this compilers from version 9.0.

Intel C++ Compiler for Windows 32-bit 7.0/7.1

member not supported, refer to the section on use of member_offset for workarounds.

When used on top of MSVC++ 7.0 or prior, argument dependent lookup is disabled by default. This will cause problems with many Boost libraries, and in particular with the serialization part of Boost.MultiIndex. Argument dependent lookup is enabled by adding /Qoption,c,--arg_dep_lookup to the project options.

Intel C++ Compiler for Windows 32-bit 8.0 and later

When used on top of MSVC++ 7.0 or prior, argument dependent lookup is disabled by default. This will cause problems with many Boost libraries, and in particular with the serialization part of Boost.MultiIndex. Argument dependent lookup is enabled by adding /Qoption,c,--arg_dep_lookup to the project options. Other than this, Boost.MultiIndex works without problems. Compiler versions from 8.0 to 9.1 have been tested.

Intel C++ Compiler Extended Memory 64 Technology 9.1 for Windows

No problems have been detected with this compiler.

Metrowerks CodeWarrior 8.3

Predefined key extractors instantiated on a given type do not accept objects of derived types. For instance:

struct base{};
struct derived:public base{};
...

identity<base> key_extractor;
derived        x;

// not accepted by this compiler: an explicit cast to base is required
key_extractor(x);

Other than this, Boost.MultiIndex works without problems under the two operating systems tested: Mac OS and Windows.

Metrowerks CodeWarrior 9 and later

Boost.MultiIndex works correctly with versions of this compiler from 9.0 to 9.5, under the two operating systems tested: Mac OS and Windows.

Microsoft Visual C++ 6.0 Service Pack 5

member not supported, refer to the section on use of member_offset for workarounds.

const_mem_fun and m em_fun not supported, refer to the section on use of const_mem_fun_explicit and  mem_fun_explicit for workarounds.

No support for index retrieval and projection nested types and member functions:

  • nth_index,
  • index,
  • nth_index_iterator,
  • nth_index_const_iterator,
  • index_iterator,
  • index_const_iterator,
  • get,
  • project.
You can use instead their global equivalents. Also, this compiler does not implement argument dependent lookup, so you might need to explicitly qualify these global names with ::boost::multi_index.

boost::multi_index::multi_index_container is imported to namespace boost by means of a using declaration. MSVC++ 6.0, however, does not properly handle this import. So, instead of writing:

boost::multi_index_container<...>

use the following:

boost::multi_index::multi_index_container<...>

or else resort to a directive using namespace boost::multi_index.

The lack of partial template specialization support in MSVC++ 6.0 results in some inconveniences when using composite_key that can be remedied as explained in "composite_key in compilers without partial template specialization".

Due to problems with function template ordering support, composite_key_compare and related classes do not accept the notational variations of operator() where one of the operands is treated as if included into a tuple of length 1. As a result, the user cannot ever omit tuple enclosing when specifying the arguments of lookup operations involving composite keys.

Predefined key extractors instantiated on a given type do not accept objects of derived types. For instance:

struct base{};
struct derived:public base{};
...

identity<base> key_extractor;
derived        x;

// not accepted by this compiler: an explicit cast to base is required
key_extractor(x);

MSVC++ 6.0 presents serious limitations for the maximum length of symbol names generated by the compiler, which might result in the linker error LNK1179: inv alid or corrupt file: duplicate comdat comdat. To overcome this problem, consult the section on reduction of symbol name lengths for various applicable workarounds.

Under some circumstances, the compiler emits the error C2587: '_U' : illegal use of local variable as default parameter, inside the MSVC internal header <xlocnum>. This problem is a recurrent bug of the compiler, and has been reported in other unrelated libraries, like the Boost Graph Library, Boost.MultiArray, Boost.Regex, CGAL and MySQL++. The error is triggered, though not in a systematic manner, by the use of multi_index_container iterator constructor. Two workarounds exist: the first consists of avoiding this constructor and replacing code like:

multi_index_container<...> s(c.begin(),c.end());

with equivalent operations:

multi_index_container<...> s;
s.insert(c.begin(),c.end());

The second workaround has not been confirmed by the author, but it is given on the Internet in connection with this error appearing in other libraries. Replace line 84 of <xlocnum>

 #define _VIRTUAL	virtual/span>

with the following:

 #define _VIRTUAL

Warning: it is not known whether this replacement can result in unexpected side effects in code implicitly using <xlocnum>.

In general, the extensive use of templates by Boost.MultiIndex puts this compiler under severe stress, so that several internal limitations may be reached. The following measures can help alleviate these problems:

  • Set the compiler option /Zm (Specify Memory Allocation Limit) to increase the amount of memory available for compilation. Usual values for this option range from 300 to 800.
  • If in debug mode, try switching from /ZI (Program Database for Edit and Continue) to a less demanding type of debugging information (/Zi, /Z7 or /Zd.)
  • Play with the precompiled headers options. Usually, turning this feature off yields the best results.
  • If the compiler emits the error C1055: compiler limit : out of keys, try disabling the option /Gm (Enable Minimal Rebuild.) In these cases, it is also beneficial to split the project into smaller subprojects.

Microsoft Visual C++ 6.0 Service Pack 5 + STLport 4.5.3 and later

Boost.MultiIndex works for this configuration. The same limitations apply as in MSVC++ 6.0 with its original Dinkumware standard library. STLport 4.6.2 and 5.0.1 has also been confirmed to work correctly.

It is not possible to use the serialization capabilities of Boost.MultiIndex along with the dynamic version of STLport, as some linking errors result. Use instead the static version of STLport. This bug is fixed in STLport 5.0.

Microsoft Visual C++ 7.0l

member not supported, refer to the section on use of member_offset for workarounds.

No support for index retrieval and projection nested types and member functions:

  • nth_index,
  • index,
  • nth_index_iterator,
  • nth_index_const_iterator,
  • index_iterator,
  • index_const_iterator,
  • get,
  • project.
You can use instead their global equivalents. Also, this compiler does not implement argument dependent lookup, so you might need to explicitly qualify these global names with ::boost::multi_index.

boost::multi_index::multi_index_container is imported to namespace boost by means of a using declaration. MSVC++ 7.0, however, does not properly handle this import. So, instead of writing:

boost::multi_index_container<...>

use the following:

boost::multi_index::multi_index_container<...>

or else resort to a directive using namespace boost::multi_index.

The lack of partial template specialization support in MSVC++ 7.0 results in some inconveniences when using composite_key that can be remedied as explained in "composite_key in compilers without partial template specialization" .

Due to problems with function template ordering support, composite_key_compare and related classes do not accept the notational variations of operator() where one of the operands is treated as if included into a tuple of length 1. As a result, the user cannot ever omit tuple enclosing when specifying the arguments of lookup operations involving composite keys.

Predefined key extractors instantiated on a given type do not accept objects of derived types. For instance:

struct base{};
struct derived:public base{};
...

identity<base> key_extractor;
derived        x;

// not accepted by this compiler: an explicit cast to base is required
key_extractor(x);

Microsoft Visual C++ 7.0 + STLport 5.0.1

Boost.MultiIndex works for this configuration. The same issues apply as in MSVC++ 7.0 with its original Dinkumware standard library.

Microsoft Visual C++ 7.1

Problems have been reported when compiling the library with the /Gm option (Enable Minimal Rebuild.) Seemingly, this is due to an internal defect of the compiler (see for instance this mention of a similar issue in the Boost Users mailing list.) If /Gm is turned off, Boost.MultiIndex compiles and runs without further problems.

Microsoft Visual C++ 7.1 + STLport 4.6.2

Boost.MultiIndex works for this configuration. The same issues apply as in MSVC++ 7.1 with its original Dinkumware standard library.

Microsoft Visual C++ 8.0

No problems have been detected with this compiler.

Microsoft Visual C++ 8.0 + STLport 5.0.1

No problems have been detected with this configuration.

Microsoft Visual C++ 8.0 x64 cross-compiler

No problems have been detected with this compiler.

Sun Studio 11 for Solaris

No problems have been detected with this platform. The exact compiler version tested was Sun C++ 5.8 Patch 121017-03 2006/07/19. The option -library=stlport4 was used to replace the default standard library with STLport.

Portability techniques

Use of member_offset

The member key extractor poses some problems in compilers that do not properly support pointers to members as non-type template arguments, as indicated by the Boost Configuration Library defect macro BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS. The following compilers have been confirmed not to work correctly with memberer

  • MSVC++ 6.0/7.0,

  • Intel C++ 7.0/7.1 for Windows,
  • VisualAge 6.0 for AIX.
    • This program can help determine if your compiler properly supports pointers to members as non-type template parameters:

    #include <iostream>

struct pair
{
  int x,y;

  pair(int x_,int y_):x(x_),y(y_){}
};

template<int pair::* PtrToPairMember>
struct foo
{
  int bar(pair& p){return p.*PtrToPairMember;}
};

int main()
{
  pair p(0,1);
  foo<&pair::x> fx;
  foo<&pair::y> fy;

  if(fx.bar(p)!=0||fy.bar(p)!=1)std::cout<<"KO"<<std::endl;
  else std::cout<<"OK"<<std::endl;

  return 0;
}

    If you find a compiler that does not pass the test, and for which BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS is not defined, please report to the Boost developers mailing list.

    To overcome this defect, a replacement utility member_offset has been provided that does the work of member at the expense of less convenient notation and the possibility of non-conformance with the standard. Please consult the reference for further information on member_offset. As an example of use, given the class

    class A
{
  int x;
}

    the instantiation member<A,int,&A::x> can be simulated then as member_offset<A,int,offsetof(A,x)>.

    For those writing portable code, Boost.MultiIndex provides the ternary macro BOOST_MULTI_INDEX_MEMBER. Continuing with the example above, the expression

    BOOST_MULTI_INDEX_MEMBER(A,int,x)

    expands by default to

    member<A,int,&A::x>

    or alternatively to

    member_offset<A,int,offsetof(A,x)>

    if BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS is defined.

    Use of f const_mem_fun_explicitmem_fun_explicit

    MSVC++ 6.0 has problems with const member functions as non-type template parameters, and thus does not accept the const_mem_fun key extractor. A simple workaround, fortunately, has been found, consisting in specifying the type of these pointers as an additional template parameter. The alternative const_mem_fun_explicit extractor adopts this solution; for instance, given the type

    struct A
{
  int f()const;
};

    the extractor const_mem_fun<A,int,&A::f> can be replaced by const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>. A similar mem_fun_explicit class template is provided for non-constant member functions.

    If you are writing cross-platform code, the selection of either key extractor is transparently handled by the macro BOOST_MULTI_INDEX_CONST_MEM_FUN, so that

    BOOST_MULTI_INDEX_CONST_MEM_FUN(A,int,f)

    expands by default to

    const_mem_fun<A,int,&A::f>

    but resolves to

    const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>

    in MSVC++ 6.0. Non-const member functions are covered by mem_fun_explicit and the macro BOOST_MULTI_INDEX_MEM_FUN

    composite_key in compilers without partial template specialization

    When usingg composite_keys, lookup is performed by passing tuples of values: this ability is achieved by suitably specializing the class templates std::equal_to, std::less, std::greater and boost::hash for composite_key_result instantiations so that they provide the appropriate overloads accepting tuples --and in the case of std::less and std::greater, also partial tuples where only the first components are specified.

    In those compilers that do not support partial template specialization, these specializations cannot be provided, and so tuple-based lookup is not available by default. In this case, multi_index_container instantiations using composite keys will work as expected, both for ordered and hashed indices, except that lookup operations will not accept tuples as an argument. For ordered indices, the most obvious workaround to this deficiency involves explicitly specifying the comparison predicate with composite_key_compare; in the case of hashed indices we can use the analogous composite_key_equal_to and composite_key_hash. This substitution is tedious as the elementary components for all the constituent key extractors must be explicitly typed. For this reason, Boost.MultiIndex provides the following replacement class templates

    • composite_key_result_equal_to,
    • composite_key_result_less,
    • composite_key_result_greater and
    • composite_key_result_hash,
    that act as the missing specializations of std::equal_to, std::less, std::greater and boost::hash for composite_key_results. They can be used as follows:

    typedef composite_key<
  phonebook_entry,
  member<phonebook_entry,std::string,&phonebook_entry::family_name>,
  member<phonebook_entry,std::string,&phonebook_entry::given_name>
> ckey_t;

typedef multi_index_container<
  phonebook_entry,
  indexed_by<
    ordered_non_unique<
      ckey_t,
      // composite_key_result_less plays the role of
      // std::less<ckey_t::result_type>
      composite_key_result_less<ckey_t::result_type>
    >,
    ordered_unique<
      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
    >
  >> phonebook;

    Reduction of symbol name lengths

    The types generated on the instantiations off multi_index_containers typically produce very long symbol names, sometimes beyond the internal limits of some compilers. There are several techniques to shorten generated symbol names: these techniques have also the beneficial side effect that resulting error messages are more readable.

    Limitation of maximum number of arguments

    The class templates indexed_by, tag and composite_key accept a variable number of arguments whose maximum number is limited by internal macros. Even non-used arguments contribute to the final types, so manually adjusting the corresponding macros can result in a modest reduction of symbol names.

    Limiting maximum number of arguments of some class templates of Boost.MultiIndex.
class template | limiting macro | default value | default value (MSVC++ 6.0)
indexed_by | BOOST_MULTI_INDEX_LIMIT_INDEXED_BY_SIZE | 20 | 5
tag | BOOST_MULTI_INDEX_LIMIT_TAG_SIZE | 20 | 3
composite_key | BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE | 10 | 5

    Type hiding

    Consider a typical instantiation of multi_index_container:

    typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >,
    ordered_unique<member<employee,int,&employee::ssnumber> >
  >
> employee_set;

    Then, for instance, the type employee_set::nth_type<0>::type resolves to the following in GCC:

    boost::multi_index::detail::ordered_index<
  boost::multi_index::identity<employee>,
  std::less<employee>,
  boost::multi_index::detail::nth_layer<
    1, employee,
    boost::multi_index::indexed_by<
      boost::multi_index::ordered_unique<
        boost::multi_index::identity<employee>, mpl_::na, mpl_::na
      >,
      boost::multi_index::ordered_non_unique<
        boost::multi_index::member<employee, std::string, &employee::name>,
        mpl_::na, mpl_::na
      >,
      boost::multi_index::ordered_unique<
        boost::multi_index::member<employee, int, &employee::ssnumber>,
        mpl_::na, mpl_::na
      >,
      mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na,
      mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na,
      mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na
    >,
    std::allocator<employee>
  >,
  boost::mpl::vector0<mpl_::na>,
  boost::multi_index::detail::ordered_unique_tag
>

    It can be seen that a significant portion of the type name is contributed by the indexed_by<...> part, which is nothing but an expanded version of the index specifier list provided in the definition of employee_set. We can prevent this very long name from appearing in the final type by encapsulating it into another, shorter-named construct:

    // reducing symbol names through type hidingg
// type hide the index spexifier list within employee_set_indices

struct employee_set_indices:
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >,
    ordered_unique<member<employee,int,&employee::ssnumber> >
  >
{};

typedef multi_index_container<
  employee,
  employee_set_indices
> employee_set;

    employee_set_indices works as a conventional typedef in all respects, save for a detail: its name does not explicitly include the information contained in the indexed_by instantiation. Applying this technique, employee_set::nth_type<0>::type now becomes:

    boost::multi_index::detail::ordered_index<
  boost::multi_index::identity<employee>,
  std::less<employee>,
  boost::multi_index::detail::nth_layer<
    1, employee,
    employee_set_indices,
    std::allocator<employee>
  >,
  boost::mpl::vector0<mpl_::na>,
  boost::multi_index::detail::ordered_unique_tag
>

    which is considerably shorter than the original, and also more easily parsed by a human reader. Type hiding would not work if, instead of making employee_set_indices a derived struct of indexed_by<...>, we had defined it as a typedef: typedefre syntactic aliases and usually get expanded by the compiler before doing any further type handling.

    Type hiding techniques can also be applied to composite_key intantiations, which often contribute a great deal to symbol name lengths.

    Revised October 16th 2006 © Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.html or copy at http://www.boost.org/LICENSE_1_0.txt ) Top


multi_index_container

Name

multi_index_container: Enables the construction of containers maintaining one or more indices with different sorting and access semantics.

Synopsis

namespace boost{

namespace multi_index{

template<typename Value,typename IndexSpecifierList,typename Allocator>
class multi_index_container;

// multi_index_container associated global class templates:

template<typename MultiIndexContainer,int N> struct nth_index;
template<typename MultiIndexContainer,typename Tag> struct index;
template<typename MultiIndexContainer,int N> struct nth_index_iterator;
template<typename MultiIndexContainer,int N> struct nth_index_const_iterator;
template<typename MultiIndexContainer,typename Tag> struct index_iterator;
template<typename MultiIndexContainer,typename Tag> struct index_const_iterator;

// multi_index_container global functions for index retrieval:

template<
  int N,typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type&
get(multi_index_container<Value,IndexSpecifierList,Allocator>& m);

template<
  int N,typename Value,typename IndexSpecifierList,typename Allocator
>
const typename nth_index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type&
get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m);

template<
  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
>
typename index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type&
get(multi_index_container<Value,IndexSpecifierList,Allocator>& m);

template<
  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
>
const typename index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type&
get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m);

// multi_index_container global functions for projection of iterators:

template<
  int N,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type
project(
  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

template<
  int N,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index_const_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type
project(
  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

template<
  typename Tag,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename index_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type
project(
  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

template<
  typename Tag,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename index_const_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type
project(
  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

// comparison:

// OP is any of ==,<,!=,>,>=,<=

template<
  typename Value1,typename IndexSpecifierList1,typename Allocator1,
  typename Value2,typename IndexSpecifierList2,typename Allocator2
>
bool operator OP(
  const multi_index_container<Value1,IndexSpecifierList1,Allocator1>& x,
  const multi_index_container<Value2,IndexSpecifierList2,Allocator2>& y);

// specialized algorithms:

template<typename Value,typename IndexSpecifierList,typename Allocator>
void swap(
  multi_index_container<Value,IndexSpecifierList,Allocator>& x,
  multi_index_container<Value,IndexSpecifierList,Allocator>& y);

} // namespace boost::multi_index

using multi_index::multi_index_container;
using multi_index::get;
using multi_index::project;

} // namespace boost

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost.MultiIndex Documentation - Examples

Boost.MultiIndex Examples

  • Example 1: basic usage

  • Example 2: using member functions as keys

  • Example 3: constructing multi_index_containers with ctor_args_list

  • Example 4: bidirectional map

  • Example 5: sequenced indices

  • Example 6: complex searches and foreign keys

  • Example 7: composite keys

  • Example 8: hashed indices

  • Example 9: serialization and MRU lists

  • Example 10: random access indices

  • Example 11: index rearrangement

Example 1: basic usage

This basic program shows the multi-indexing capabilities of Boost.MultiIndex with a set of employee records. See basic.cpp.

Example 2: using member functions as keys

Keys assigned to an index are based on a member variable of the element, but key extractors can be defined which take their value from a member function. This has some similarity with the concept of calculated keys supported by some relational database engines. The example shows how to use the predefined const_mem_fun key extractor to deal with this situation.

Keys based on member functions usually will not be actual references, but rather the temporary values resulting from the invocation of the member function used. This implies that modify_key cannot be applied to this type of extractors. See memfun_key.cpp.

Example 3: constructing multi_index_containers with ctor_args_list

This example shows the usage of multi_index_container::ctor_arg_list, whose definition and purpose are explained in the tutorial. The program groups a sorted collection of numbers based on identification through modulo arithmetic, by which x and y are equivalent if (x%n)==(y%n), for some fixed n. See non_default_ctor.cpp.

Example 4: bidirectional mapp

This example shows how to construct a bidirectional map with multi_index_container. A bidirectional map that is, a container of elements of std::pair<const FromType,const ToType> such that no two elements exists with the same first or second value (std::map only guarantees uniqueness of the first member). Fast lookup is provided for both keys. The program features a tiny Spanish-English dictionary with online query of words in both languages. See bimap.cpp.

Example 5: sequenced indices

The combination of a sequenced index with an index of type ordered_non_unique yields a list-like structure with fast lookup capabilities. The example performs some operations on a given text, like word counting and selective deletion of some words. See sequenced.cpp.

Example 6: complex searches and foreign keys

See complex_structs.cpp.

This program illustrates some advanced techniques that can be applied for complex data structures using multi_index_container. Consider a car_model class for storing information about automobiles. On a first approach, car_model can be defined as:

struct car_model
{
  std::string model;
  std::string manufacturer;
  int         price;
};

This definition has a design flaw that any reader acquainted with relational databases can easily spot: The manufacturer member is duplicated among all cars having the same manufacturer. This is a waste of space and poses difficulties when, for instance, the name of a manufacturer has to be changed. Following the usual principles in relational database design, the appropriate design involves having the manufactures stored in a separate multi_index_container and store pointers to these in car_model:

struct car_manufacturer
{
  std::string name;
};

struct car_model
{
  std::string       model;
  car_manufacturer* manufacturer;
  int               price;
};

Although predefined Boost.MultiIndex key extractors can handle many situations involving pointers (see advanced features of Boost.MultiIndex key extractors in the tutorial), this case is complex enough that a suitable key extractor has to be defined. The following utility cascades two key extractors:

template<class KeyExtractor1,class KeyExtractor2>
struct key_from_key
{
public:
  typedef typename KeyExtractor1::result_type result_type;

  key_from_key(
    const KeyExtractor1& key1_=KeyExtractor1(),
    const KeyExtractor2& key2_=KeyExtractor2()):
    key1(key1_),key2(key2_)
  {}

  template<typename Arg>
  result_type operator()(Arg& arg)const
  {
    return key1(key2(arg));
  }

private:
  KeyExtractor1 key1;
  KeyExtractor2 key2;
};

so that access from a car_model to the name field of its associated car_manufacturer can be accomplished with

key_from_key<
  member<car_manufacturer,const std::string,&car_manufacturer::name>,
  member<car_model,const car_manufacturer *,car_model::manufacturer>
>

The program asks the user for a car manufacturer and a range of prices and returns the car models satisfying these requirements. This is a complex search that cannot be performed on a single operation. Broadly sketched, one procedure for executing the selection is:

  1. Select the elements with the given manufacturer by means of equal_range,

  2. feed these elements into a multi_index_container sorted by price,

  3. select by price using lower_bound and upper_bound;

or alternatively:

  1. Select the elements within the price range with lower_bound and upper_bound,

  2. feed these elements into a multi_index_container sorted by manufacturer,

  3. locate the elements with given manufacturer using equal_range.

An interesting technique developed in the example lies in the construction of the intermediate multi_index_container. In order to avoid object copying, appropriate view types are defined with multi_index_containers having as elements pointers to car_models instead of actual objects. These views have to be supplemented with appropriate dereferencing key extractors.

Example 7: composite keys

See composite_keys.cpp.

Boost.MultiIndex composite_key construct provides a flexible tool for creating indices with non-trivial sorting criteria. The program features a rudimentary simulation of a file system along with an interactive Unix-like shell. A file entry is represented by the following structure:

struct file_entry
{
  std::string       name;
  unsigned          size;
  bool              is_dir; // true if the entry is a directory
  const file_entry* dir;    // directory this entry belongs in
};

Entries are kept in a multi_index_container maintaining two indices with composite keys:

  • A primary index ordered by directory and name,

  • a secondary index ordered by directory and size.

The reason that the order is made firstly by the directory in which the files are located obeys to the local nature of the shell commands, like for instance ls. The shell simulation only has three commands:

  • cd [.|..|<directory>]

  • ls [-s] (-s orders the output by size)

  • mkdir <directory>

The program exits when the user presses the Enter key at the command prompt.

The reader is challenged to add more functionality to the program; for instance:

  • Implement additional commands, like cp.

  • Add handling of absolute paths.

  • Use serialization to store and retrieve the filesystem state between program runs.

Example 8: hashed indices

See hashed.cpp.

Hashed indices can be used as an alternative to ordered indices when fast lookup is needed and sorting information is of no interest. The example features a word counter where duplicate entries are checked by means of a hashed index. Confront the word counting algorithm with that of example 5.

Example 9: serialization and MRU lists

See serialization.cpp.

A typical application of serialization capabilities allows a program to restore the user context between executions. The example program asks the user for words and keeps a record of the ten most recently entered ones, in the current or in previous sessions. The serialized data structure, sometimes called an MRU (most recently used) list, has some interest on its own: an MRU list behaves as a regular FIFO queue, with the exception that, when inserting a preexistent entry, this does not appear twice, but instead the entry is moved to the front of the list. Observe this behavior in many programs featuring a "Recent files" menu command. This data structure is implemented with multi_index_container by combining a sequenced index and an index of type hashed_unique.

Example 10: random access indices

See random_access.cpp.

The example resumes the text container introduced in example 5 and shows how substituting a random access index for a sequenced index allows for extra capabilities like efficient access by position and calculation of the offset of a given element into the container.

Example 11: index rearrangement

See rearrange.cpp.

There is a relatively common piece of urban lore claiming that a deck of cards must be shuffled seven times in a row to be perfectly mixed. The statement derives from the works of mathematician Persi Diaconis on riffle shuffling: this shuffling technique involves splitting the deck in two packets roughly the same size and then dropping the cards from both packets so that they become interleaved. It has been shown that when repeating this procedure seven times the statistical distribution of cards is reasonably close to that associated with a truly random permutation. A measure of "randomness" can be estimated by counting rising sequences: consider a permutation of the sequence 1,2, ... , n, a rising sequence is a maximal chain of consecutive elements m, m+1, ... , m+r such that they are arranged in ascending order. For instance, the permutation 125364789 is composed of the two rising sequences 1234 and 56789, as becomes obvious by displaying the sequence like this, 125364789. The average number of rising sequences in a random permutation of n elements is (n+1)/2: by contrast, after a single riffle shuffle of an initially sorted deck of cards, there cannot be more than two rising sequences. The average number of rising sequences approximates to (n+1)/2 as the number of consecutive riffle shuffles increases, with seven shuffles yielding a close result for a 52-card poker deck. Brad Mann's paper "How many times should you shuffle a deck of cards?" provides a rigorous yet very accessible treatment of this subject.

The example program estimates the average number of rising sequences in a 52-card deck after repeated riffle shuffling as well as applying a completely random permutation. The deck is modeled by the following container:

multi_index_container<
  int,
  indexed_by<
    random_access<>,
    random_access<>
  >
>

where the first index stores the current arrangement of the deck, while the second index is used to remember the start position. This representation allows for an efficient implementation of a rising sequences counting algorithm in linear time. rearrange is used to apply to the deck a shuffle performed externally on an auxiliary data structure.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Future work

Boost.MultiIndex Future work

A number of new functionalities are considered for inclusion into future releases of Boost.MultiIndex. Some of them depend on the potential for extensibility of the library, which has been a guiding principle driving the current internal design of multi_index_container.

  • Ranked indices

  • Notifying indices

  • Constraints

  • User-defined indices

  • Bidirectional map

  • Indexed maps

  • Move semantics

Ranked indices

Ordered indices are implemented using red-black trees; these trees can be augmented with additional information to obtain a type of data structure called order-statistics trees, allowing for logarithmic search of the n-th element. It has been proposed that order-statistics trees be used to devise a new type of ranked indices that support operator[] while retaining the functionality of ordered indices.

Notifying indices

Notifying indices can be implemented as decorators over preexistent index types, with the added functionality that internal events of the index (insertion, erasing, modifying of elements) are signalled to an external entity --for instance, by means of the Boost.Signals library. This functionality can have applications for:

  1. Logging,

  2. interfacing to GUI-based applications,

  3. synchronization between separate data structures.

The following is a sketch of a possible realization of notifying indices:

struct insert_log
{
  void operator()(int x)
  {
    std::clog<<"insert: "<<x<<std::endl;
  }
};

int main()
{
  typedef multi_index_container<
    int,
    indexed_by<
      notifying<ordered_unique<identity<int> > >, // notifying index
      ordered_non_unique<identity<int> >
    >
  > indexed_t;

  indexed_t t;

  // on_insert is the signal associated to insertions
  t.on_insert.connect(insert_log());

  t.insert(0);
  t.insert(1);

  return 0;
}

// output:
//   insert: 0
//   insert: 1

Constraints

The notifying indices functionality described above exploits a powerful design pattern based on index adaptors, decorators over preexistent indices which add some functionality or somehow change the semantics of the underlying index. This pattern can be used for the implementation of constraints, adaptors that restrict the elements accepted by an index according to some validation predicate. The following is a possible realization of how constraints syntax may look like:

struct is_even
{
  bool operator()(int x)const{return x%2==0;}
};

typedef multi_index_container<
  int,
  indexed_by<
    constrained<ordered_unique<identity<int> >,is_even>
  >
> indexed_t;

User-defined indices

The mechanisms by which Boost.MultiIndex orchestrates the operations of the indices held by a multi_index_container are simple enough to make them worth documenting so that the user can write implementations for own indices.

Bidirectional map

Example 4 in the examples section features a bidirectional map, implemented as a multi_index_container with two unique ordered indices. This particular structure is deemed important enough as to provide it as a separate class template, relying internally in multi_index_container. As feedback is collected from the users of Boost.MultiIndex, other singular instantiations of multi_index_container might be encapsulated to form a component library of ready to use containers.

Indexed maps

multi_index_container is rich enough to provide the basis for implementation of indexed maps, i.e. maps which can be looked upon several different keys. The motivation for having such a container is mainly aesthetic convenience, since it would not provide any additional feature to similar constructs based directly on multi_index_container.

The main challenge in writing an indexed map lies in the design of a reasonable interface that resembles that of std::map as much as possible. There seem to be fundamental difficulties in extending the syntax of a std::map to multiple keys. For one example, consider the situation:

indexed_map<int,string,double> m;
// keys are int and string, double is the mapped to value

...

cout<<m[0]<<endl;      // OK
cout<<m["zero"]<<endl; // OK
m[1]=1.0;              // !!

In the last sentence of the example, the user has no way of providing the string key mapping to the same value as m[1]. This and similar problems have to be devoted a careful study when designing the interface of a potential indexed map.

Move semantics

Andrei Alexandrescu introduced a technique for simulating move constructors called Mojo (see his article in C/C++ User Journal "Generic<Programming>: Move Constructors".) Move semantics alleviates the computational load involved in the creation and copying of temporary objects, specially for heavy classes such as multi_index_containers. David Abrahams and Gary Powell provide an alternative implementation of move semantics in their paper "Clarification of Initialization of Class Objects by rvalues" for the C++ Evolution Working Group.

Adding move semantics to multi_index_container is particularly beneficial when the container is used as an internal building block in other libraries (vg. relational database frameworks), enabling the efficient development of functions returning multi_index_containers. Without support for move semantics, this scheme is impractical and less elegant syntaxes should be resorted to.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Index

Boost Multi-index Containers Library

The Boost Multi-index Containers Library provides a class template named multi_index_container which enables the construction of containers maintaining one or more indices with different sorting and access semantics. Indices provide interfaces similar to those of STL containers, making using them familiar. The concept of multi-indexing over the same collection of elements is borrowed from relational database terminology and allows for the specification of complex data structures in the spirit of multiply indexed relational tables where simple sets and maps are not enough. A wide selection of indices is provided, modeled after analogous STL containers like std::set, std::list and hashed sets.

Boost.MultiIndex features additional functionalities, like subobject searching, range querying and in-place updating of elements, which make it a convenient replacement for std::set and set::multiset even when no multi-indexing capabilities are needed.

The versatile nature of Boost.MultiIndex allows for the specification of a wide spectrum of different data structures. The following are possible examples of use developed in the documentation:

  • Sets with several iteration orders and search criteria.

  • Lists with fast lookup and/or without duplicates.

  • Bidirectional maps, i.e. maps searchable either for key or value.

  • MRU (most recently used) lists, structures keeping the n last referenced items, beginning with the newest ones.

  • Emulations of standard containers taking advantage of the extra functionalities provided by Boost.MultiIndex.

Contents

  • Tutorial

  • Reference

  • Compiler specifics

  • Performance

  • Examples

  • Tests

  • Future work

  • Release notes

  • Acknowledgements

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Performance

Boost.MultiIndex Performance

  • Introduction
  • Manual simulation of a multi_index_container
  • Spatial efficiency
  • Time efficiency
  • Performance tests
    • Results for 1 ordered index
      • Memory consumption
      • Execution time
    • Results for 1 sequenced index
      • Memory consumption
      • Execution time
    • Results for 2 ordered indices
      • Memory consumption
      • Execution time
    • Results for 1 ordered index + 1 sequenced index
      • Memory consumption
      • Execution time
    • Results for 3 ordered indices
      • Memory consumption
      • Execution time
    • Results for 2 ordered indices + 1 sequenced index
      • Memory consumption
      • Execution time
  • Conclusions

Introduction

Boost.MultiIndex helps the programmer to avoid the manual construction of cumbersome compositions of containers when multi-indexing capabilities are needed. Furthermore, it does so in an efficient manner, both in terms of space and time consumption. The space savings stem from the compact representation of the underlying data structures, requiring a single node per element. As for time efficiency, Boost.MultiIndex intensively uses metaprogramming techniques producing very tight implementations of member functions which take care of the elementary operations for each index: for multi_index_containers with two or more indices, the running time can be reduced to half as long as with manual simulations involving several STL containers.

Manual simulation of a multi_index_container

The section on emulation of standard containers with multi_index_container shows the equivalence between single-index multi_index_containers and some STL containers. Let us now concentrate on the problem of simulating a multi_index_container with two or more indices with a suitable combination of standard containers.

Consider the following instantiation of multi_index_container:

typedef multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    ordered_non_unique<identity<int>, std::greater >,
  >
> indexed_t;

indexed_t maintains two internal indices on elements of type int. In order to simulate this data structure resorting only to standard STL containers, one can use on a first approach the following types:

// dereferencing compare predicate
template<typename Iterator,typename Compare>
struct it_compare
{
  bool operator()(const Iterator& x,const Iterator& y)const
  {
    return comp(*x,*y);
  }

private:
  Compare comp;
};

typedef std::set<int>  manual_t1; // equivalent to indexed_t's index #0
typedef std::multiset<
  const int*,
  it_compare<
    const int*,
    std::greater<int>
  >
>                      manual_t2; // equivalent to indexed_t's index #1

where manual_t1 is the "base" container that holds the actual elements, and manual_t2 stores pointers to elements of manual_t1. This scheme turns out to be quite inefficient, though: while insertion into the data structure is simple enough:

manual_t1 c1;
manual_t2 c2;

// insert the element 5
manual_t1::iterator it1=c1.insert(5).first;
c2.insert(&*it1);
deletion, on the other hand, necessitates a logarithmic search, whereas indexed_t deletes in constant time:
// remove the element pointed to by it2
manual_t2::iterator it2=...;
c1.erase(**it2); // watch out! performs in logarithmic time
c2.erase(it2);

The right approach consists of feeding the second container not with raw pointers, but with elements of type manual_t1::iterator:

typedef std::set<int>    manual_t1; // equivalent to indexed_t's index #0
typedef std::multiset<
  manual_t1::iterator,
  it_compare<
    manual_t1::iterator,
    std::greater<int>
  >
>                        manual_t2; // equivalent to indexed_t's index #1

Now, insertion and deletion can be performed with complexity bounds equivalent to those of indexed_t:

manual_t1 c1;
manual_t2 c2;

// insert the element 5
manual_t1::iterator it1=c1.insert(5).first;
c2.insert(it1);

// remove the element pointed to by it2
manual_t2::iterator it2=...;
c1.erase(*it2); // OK: constant time
c2.erase(it2);

The construction can be extended in a straightforward manner to handle more than two indices. In what follows, we will compare instantiations of multi_index_container against this sort of manual simulations.

Spatial efficiency

The gain in space consumption of multi_index_container with respect to its manual simulations is amenable to a very simple theoretical analysis. For simplicity, we will ignore alignment issues (which in general play in favor of multi_index_container.)

Nodes of a multi_index_container with N indices hold the value of the element plus N headers containing linking information for each index. Thus the node size is

SI = e + h0 + ··· + hN-1, where
e = size of the element,
hi = size of the i-th header.

On the other hand, the manual simulation allocates N nodes per element, the first holding the elements themselves and the rest storing iterators to the "base" container. In practice, an iterator merely holds a raw pointer to the node it is associated to, so its size is independent of the type of the elements. Summing all contributions, the space allocated per element in a manual simulation is

SM = (e + h0) + (p + h1) + ··· + (p + hN-1) = SI + (N-1)p, where
p = size of a pointer.

The relative amount of memory taken up by multi_index_container with respect to its manual simulation is just SI / SM, which can be expressed then as:

SI / SM = SI / (SI + (N-1)p).

The formula shows that multi_index_container is more efficient with regard to memory consumption as the number of indices grow. An implicit assumption has been made that headers of multi_index_container index nodes are the same size that their analogues in STL containers; but there is a particular case in which this is often not the case: ordered indices use a spatial optimization technique which is not present in many implementations of std::set, giving an additional advantage to multi_index_containers of one system word per ordered index. Taking this fact into account, the former formula can be adjusted to:

SI / SM = SI / (SI + (N-1)p + Ow),

where O is the number of ordered indices of the container, and w is the system word size (typically 4 bytes on 32-bit architectures.)

These considerations have overlooked an aspect of the greatest practical importance: the fact that multi_index_container allocates a single node per element, compared to the many nodes of different sizes built by manual simulations, diminishes memory fragmentation, which can show up in more usable memory available and better performance.

Time efficiency

From the point of view of computational complexity (i.e. big-O characterization), multi_index_container and its corresponding manual simulations are equivalent: inserting an element into a multi_index_container reduces to a simple combination of elementary insertion operations on each of the indices, and similarly for deletion. Hence, the most we can expect is a reduction (or increase) of execution time by a roughly constant factor. As we will see later, the reduction can be very significative for multi_index_containers with two or more indices.

In the special case of multi_index_containers with only one index, resulting performance will roughly match that of the STL equivalent containers: tests show that there is at most a negligible degradation with respect to STL, and even in some cases a small improvement.

Performance tests

See source code used for measurements.

In order to assess the efficiency of multi_index_container, the following basic algorithm

multi_index_container<...> c;
for(int i=0;i<n;++i)c.insert(i);
for(iterator it=c.begin();it!=c.end();)c.erase(it++);

has been measured for different instantiations of multi_index_container at values of n 1,000, 10,000 and 100,000, and its execution time compared with that of the equivalent algorithm for the corresponding manual simulation of the data structure based on STL containers. The table below describes the test environments used.

Tests environments.
Compiler | Settings | OS and CPU
GCC 3.4.5 (mingw special) | -O3 | Windows 2000 Pro on P4 1.5 GHz, 256 MB RAM
Intel C++ 7.1 | default release settings | Windows 2000 Pro on P4 1.5 GHz, 256 MB RAM
Microsoft Visual C++ 8.0 | default release settings, _SECURE_SCL=0 | Windows XP on P4 Xeon 3.2 GHz, 1 GB RAM

The relative memory consumption (i.e. the amount of memory allocated by a multi_index_container with respect to its manual simulation) is determined by dividing the size of a multi_index_container node by the sum of node sizes of all the containers integrating the simulating data structure.

Results for 1 ordered index

The following instantiation of multi_index_container was tested:

multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >
  >
>

which is functionally equivalent to std::set<int>.

Memory consumption

GCC 3.4.5 | ICC 7.1 | MSVC 8.0
80% | 80% | 80%
Table 1: Relative memory consumption of multi_index_container with 1 ordered index.

The reduction in memory usage is accounted for by the optimization technique implemented in Boost.MultiIndex ordered indices, as explained above.

Execution time

performance of multi_index_container with 1 ordered index
Fig. 1: Performance of multi_index_container with 1 ordered index.

Somewhat surprisingly, multi_index_container performs slightly better than std::set. A very likely explanation for this behavior is that the lower memory consumption of multi_index_container results in a higher processor cache hit rate. The improvement is smallest for GCC, presumably because the worse quality of this compiler's optimizer masks the cache-related benefits.

Results for 1 sequenced index

The following instantiation of multi_index_container was tested:

multi_index_container<
  int,
  indexed_by<
    sequenced<>
  >
>

which is functionally equivalent to std::list<int>.

Memory consumption

GCC 3.4.5 | ICC 7.1 | MSVC 8.0
100% | 100% | 100%
Table 2: Relative memory consumption of multi_index_container with 1 sequenced index.

The figures confirm that in this case multi_index_container nodes are the same size than those of its std::list counterpart.

Execution time

performance of multi_index_container with 1 sequenced index
Fig. 2: Performance of multi_index_container with 1 sequenced index.

multi_index_container does not attain the performance of its STL counterpart, although the figures are close. Again, the worst results are those of GCC, with a degradation of up to 7%, while ICC and MSVC do not exceed a mere 5%.

Results for 2 ordered indices

The following instantiation of multi_index_container was tested:

multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    ordered_non_unique<identity<int> >
  >
>

Memory consumption

GCC 3.4.5 | ICC 7.1 | MSVC 8.0
70% | 70% | 70%
Table 3: Relative memory consumption of multi_index_container with 2 ordered indices.

These results coincide with the theoretical formula for SI = 28, N = O = 2 and p = w = 4.

Execution time

performance of multi_index_container with 2 ordered indices
Fig. 3: Performance of multi_index_container with 2 ordered indices.

The experimental results confirm our hypothesis that multi_index_container provides an improvement on execution time by an approximately constant factor, which in this case lies around 60%. There is no obvious explanation for the increased advantage of multi_index_container in MSVC for n=105.

Results for 1 ordered index + 1 sequenced index

The following instantiation of multi_index_container was tested:

multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    sequenced<>
  >
>

Memory consumption

GCC 3.4.5 | ICC 7.1 | MSVC 8.0
75% | 75% | 75%
Table 4: Relative memory consumption of multi_index_container with 1 ordered index + 1 sequenced index.

These results coincide with the theoretical formula for SI = 24, N = 2, O = 1 and p = w = 4.

Execution time

performance of multi_index_container with 1 ordered index + 1 sequenced index
Fig. 4: Performance of multi_index_container with 1 ordered index + 1 sequenced index.

For n=103 and n=104, the results are in agreement with our theoretical analysis, showing a constant factor improvement of 50-65% with respect to the STL-based manual simulation. Curiously enough, this speedup gets even higher when n=105 for two of the compilers, namely GCC and ICC. In order to rule out spurious results, the tests have been run many times, yielding similar outcomes. Both test environments are deployed on the same machine, which points to some OS-related reason for this phenomenon.

Results for 3 ordered indices

The following instantiation of multi_index_container was tested:

multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    ordered_non_unique<identity<int> >,
    ordered_non_unique<identity<int> >
  >
>

Memory consumption

GCC 3.4.5 | ICC 7.1 | MSVC 8.0
66.7% | 66.7% | 66.7%
Table 5: Relative memory consumption of multi_index_container with 3 ordered indices.

These results coincide with the theoretical formula for SI = 40, N = O = 3 and p = w = 4.

Execution time

performance of multi_index_container with 3 ordered indices
Fig. 5: Performance of multi_index_container with 3 ordered indices.

Execution time for this case is between 45% and 55% lower than achieved with an STL-based manual simulation of the same data structure.

Results for 2 ordered indices + 1 sequenced index

The following instantiation of multi_index_container was tested:

multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    ordered_non_unique<identity<int> >,
    sequenced<>
  >
>

Memory consumption

GCC 3.4.5 | ICC 7.1 | MSVC 8.0
69.2% | 69.2% | 69.2%
Table 6: Relative memory consumption of multi_index_container with 2 ordered indices + 1 sequenced index.

These results coincide with the theoretical formula for SI = 36, N = 3, O = 2 and p = w = 4.

Execution time

performance of multi_index_container with 2 ordered indices + 1 sequenced index
Fig. 6: Performance of multi_index_container with 2 ordered indices + 1 sequenced index.

In accordance to the expectations, execution time is improved by a fairly constant factor, which ranges from 45% to 55%.

Conclusions

We have shown that multi_index_container outperforms, both in space and time efficiency, equivalent data structures obtained from the manual combination of STL containers. This improvement gets larger when the number of indices increase.

In the special case of replacing standard containers with single-indexed multi_index_containers, the performance of Boost.MultiIndex is comparable with that of the tested STL implementations, and can even yield some improvements both in space consumption and execution time.

Revised May 9th 2006
© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Hashed indices reference

Contents

  • Header "boost/multi_index/hashed_index_fwd.hpp" synopsis
  • Header "boost/multi_index/hashed_index.hpp" synopsis
    • Index specifiers hashed_unique and hashed_non_unique
    • Hashed indices
      • Complexity signature
      • Instantiation types
      • Nested types
      • Constructors, copy and assignment
      • Modifiers
      • Observers
      • Lookup
      • Hash policy
      • Serialization

Header "boost/multi_index/hashed_index_fwd.hpp" synopsis

namespace boost{

namespace multi_index{

// index specifiers hashed_unique and hashed_non_unique

template<consult hashed_unique reference for arguments>
struct hashed_unique;
template<consult hashed_non_unique reference for arguments>
struct hashed_non_unique;

// indices

namespace detail{

template<implementation defined> class index name is implementation defined;

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

hashed_index_fwd.hpp provides forward declarations for index specifiers hashed_unique and hashed_non_unique and their associated hashed index classes.

Header "boost/multi_index/hashed_index.hpp" syn opsis

namespace boost{

namespace multi_index{

// index specifiers hashed_unique and hashed_non_unique

template<consult hashed_unique reference for arguments>
struct hashed_unique;
template<consult hashed_non_unique reference for arguments>
struct hashed_non_unique;

// indices

namespace detail{

template<implementation defined> class index class name implementation defined;

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

Index specifiers hashed_unique and hashed_non_unique

These index specifiers allow for insertion of hashed indices without and with allowance of duplicate elements, respectively. The syntax of hashed_unique and hashed_non_unique coincide, thus they are described in a grouped manner. hashed_unique and hashed_non_unique can be instantiated in two different forms, according to whether a tag list for the index is provided or not:

template<
  typename KeyFromValue,
  typename Hash=boost::hash<KeyFromValue::result_type>,
  typename Pred=std::equal_to<KeyFromValue::result_type>
>
struct (hashed_unique | hashed_non_unique);

template<
  typename TagList,
  typename KeyFromValue,
  typename Hash=boost::hash<KeyFromValue::result_type>,
  typename Pred=std::equal_to<KeyFromValue::result_type>
>
struct (hashed_unique | hashed_non_unique);

If provided, TagList must be an instantiation of the class template tag. The template arguments are used by the corresponding index implementation, refer to the hashed indices reference section for further explanations on their acceptable type values.

Hashed indices

A hashed index provides fast retrieval of elements of a multi_index_container through hashing tecnhiques. The interface and semantics of hashed indices are modeled according to the proposal for unordered associative containers given in the C++ Proposed Draft Tecnhical Report on Standard Library Extensions, also known as TR1. A hashed index is particularized according to a given Key Extractor that retrieves keys from elements of multi_index_container, a Hash function object which returns hash values for the keys and a binary predicate Pred acting as an equivalence relation on values of Key.

There are two variants of hashed indices: unique, which do not allow duplicate elements (with respect to its associated equality predicate) and non-unique, which accept those duplicates. The interface of these two variants is the same, so they are documented together, with minor differences explicitly stated when they exist.

Except where noted, hashed indices (both unique and non-unique) are models of Unordered Associative Container, in the spirit of std::tr1::unordered_sets. Validity of iterators and references to elements is preserved in all cases. Occasionally, the exception safety guarantees provided are actually stronger than required by the extension draft.Descriptions of those types is provided and operations that are either not present in the concepts modeled or do not exactly conform to the requirements for unordered associative containers.

namespace boost{

namespace multi_index{

namespace detail{

template<implementation defined: dependent on types Value, Allocator,
  TagList, KeyFromValue, Hash, Pred>
class name is implementation defined
{
public:
  // types:

  typedef typename KeyFromValue::result_type         key_type;
  typedef Value                                      value_type;
  typedef KeyFromValue                               key_from_value;
  typedef Hash                                       hasher;
  typedef Pred                                       key_equal;
  typedef tuple<
    size_type,key_from_value,hasher,key_equal>       ctor_args;
  typedef Allocator                                  allocator_type;
  typedef typename Allocator::pointer                pointer;
  typedef typename Allocator::const_pointer          const_pointer;
  typedef typename Allocator::reference              reference;
  typedef typename Allocator::const_reference        const_reference;
  typedef implementation defined                     size_type;
  typedef implementation defined                     difference_type;
  typedef implementation defined                     iterator;
  typedef implementation defined                     const_iterator;
  typedef implementation defined                     local_iterator;
  typedef implementation defined                     const_local_iterator;

  // construct/destroy/copy:

  index class name& operator=(const index class name& x);

  allocator_type get_allocator()const;

  // size and capacity:

  bool      empty()const;
  size_type size()const;
  size_type max_size()const;

  // iterators:

  iterator       begin();
  const_iterator begin()const;
  iterator       end();
  const_iterator end()const;

  // modifiers:

  std::pair<iterator,bool> insert(const value_type& x);
  iterator insert(iterator position,const value_type& x);
  template<typename InputIterator>
  void insert(InputIterator first,InputIterator last);

  iterator  erase(iterator position);
  size_type erase(const key_type& x);
  iterator  erase(iterator first,iterator last);

  bool replace(iterator position,const value_type& x);
  template<typename Modifier> bool modify(iterator position,Modifier mod);
  template<typename Modifier> bool modify_key(iterator position,Modifier mod);

  void clear();
  void swap(index class name& x);

  // observers:

  key_from_value key_extractor()const;
  hasher         hash_function()const;
  key_equal      key_eq()const;

  // lookup:

  template<typename CompatibleKey>
  iterator find(const CompatibleKey& x)const;
  template<
    typename CompatibleKey,typename CompatibleHash, typename CompatiblePred
  >
  iterator find(
    const CompatibleKey& x,
    const CompatibleHash& hash,const CompatiblePred& eq)const;

  template<typename CompatibleKey>
  size_type count(const CompatibleKey& x)const;
  template<
    typename CompatibleKey,typename CompatibleHash, typename CompatiblePred
  >
  size_type count(
    const CompatibleKey& x,
    const CompatibleHash& hash,const CompatiblePred& eq)const;

  template<typename CompatibleKey>
  std::pair<iterator,iterator> equal_range(const CompatibleKey& x)const;
  template<
    typename CompatibleKey,typename CompatibleHash, typename CompatiblePred
  >
  std::pair<iterator,iterator> equal_range(
    const CompatibleKey& x,
    const CompatibleHash& hash,const CompatiblePred& eq)const;

  // bucket interface:

  size_type bucket_count()const;
  size_type max_bucket_count()const;
  size_type bucket_size(size_type n)const;
  size_type bucket(const key_type& k)const;

  local_iterator       begin(size_type n);
  const_local_iterator begin(size_type n)const;
  local_iterator       end(size_type n);
  const_local_iterator end(size_type n)const;

  // hash policy:

  float load_factor()const;
  float max_load_factor()const;
  void  max_load_factor(float z);
  void  rehash(size_type n);
};

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

Complexity signature

Here and in the descriptions of operations of hashed indices, the scheme outlined in the complexity signature section is adopted. The complexity signature of hashed indices is:

  • copying: c(n)=n*log(n),
  • insertion: average case i(n)=1 (constant), worst case i(n)=n,
  • hinted insertion: average case h(n)=1 (constant), worst case h(n)=n,
  • deletion: average case d(n)=1 (constant), worst case d(n)=n,
  • replacement:
    • if the new element key is equivalent to the original, r(n)=1 (constant),
    • otherwise, average case r(n)=1 (constant), worst case r(n)=n,
  • modifying: average case m(n)=1 (constant), worst case m(n)=n.

Instantiation types

Hashed indices are instantiated internally to multi_index_container and specified by means of in dexed_by with index specifiers hashed_unique and hashed_non_unique . Instantiations are dependent on the following types:

  • Value from multi_index_container,
  • Allocator from multi_index_container,
  • TagList from the index specifier (if provided),
  • KeyFromValue from the index specifier,
  • Hash from the index specifier,
  • Pred from the index specifier.
TagList must be an instantiation of tag. The type KeyFromValue, which determines the mechanism for extracting a key from Value, must be a model of Key Extractor from Value. Hash is a Unary Function taking a single argument of type KeyFromValue::result_type and returning a value of type std::size_t in the range [0, std::numeric_limits<std::size_t>::max()). Pred is a Binary Predicate inducing an equivalence relation on elements of KeyFromValue::result_type. It is required that the Hash object return the same value for keys equivalent under Pred.

Nested types

ctor_args
The first element of this tuple indicates the minimum number of buckets set up by the index on construction time. If the default value 0 is used, an implementation defined number is used instead.
iterator
const_iterator
local_iterator
const_local_iterator
These types are models of Forward Iterator.

Constructors, copy and assignment

As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided. Upon construction, max_load_factor() is 1.0.

index class name& operator=(const index class name& x);
Effects:
a=b;
where a and b are the multi_index_container objects to which *this and x belong, respectively.
Returns: *this.

Modifiers

std::pair<iterator,bool> insert(const value_type& x);
Effects: Inserts x into the multi_index_container to which the index belongs if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND insertion is allowed by all other indices of the multi_index_container.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
 Complexity: O(I(n)).
Exception safety: Strong.
iterator insert(iterator position,const value_type& x);
Requires: position is a valid iterator of the index.
Effects: Inserts x into the multi_index_container to which the index belongs if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND insertion is allowed by all other indices of the multi_index_container.
position is used as a hint to improve the efficiency of the operation.
Returns: On successful insertion, an iterator to the newly inserted element. Otherwise, an iterator to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(H(n)).
Exception safety: Strong.
template<typename InputIterator>
void insert(InputIterator first,InputIterator last);
Requires: InputIterator is a model of Input Iterator over elements of type value_type or a type convertible to value_type. first and last are not iterators into any index of the multi_index_container to which this index belongs. last is reachable from first.
Effects:
iterator hint=end();
while(first!=last)hint=insert(hint,*first++);
Complexity: O(m*H(n+m)), where m is the number of elements in [first, last).
Exception safety: Basic.
iterator erase(iterator position);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Deletes the element pointed to by position.
Returns: An iterator pointing to the element immediately following the one that was deleted, or end() if no such element exists.
Complexity: O(D(n)).
Exception safety: nothrow.
size_type erase(const key_type& x);
Effects: Deletes the elements with key equivalent to x.
Returns: Number of elements deleted.
Complexity: Average case, O(1 + m*D(n)), worst case O(n + m*D(n)), where m is the number of elements deleted.
Exception safety: Basic.
iterator erase(iterator first,iterator last);
Requires: [first,last) is a valid range of the index.
Effects: Deletes the elements in [first,last).
Returns: last.
Complexity: O(m*D(n)), where m is the number of elements in [first,last).
Exception safety: nothrow.
bool replace(iterator position,const value_type& x);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Assigns the value x to the element pointed to by position into the multi_index_container to which the index belongs if, for the value x
  • the index is non-unique OR no other element exists (except possibly *position) with equivalent key,
  • AND replacing is allowed by all other indices of the multi_index_container.
Postconditions: Validity of position is preserved in all cases.
Returns: true if the replacement took place, false otherwise.
Complexity: O(R(n)).
Exception safety: Strong. If an exception is thrown by some user-provided operation the multi_index_container to which the index belongs remains in its original state.
template<typename Modifier> bool modify(iterator position,Modifier mod);
Requires: Modifier is a model of Unary Function accepting arguments of type value_type&. position is a valid dereferenceable iterator of the index.
Effects: Calls mod(e) where e is the element pointed to by position and rearranges *position into all the indices of the multi_index_container. Rearrangement is successful if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND rearrangement is allowed by all other indices of the multi_index_container.
If the rearrangement fails, the element is erased.
Postconditions: Validity of position is preserved if the operation succeeds.
Returns: true if the operation succeeded, false otherwise.
Complexity: O(M(n)).
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod), then the element pointed to by position is erased.
template<typename Modifier> bool modify_key(iterator position,Modifier mod);
Requires: key_from_value is a read/write Key Extractor from value_type. Modifier is a model of Unary Function accepting arguments of type key_type&. position is a valid dereferenceable iterator of the index.
Effects: Calls mod(k) where k is the key obtained by the internal KeyFromValue object of the index from the element pointed to by position, and rearranges *position into all the indices of the multi_index_container. Rearrangement is successful if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND rearrangement is allowed by all other indices of the multi_index_container.
If the rearrangement fails, the element is erased.
Postconditions:Validity of position is preserved if the operation succeeds.
Returns: true if the operation succeeded, false otherwise.
Complexity: O(M(n)).
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod), then the element pointed to by position is erased.

Observers

Apart from standard hash_function and key_eq, hashed indices have a member function for retrieving the internal key extractor used.

key_from_value key_extractor()const;
Returns a copy of the key_from_value object used to construct the index.
Complexity: Constant.

Lookup

Hashed indices provide the full lookup functionality required by unordered associative containers, namely find, count, and equal_range. Additionally, these member functions are templatized to allow for non-standard arguments, so extending the types of search operations allowed. The kind of arguments permissible when invoking the lookup member functions is defined by the following concept.

Consider a pair (Hash, Pred) where Hash is a hash functor over values of type Key and Pred is a Binary Predicate inducing an equivalence relation on Key, with the additional constraint that equivalent keys have the same hash value. A triplet of types (CompatibleKey, CompatibleHash, CompatiblePred) is said to be a compatible extension of (Hash, Pred) if

  1. CompatibleHash is a hash functor on values of type CompatibleKey,
  2. CompatiblePred is a Binary Predicate over (Key, CompatibleKey),
  3. CompatiblePred is a Binary Predicate over (CompatibleKey, Key),
  4. if c_eq(ck,k1) then c_eq(k1,ck),
  5. if c_eq(ck,k1) and eq(k1,k2) then c_eq(ck,k2),
  6. if c_eq(ck,k1) and c_eq(ck,k2) then eq(k1,k2),
  7. if c_eq(ck,k1) then c_hash(ck)==hash(k1),
for every c_hash of type CompatibleHash, c_eq of type CompatiblePred, hash of type Hash, eq of type Pred, ck of type CompatibleKey and k1, k2 of type Key.

Additionally, a type CompatibleKey is said to be a compatible key of (Hash, Pred) if (CompatibleKey, Hash, Pred) is a compatible extension of (Hash, Pred). This implies that Hash and Pred accept arguments of type CompatibleKey, which usually means they have several overloads of their corresponding operator() member functions.

In the context of a compatible extension or a compatible key, the expression "equivalent key" takes on its obvious interpretation.

template<typename CompatibleKey> iterator find(const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of (hasher, key_equal).
Effects: Returns a pointer to an element whose key is equivalent to x, or end() if such an element does not exist.
Complexity: Average case O(1) (constant), worst case O(n).
template<
  typename CompatibleKey,typename CompatibleHash, typename CompatiblePred
>
iterator find(
  const CompatibleKey& x,
  const CompatibleHash& hash,const CompatiblePred& eq)const;
Requires: (CompatibleKey, CompatibleHash, CompatiblePred) is a compatible extension of (hasher, key_equal).
Effects: Returns a pointer to an element whose key is equivalent to x, or end() if such an element does not exist.
Complexity: Average case O(1) (constant), worst case O(n).
template<typename CompatibleKey>
size_type count(const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of (hasher, key_equal).
Effects: Returns the number of elements with key equivalent to x.
Complexity: Average case O(count(x)), worst case O(n).
template<
  typename CompatibleKey,typename CompatibleHash, typename CompatiblePred
>
size_type count(
  const CompatibleKey& x,
  const CompatibleHash& hash,const CompatiblePred& eq)const;
Requires: (CompatibleKey, CompatibleHash, CompatiblePred) is a compatible extension of (hasher, key_equal).
Effects: Returns the number of elements with key equivalent to x.
Complexity: Average case O(count(x,hash,eq)), worst case O(n).
template<typename CompatibleKey>
std::pair<iterator,iterator> equal_range(const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of (hasher, key_equal).
Effects: Returns a range containing all elements with keys equivalent to x (and only those).
Complexity: Average case O(count(x)), worst case O(n).
template<
  typename CompatibleKey,typename CompatibleHash, typename CompatiblePred
>
std::pair<iterator,iterator> equal_range(
  const CompatibleKey& x,
  const CompatibleHash& hash,const CompatiblePred& eq)const;
Requires: (CompatibleKey, CompatibleHash, CompatiblePred) is a compatible extension of (hasher, key_equal).
Effects: Returns a range containing all elements with keys equivalent to x (and only those).
Complexity: Average case O(count(x,hash,eq)), worst case O(n).

Hash policy

void rehash(size_type n);
Effects: Increases if necessary the number of internal buckets so that size()/bucket_count() does not exceed the maximum load factor, and bucket_count()>=n.
Postconditions: Validity of iterators and references to the elements contained is preserved.
Complexity: Average case O(size()), worst case O(size(n)2).
Exception safety: Strong.

Serialization

Indices cannot be serialized on their own, but only as part of the multi_index_container into which they are embedded. In describing the additional preconditions and guarantees associated to hashed indices with respect to serialization of their embedding containers, concepts defined in the multi_index_container serialization section are used.

Operation: saving of a multi_index_container m to an output archive (XML archive) ar.
Requires: No additional requirements to those imposed by the container.
Operation: loading of a multi_index_container m' from an input archive (XML archive) ar.
Requires: Additionally to the general requirements, key_eq() must be serialization-compatible with m.get<i>().key_eq(), where i is the position of the hashed index in the container.
Postconditions: On succesful loading, the range [begin(), end()) contains restored copies of every element in [m.get<i>().begin(), m.get<i>().end()), though not necessarily in the same order.
Operation: saving of an iterator or const_iterator it to an output archive (XML archive) ar.
Requires: it is a valid iterator of the index. The associated multi_index_container has been previously saved.
Operation: loading of an iterator or const_iterator it' from an input archive (XML archive) ar.
Postconditions: On succesful loading, if it was dereferenceable then *it' is the restored copy of *it, otherwise it'==end().
Note: It is allowed that it be a const_iterator and the restored it' an iterator, or viceversa.
Operation: saving of a local_iterator or const_local_iterator it to an output archive (XML archive) ar.
Requires: it is a valid local iterator of the index. The associated multi_index_container has been previously saved.
Operation: loading of a local_iterator or const_local_iterator it' from an input archive (XML archive) ar.
Postconditions: On succesful loading, if it was dereferenceable then *it' is the restored copy of *it; if it was m.get<i>().end(n) for some n, then it'==m'.get<i>().end(n) (where m is the original multi_index_contai ner, m' its restored copy and i is the ordinal of the index.)
Note: It is allowed that it be a const_local_iterator and the restored it' a local_iterator, or viceversa.
Revised July 13th 2006
© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Documentation - Reference

Boost.MultiIndex Reference

  • Header dependencies

  • Class template multi_index_container

  • Index reference

  • Ordered indices

  • Hashed indices

  • Sequenced indices

  • Random access indices

  • Key Extraction

Header dependencies

The following dependencies among headers of Boost.MultiIndex hold:

  • "boost/multi_index_container.hpp" includes

    • "boost/multi_index/indexekd_by.hpp".

  • "boost/multi_index/ordered_index.hpp" includes

    • "boost/multi_index/tag.hpp".

  • "boost/multi_index/hashed_index.hpp" includes

    • "boost/multi_index/tag.hpp".

  • "boost/multi_index/sequenced_index.hpp" includes

    • "boost/multi_index/tag.hpp".

  • "boost/multi_index/random_access_index.hpp" includes

    • "boost/multi_index/tag.hpp".

  • "boost/multi_index/key_extractors.hpp" includes

    • "boost/multi_index/identity.hpp",

    • "boost/multi_index/member.hpp",

    • "boost/multi_index/mem_fun.hpp" and

    • "boost/multi_index/composite_key.hpp".

So, a program using Boost.MultiIndex must include "boost/multi_index_container.hpp", the headers defining the index types to be used and possibly one or more key extraction headers for key-based indices. Note that all the key extractors provided by Boost.MultiIndex are automatically included with "boost/multi_index/key_extractors.hpp".

In order to use the serialization capabilities of Boost.MultiIndex, the appropriate Boost.Serialization library module must be linked. Other than that, Boost.MultiIndex is a header-only library, requiring no additional object modules.


Boost.MultiIndex Index reference

Contents

  • Index concepts
  • Complexity signature
  • Index specification
  • Header "boost/multi_index/indexed_by.hpp" synopsis
    • Class template indexed_by
  • Tags
  • Header "boost/multi_index/tag.hpp" synopsis
    • Class template tag
  • Indices provided by Boost.MultiIndex
    • Key-based indices
    • Other types
  • Index views

Index concepts

multi_index_container instantiations comprise one or more indices specified at compile time. Each index allows read/write access to the elements contained in a definite manner. For instance, ordered indices provide a set-like interface to the elements, whereas sequenced indices mimic the functionality of std::list.

Indices are not isolated objects, and so cannot be constructed on their own. Rather they are embedded into a multi_index_container as specified by means of an index specifier. The name of the index class implementation proper is never directly exposed to the user, who has only access to the associated index specifier.

Insertion and erasing of elements are always performed through the appropriate interface of some index of the multi_index_container; these operations, however, do have an impact on all other indices as well: for instance, insertion through a given index may fail because there exists another index which bans the operation in order to preserve its invariant (like uniqueness of elements.) This circumstance, rather than being an obstacle, yields much of the power of Boost.MultiIndex: equivalent constructions based on manual composition of standard containers would have to add a fair amount of code in order to globally preserve the invariants of each container while guaranteeing that all of them are synchronized. The global operations performed in a joint manner among the various indices can be reduced to six primitives:

  • Copying,
  • insertion of an element,
  • hinted insertion, where a preexisting element is suggested in order to improve the efficiency of the operation,
  • deletion of an element,
  • replacement of the value of an element, which may trigger the rearrangement of this element in one or more indices, or the banning of the replacement,
  • modification of an element, and its subsequent rearrangement/banning by the various indices.
The last two primitives deserve some further explanation: in order to guarantee the invariants associated to each index (e.g. some definite ordering,) elements of a multi_index_container are not mutable. To overcome this restriction, indices expose member functions for updating and modifying, which allow for the mutation of elements in a controlled fashion. Immutability of elements does not significantly impact the interface of ordered indices, as it is based upon that of std::set and std:multiset, and these containers also have non-mutable elements; but it may come as a surprise when dealing with sequenced indices, which are designed upon the functionality provided by std::list.

These global operations are not directly exposed to the user, but rather they are wrapped as appropriate by each index (for instance, ordered indices provide a set-like suite of insertion member functions, whereas sequenced and random access indices have push_back and push_front operations.) Boost.MultiIndex poses no particular conditions on the interface of indices, save that they must model Container (without the requirement of being Assignable .)

Complexity signature

Some member functions of an index interface are implemented by global primitives from the list above. Complexity of these operations thus depends on all indices of a given multi_index_container, not just the currently used index.

In order to establish complexity estimates, an index is characterized by its complexity signature, consisting of the following associated functions on the number of elements:

  • c(n): copying,
  • i(n): insertion,
  • h(n): hinted insertion,
  • d(n): deletion,
  • r(n): replacement,
  • m(n): modifying.

Each function yields the complexity estimate of the contribution of the index to the corresponding global primitive. Let us consider an instantiation of multi_index_container with N indices labelled 0,...,N-1 whose complexity signatures are (ci,ii,hi,di,ri,mi); the insertion of an element in such a container is then of complexity O(i0(n)+···+iN-1(n)) where n is the number of elements. To abbreviate notation, we adopt the following definitions:
  • C(n)=c0(n)+···+cN-1(n),
  • I(n)=i0(n)+···+iN-1(n),
  • H(n)=h0(n)+···+hN-1(n),
  • D(n)=d0(n)+···+dN-1(n),
  • R(n)=r0(n)+···+rN-1(n),
  • M(n)=m0(n)+···+mN-1(n).
For instance, consider a multi_index_container with two ordered indices, for which i(n)=log(n), and a sequenced index with i(n)=1 (constant time insertion). Insertion of an element into this multi_index_container is then of complexity.
O(I(n))=O(2*log(n)+1)=O(log(n)).

Index specification

Index specifiers are passed as instantiation arguments to multi_index_container and provide the information needed to incorporate the corresponding indices. Future releases of Boost.MultiIndex may allow for specification of user-defined indices. Meanwhile, the requirements for an index specifier remain implementation defined. Currently, Boost.MultiIndex provides the index specifiers.

  • ordered_unique and ordered_non_unique for ordered indices,
  • hashed_unique and hashed_non_unique for hashed indices,
  • sequenced for sequenced indices
  • and random_access for random access indices.

Header "boost/multi_index/indexed_by.hpp" synopsis

namespace boost{

namespace multi_index{

template<typename T0,...,typename Tn>
struct indexed_by;

} // namespace boost::multi_index

} // namespace boost

Class template indexed_by

indexed_by is a model of MPL Random Access Sequence and MPL Extensible Sequence meant to be used to specify a compile-time list of indices as the IndexSpecifierList of m ulti_index_container.

template<typename T0,...,typename Tn>
struct indexed_by;

Each user-provided element of indexed_list must be an index specifier. At least an element must be provided. The maximum number of elements of an indexed_by sequence is implementation defined.

Tags

Tags are just conventional types used as mnemonics for indices of an multi_index_container, as for instance in member function get. Each index can have none, one or more tags associated. The way tags are assigned to a given index is dependent on the particular index specifier. However, for convenience all indices of Boost.MultiIndex support tagging through the class template tag.

Header "boost/multi_index/tag.hpp" synopsis

namespace boost{

namespace multi_index{

template<typename T0,...,typename Tn>
struct tag;

} // namespace boost::multi_index

} // namespace boost

Class template tag

tag is a typelist construct used to specify a compile-time sequence of tags to be assigned to an index in instantiation time.

template<typename T0,...,typename Tn>
struct tag;

Elements of tag can be any type, though the user is expected to provide classes with mnemonic names. Duplicate elements are not allowed. The maximum number of elements of a tag instantiation is implementation defined.

Indices provided by Boost.MultiIndex

Key-based indices

Indices of this type are organized around keys obtained from the elements, as described in the key extraction reference.

  • Ordered indices sort the elements on the key and provide fast lookup capabilites.
  • Hashed indices offer high efficiency access through hashing techniques.

Other types

  • Sequenced indices allow to arrange elements as in a bidirectional list.
  • Random access indices provide constant time positional access and free ordering of elements.

Index views

The following concept is used by the rearrange facilities of non key-based indices. Given an index i of type Index, a view of i is any range [first,last) where first and last are objects of a type Iterator modelling Input Iterator such that

  1. the associated value type of Iterator is convertible to const Index::value_type&
  2. and each of the elements of i appears exactly once in [first,last).
Note that the view refers to the actual elements of i, not to copies of them. Additionally, a view is said to be free if its traversal order is not affected by changes in the traversal order of i. Examples of free views are:
  • [c.begin(),c.end()), where c is any container of reference wrappers (from Boost.Ref) to the elements of i containing exactly one reference to every element.
  • [i'.begin(),i'.end()), where i' is any index belonging to the same multi_index_container as i, except i itself.
  • Any range which is a permutation of the ones described above, as for instance [c.rbegin(),c.rend()), or ranges obtained from the former with the aid of permutation_iterator from Boost.Iterator.

Revised February 6th 2006© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Documentation - Key extraction reference

Boost.MultiIndex Key extraction reference

Contents

  • Key Extractors
    • Chained pointers
  • Header "boost/multi_index/key_extractors.hpp" synopsis
  • Header "boost/multi_index/identity.hpp" synopsis
    • Class template identity
  • Header "boost/multi_index/member.hpp" synopsis
    • Class template member
    • Class template member_offset
    • Macro BOOST_MULTI_INDEX_MEMBER
  • Header "boost/multi_index/mem_fun.hpp" synopsis
    • Class template const_mem_fun
    • Class template mem_fun
    • Class template const_mem_fun_explicit
    • Class template mem_fun_explicit
    • Macro BOOST_MULTI_INDEX_CONST_MEM_FUN
    • Macro BOOST_MULTI_INDEX_MEM_FUN
  • Header "boost/multi_index/composite_key.hpp" synopsis
    • Class template composite_key
    • Class template composite_key_result
    • Equality
      • Class template composite_key_equal_to
      • Class template composite_key_result_equal_to
      • Specialization of std::equal_to for composite_key results
    • Comparison
      • Class template composite_key_compare
      • Class template composite_key_result_less
      • Class template composite_key_result_greater
      • Specialization of std::less for composite_key results
      • Specialization of std::greater for composite_key results
    • Hashing
      • Class template composite_key_hash
      • Class template composite_key_result_hash
      • Specialization of boost::hash for composite_key results
    • Semantics of composite_key_result

Key Extractors

Key extraction classes are used by key-based indices to obtain the indexing keys from the elements of a multi_index_container. An Assignable class KeyFromValue is said to be a key extractor from a type Type if

  1. the type KeyFromValue::result_type is defined,
  2. k1(ca) is defined and returns a value convertible to const KeyFromValue::result_type&,
  3. if k2 is a copy of k1, k1(ca) is the same value as k2(ca),
for every k1, k2 of type const KeyFromValue, and ca of type const Type&.

Additionally, KeyFromValue is a read/write key extractor if the following extra conditions are met:

  1. k1(a) is defined and returns a value convertible to KeyFromValue::result_type&,
  2. const_cast<const KeyFromValue::result_type&>(k1(a)) is the same value as k1(const_cast<const Type&>(a)),
for every k1 of type const KeyFromValue and a of type Type&.

Boost.MultiIndex provides five general-purpose key extractors:

  • identity,
  • member,
  • const_mem_fun,
  • mem_fun and
  • composite_key,
plus replacements for some of them:
  • member_offset,
  • const_mem_fun_explicit and
  • mem_fun_explicit,
that workaround some deficiencies in the support for non-type template parameters by certain compilers.

Chained pointers

The key extractors provided by Boost.MultiIndex are templatized according to the type Type and serve to extract keys not only from objects of type Type, but also from reference wrappers provided by Boost.Ref and from chained pointers to Type (or to reference wrappers of Type): a chained pointer is any type P such that, for an object p of type const P

  • *p yields an object of type Type& or boost::reference_wrapper<Type>, OR
  • *p yields a chained pointer to Type,
that is, chained pointers are arbitrary compositions of pointer-like objects ultimately dereferencing to values of Type& or boost::reference_wrapper<Type>.

Header "boost/multi_index/key_extractors.hpp" synopsis

#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/composite_key.hpp>

This header includes all the key extractors provided by Boost.MultiIndex.

Header "boost/multi_index/identity.hpp" synopsis

namespace boost{

namespace multi_index{

template<typename T> struct identity;

} // namespace boost::multi_index

} // namespace boost

Class template identity

identity is a Key Extractor that acts as a do-nothing identity functor.

template<typename Type>
struct identity
{
  typedef Type result_type;

  // only provided if const ChainedPtr& is not convertible to const Type&
  template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;

  const Type& operator()(const Type& x)const;
  Type&       operator()(Type& x)const; // only provided if Type is non-const

  // only provided if Type is non-const
  const Type& operator()(const reference_wrapper<const Type>& x)const;

  // only provided if Type is const
  Type& operator()(
    const reference_wrapper<typename remove_const<Type>::type>& x)const;

  Type& operator()(const reference_wrapper<Type>& x)const;
};

identity<Type> is a model of:

  • read/write Key Extractor from Type,
  • Key Extractor from reference_wrapper<const Type>,
  • read/write Key Extractor from reference_wrapper<Type>,
  • Key Extractor from any chained pointer to const Type,
  • read/write Key Extractor from any chained pointer to Type.

identity members

template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;
Requires: ChainedPtr is a chained pointer type to Type.
Returns: a reference to the object chained-pointed to by x.
const Type& operator()(const Type& x)const;
Returns: x.
Type& operator()(Type& x)const;
Returns: x.
const Type& operator()(const reference_wrapper<const Type>& x)const;
Returns: x.get().
Type& operator()(const reference_wrapper<typename remove_const<Type>::type>& x)const;
Returns: x.get().
Type& operator()(const reference_wrapper<Type>& x)const;
Returns: x.get().

Header "boost/multi_index/member.hpp" synopsis

namespace boost{

namespace multi_index{

template<class Class,typename Type,Type Class::*PtrToMember>
struct member;

template<class Class,typename Type,std::size_t OffsetOfMember>
struct member_offset;

#define BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName) implementation defined

} // namespace boost::multi_index

} // namespace boost

Class template member

member is a Key Extractor aimed at accessing a given member of a class.

template<class Class,typename Type,Type Class::*PtrToMember>
struct member
{
  typedef Type result_type;

  // only provided if const ChainedPtr& is not convertible to const Class&
  template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;

  const Type& operator()(const Class& x)const;
  Type&       operator()(Class& x)const; // only provided if Type is non-const
  const Type& operator()(const reference_wrapper<const Class>& x)const;
  Type&       operator()(const reference_wrapper<Class>& x)const;
};

The PtrToMember template argument specifies the particular Type Class::* pointer to the member to be extracted. member<Class,Type,PtrToMember> is a model of:

  • read/write Key Extractor from Class,
  • Key Extractor from reference_wrapper<const Class>,
  • read/write Key Extractor from reference_wrapper<Class>,
  • Key Extractor from any chained pointer to const Class,
  • read/write Key Extractor from any chained pointer to Class.

member members

template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;
Requires: ChainedPtr is a chained pointer type to Type.
Returns: a reference to the object chained-pointed to by x.
const Type& operator()(const Class& x)const;
Returns: x.*PtrToMember.
Type& operator()(Class& x)const;
Returns: x.*PtrToMember.
const Type& operator()(const reference_wrapper<const Class>& x)const;
Returns: x.get().*PtrToMember.
Type& operator()(const reference_wrapper<Class>& x)const;
Returns: x.get().*PtrToMember.

Class template member_offset

Some compilers do not properly support pointers to members as non-type template arguments. The following have been confirmed to have bugs in this respect:

  • MSVC++ 6.0 (see Microsoft Knowledge Base article #249045),
  • MSVC++ 7.0 (not officially confirmed by Microsoft),
  • Intel C++ 7.0/7.1 for Windows (support issue #207321),
  • VisualAge 6.0 for AIX (internal defect #288539.)
In this situation, member_offset provides an alternative to member accepting offsets instead of pointers to members. Please note that the use of offsetof on non-POD types is forbidden by the standard; luckily enough, most compilers accept it nevertheless, so member_offset serves as a workaround for most practical purposes.

template<class Class,typename Type,std::size_t OffsetOfMember>
struct member_offset
{
  typedef Type result_type;

  // only provided if const ChainedPtr& is not convertible to const Class&
  template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;

  const Type& operator()(const Class& x)const;
  Type&       operator()(Class& x)const; // only provided if Type is non-const
  const Type& operator()(const reference_wrapper<const Class>& x)const;
  Type&       operator()(const reference_wrapper<Class>& x)const;
};

As an example of use, given the class

class A
{
  int x;
}

the instantiation member<A,int,&A::x> can be simulated then as member_offset<A,int,offsetof(A,x)>.

Macro BOOST_MULTI_INDEX_MEMBER

BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName)

This macro is provided as an aid for using member and member_offset when writing cross-platform code. In the usual cases, it expands to

::boost::multi_index::member<Class,Type,&Class::MemberName>

but it resolves to

::boost::multi_index::member_offset<Class,Type,offsetof(Class,MemberName)>

if the Boost Configuration Library defect macro BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS is defined.

Header "boost/multi_index/mem_fun.hpp" synopsis

namespace boost{

namespace multi_index{

template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()const>
struct const_mem_fun;

template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()>
struct mem_fun;

template<
  class Class,typename Type,
  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
>
struct const_mem_fun_explicit;

template<
  class Class,typename Type,
  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
>
struct mem_fun_explicit;

#define BOOST_MULTI_INDEX_CONST_MEM_FUN(Class,Type,MemberFunName) \
implementation defined
#define BOOST_MULTI_INDEX_MEM_FUN(Class,Type,MemberFunName) \
implementation defined

} // namespace boost::multi_index

} // namespace boost

Class template const_mem_fun

const_mem_fun is a Key Extractor returning as key the result of invoking a given constant member function of a class.

template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()const>
struct const_mem_fun
{
  typedef typename remove_reference<Type>::type result_type;

  // only provided if const ChainedPtr& is not convertible to const Class&
  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;

  Type operator()(const Class& x)const;
  Type operator()(const reference_wrapper<const Class>& x)const;
  Type operator()(const reference_wrapper<Class>& x)const;
};

The PtrToMemberFunction template argument specifies the particular Type (Class::*PtrToMemberFunction)()const pointer to the the constant member function used in the extraction. const_mem_fun<Class,Type,PtrToMemberFunction> is a model of:

  • Key Extractor from Class,
  • Key Extractor from reference_wrapper<const Class>,
  • Key Extractor from reference_wrapper<Class>,
  • Key Extractor from any chained pointer to const Class,
  • Key Extractor from any chained pointer to Class.

const_mem_fun members

template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;
Requires: ChainedPtr is a chained pointer type to Type.
Returns: (y.*PtrToMemberFunction)(), where y is the object chained-pointed to by x.
Type operator()(const Class& x)const;
Returns: (x.*PtrToMemberFunction)().
Type operator()(const reference_wrapper<const Class>& x)const;
Returns: (x.get().*PtrToMemberFunction)().
Type operator()(const reference_wrapper<Class>& x)const;
Returns: (x.get().*PtrToMemberFunction)().

Class template mem_fun

mem_fun is a Key Extractor returning as key the result of invoking a given member function of a class.

template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()>
struct mem_fun
{
  typedef typename remove_reference<Type>::type result_type;

  // only provided if ChainedPtr& is not convertible to Class&
  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;

  Type operator()(Class& x)const;
  Type operator()(const reference_wrapper<Class>& x)const;
};

The PtrToMemberFunction template argument specifies the particular Type (Class::*PtrToMemberFunction)() pointer to the the member function used in the extraction. mem_fun<Class,Type,PtrToMemberFunction> is a model of:

  • Key Extractor from reference_wrapper<Class>,
  • Key Extractor from any chained pointer to Class.

mem_fun members

template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;
Requires: ChainedPtr is a chained pointer type to Type.
Returns: (y.*PtrToMemberFunction)(), where y is the object chained-pointed to by x.
Type operator()(Class& x)const;
Returns: (x.*PtrToMemberFunction)().
Type operator()(const reference_wrapper<Class>& x)const;
Returns: (x.get().*PtrToMemberFunction)().

Class template const_mem_fun_explicit

MSVC++ 6.0 do not properly support pointers to constant member functions as non-type template parameters, thus const_mem_fun cannot be used in this compiler. A simple workaround consists in specifying the type of these pointers as an additional template parameter.

template<
  class Class,typename Type,
  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
>
struct const_mem_fun_explicit
{
  typedef typename remove_reference<Type>::type result_type;

  // only provided if const ChainedPtr& is not convertible to const Class&
  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;

  Type operator()(const Class& x)const;
  Type operator()(const reference_wrapper<const Class>& x)const;
  Type operator()(const reference_wrapper<Class>& x)const;
};

const_mem_fun_explicit provides the very same functionality as its const_mem_fun analogous instantiation. For example, given the type

struct A
{
  int f()const;
};

the extractor const_mem_fun<A,int,&A::f> can be replaced by const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>.

Class template mem_fun_explicit

For analogy with const_mem_fun_explicit, a variation of mem_fun is provided accepting an additional parameter with the type of the pointer to non-constant member function used for extraction.

template<
  class Class,typename Type,
  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
>
struct mem_fun_explicit
{
  typedef typename remove_reference<Type>::type result_type;

  // only provided if ChainedPtr& is not convertible to Class&
  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;

  Type operator()(Class& x)const;
  Type operator()(const reference_wrapper<Class>& x)const;
};

Macro BOOST_MULTI_INDEX_CONST_MEM_FUN

BOOST_MULTI_INDEX_CONST_MEM_FUN(Class,Type,MemberFunName)

Use this macro when writing cross-platform code selectively using const_mem_fun_explicit in place of const_mem_fun for compilers not supporting the latter. In the usual cases, the macro expands to

::boost::multi_index::const_mem_fun<Class,Type,&Class::MemberFunName>

but it resolves to

::boost::multi_index::const_mem_fun_explicit<
  Class,Type,Type (Class::*)()const,&Class::MemberFunName
>

for MSVC++ 6.0 or lower.

Macro BOOST_MULTI_INDEX_MEM_FUN

BOOST_MULTI_INDEX_MEM_FUN(Class,Type,MemberFunName)

By default, the macro expands to

::boost::multi_index::mem_fun<Class,Type,&Class::MemberFunName>

but it resolves to

::boost::multi_index::mem_fun_explicit<
  Class,Type,Type (Class::*)(),&Class::MemberFunName
>

for MSVC++ 6.0 or lower.

Header "boost/multi_index/composite_key.hpp" synopsis

namespace boost{

namespace multi_index{

template<typename Value,typename KeyFromValue0,...,typename KeyFromValuen>
struct composite_key;

template<typename CompositeKey>
struct composite_key_result;

// comparison operators for composite_key_result:

// OP is any of =,<,!=,>,>=,<=

template<typename CompositeKey1,typename CompositeKey2>
bool operator OP(
  const composite_key_result<CompositeKey1>& x,
  const composite_key_result<CompositeKey2>& y);

template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator OP(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y);

template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator OP(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y);

// equality functors:

template<typename Pred0,...,typename Predn>
struct composite_key_equal_to;

template<typename CompositeKeyResult>
struct composite_key_result_equal_to;

// comparison functors:

template<typename Compare0,...,typename Comparen>
struct composite_key_compare;

template<typename CompositeKeyResult>
struct composite_key_result_less;

template<typename CompositeKeyResult>
struct composite_key_result_greater;

// hash functors:

template<typename Hash0,...,typename Hashn>
struct composite_key_hash;

template<typename CompositeKeyResult>
struct composite_key_result_hash;

} // namespace boost::multi_index

} // namespace boost

// specializations of external functors for composite_key_result:

namespace std{

template<typename CompositeKey>
struct equal_to<boost::multi_index::composite_key_result<CompositeKey> >;

template<typename CompositeKey>
struct less<boost::multi_index::composite_key_result<CompositeKey> >;

template<typename CompositeKey>
struct greater<boost::multi_index::composite_key_result<CompositeKey> >;

} // namespace std

namespace boost{

template<typename CompositeKey>
struct hash<boost::multi_index::composite_key_result<CompositeKey> >;

} // namespace boost

Class template composite_key

composite_key is a Key Extractor returning the combined value of several key extractors whose type is specified at compile time. The returned object is of type composite_key_result <composite_key>.

template<typename Value,typename KeyFromValue0,...,typename KeyFromValuen>
struct composite_key
{
  typedef tuple<KeyFromValue0,...,KeyFromValuen> key_extractor_tuple;
  typedef Value                                  value_type;
  typedef composite_key_result<composite_key>    result_type;

  composite_key(
    const KeyFromValue0& k0=KeyFromValue0(),
    ...
    const KeyFromValuen& kn=KeyFromValuen());

  composite_key(const key_extractor_tuple& x);

  const key_extractor_tuple& key_extractors()const;
  key_extractor_tuple&       key_extractors()

  // only provided if const ChainedPtr& is not convertible to const value_type&
  template<typename ChainedPtr>
  result_type operator()(const ChainedPtr& x)const;

  result_type operator()(const value_type& x)const;
  result_type operator()(const reference_wrapper<const value_type>& x)const;
  result_type operator()(const reference_wrapper<value_type>& x)const;
};

KeyFromValue0, ... , KeyFromValuen are the types of the key extractors combined into the composite key. Each of these types must be a Key Extractor from Value. At least a key extractor must be provided. The maximum number of key extractors of a composite_key instantiation is implementation defined. composite_key internally stores an object of every constituent key extractor type. composite_key<Value,KeyFromValue0,...,KeyFromValuen> is a model of:

  • Key Extractor from Value,
  • Key Extractor from reference_wrapper<const Value>,
  • Key Extractor from reference_wrapper<Value>,
  • Key Extractor from any chained pointer to const Value,
  • Key Extractor from any chained pointer to Value.

composite_key members

composite_key(
  const KeyFromValue0& k0=KeyFromValue0(),
  ...
  const KeyFromValuen& kn=KeyFromValuen());
Effects: Constructs a composite_key that stores copies of the key extractor objects supplied.
composite_key(const key_extractor_tuple& x);
Effects: Constructs a composite_key that stores copies of the key extractor objects supplied in x.
const key_extractor_tuple& key_extractors()const;
Returns: a constant reference to a tuple holding the key extractors internally stored by the composite_key.
key_extractor_tuple& key_extractors();
Returns: a reference to a tuple holding the key extractors internally stored by the composite_key.
template<typename ChainedPtr>
result_type operator()(const ChainedPtr& x)const;
Requires: ChainedPtr is a chained pointer type to result_type.
Returns: a result_type object dependent on *this and y, where y is the object chained-pointed to by x.
result_type operator()(const value_type& x)const;
Returns: a result_type object dependent on *this and x.
result_type operator()(const reference_wrapper<const value_type>& x)const;
Returns: a result_type object dependent on *this and x.get().
result_type operator()(const reference_wrapper<value_type>& x)const;
Returns: a result_type object dependent on *this and x.get().

Class template composite_key_result

This is an opaque type returned by composite_key instantiations as their extracted key.

template<typename CompositeKey>
struct composite_key_result
{
  no public interface available
};

// comparison:

// OP is any of =,<,!=,>,>=,<=

template<typename CompositeKey1,typename CompositeKey2>
bool operator OP(
  const composite_key_result<CompositeKey1>& x,
  const composite_key_result<CompositeKey2>& y);

template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator OP(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y);

template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator OP(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y);
CompositeKey is the composite_key instantiation to which the composite_key_result type is associated. Objects of type composite_key_result returned by a composite key must be always treated as temporary, i.e. they should not be stored or copied. composite_key_result is not guaranteed to be a model of Default Constructible or Assignable. Every object of type composite_key_result<CompositeKey> is internally associated to the CompositeKey from which it is returned and the object of type CompositeKey::value_type to which the composite key was applied.

Notation

Given an x of type composite_key_result<CompositeKey>, we use the following notation:

  • ck(x) is the CompositeKey object associated to x,
  • v(x) is the object of type CompositeKey::value_type associated to x,
  • ki(x) = ck(x).key_extractors().get<i>(), that is, is the i-th key extractor of ck(x),
  • xi = ki(x)(v(x)), that is, the key extracted from v(x) by the i-th key extractor,
  • length(x) is the number of key extractors of ck(x).
Also, if y is a tuple of values, we define:
  • yi=y.get<i>(),
  • length(y) is the number of elements of y.

Comparison operators

template<typename CompositeKey1,typename CompositeKey2>
bool operator==(
  const composite_key_result<CompositeKey1>& x,
  const composite_key_result<CompositeKey2>& y);
template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator==(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y);
template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator==(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y);
Requires: length(x)==length(y). The expression xi==yi is valid for all i in [0,length(x)).
Returns: true if and only if
xi==yi for all i in [0,length(x)).
Complexity: No more key extraction operations and comparisons are performed than those necessary for the evaluation of the expression above, starting at i==0. The evaluation is short-circuited as soon as the result is determined to be false.
template<typename CompositeKey1,typename CompositeKey2>
bool operator<(
  const composite_key_result<CompositeKey1>& x,
  const composite_key_result<CompositeKey2>& y);
template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator<(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y);
template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator<(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y);
Requires: The expressions xi<yi and yi<xi are valid for all i in [0,min(length(x),length(y))).
Returns: true if and only if there exists some j in the range [0,min(length(x),length(y))) such that
!(xi<yi) && !(yi<xi) for all i in [0,j),
  xj<yj.
Complexity: No more key extraction operations and comparisons are performed than those necessary for the evaluation of the expression above, starting at i==0. The evaluation is short-circuited as soon as the result is determined to be false.
template<typename CompositeKey1,typename CompositeKey2>
bool operator OP(
  const composite_key_result<CompositeKey1>& x,
  const composite_key_result<CompositeKey2>& y);
template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator OP(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y);
template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator OP(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y);

(OP is any of !=, >, >=, <=.)

Requires: The expressions given below are valid (for the particular OP considered.)
Returns: true if and only if
!(x==y) (OP is !=),
  y< x  (OP is > ),
!(x< y) (OP is >=),
!(y< x) (OP is <=).

Equality

Class template composite_key_equal_to

composite_key_equal_to tests for equality between composite_key_result instantiations and between these and tuples of values, using an internally stored collection of elementary equality predicates.

template<typename Pred0,...,typename Predn>
struct composite_key_equal_to
{
  typedef tuple<Pred0,...,Predn> key_eq_tuple;

  composite_key_equal_to(
    const Pred0& p0=Pred0(),
    ...
    const Predn& pn=Predn());

  composite_key_equal_to(const key_eq_tuple& x);

  const key_eq_tuple& key_eqs()const;
  key_eq_tuple&       key_eqs();

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const composite_key_result<CompositeKey1> & x,
    const composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const composite_key_result<CompositeKey>& x,
    const tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const tuple<Value0,...,Valuen>& x,
    const composite_key_result<CompositeKey>& y)const;
};

Pred0, ... , Predn are the types of the equality predicates stored by composite_key_equal_to. Each of these types must be a Binary Predicate. At least an equality predicate must be provided. The maximum number of equality predicates of a composite_key_equal_to instantiation is implementation defined. composite_key_equal_to is Assignable. It is also Default Constructible if each Predi is Default Constructible.

Note that formally it is not required that the Predi types behave as equality predicates in any definite way. However, the semantics of composite_key_equal_to is well defined if this is the case, as explained in the section on the semantics of composite_key_result.

Notation

In what follows we use the same notation introduced for composite_key_result.

composite_key_equal_to members

composite_key_equal_to(
  const Pred0& p0=Pred0(),
  ...
  const Predn& pn=Predn());
Effects: Constructs a composite_key_equal_to that stores copies of the equality predicates supplied.
composite_key_equal_to(const key_eq_tuple& x);
Effects: Constructs a composite_key_equal_to that stores copies of the equality predicate objects supplied in x.
const key_eq_tuple& key_eqs()const;
Returns: a constant reference to a tuple holding the equality predicate objects internally stored by the composite_key_equal_to.
key_eq_tuple& key_eqs();
Returns: a reference to a tuple holding the equality predicate objects internally stored by the composite_key_equal_to.
template<typename CompositeKey1,typename CompositeKey2>
bool operator()(
  const composite_key_result<CompositeKey1> & x,
  const composite_key_result<CompositeKey2> & y)const;
template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator()(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y)const;
template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator()(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y)const;
Requires: length(x)==length(y). The expressions key_eqs().get<i>()(xi,yi) and key_eqs().get<i>()(yi,xi) are valid for all i in [0,length(x)).
Returns: true if and only
key_eqs().get<i>()(xi,yi) for all i in [0,length(x)).
Complexity: No more key extraction operations and comparisons are performed than those necessary for the evaluation of the expression above, starting at i==0. The evaluation is short-circuited as soon as the result is determined to be false.

Class template composite_key_result_equal_to

composite_key_result_equal_to acts as a particularization of composite_key_equal_to where all the comparison predicates supplied are instantiations of std::equal_to.

template<typename CompositeKeyResult>
struct composite_key_result_equal_to
{
  typedef CompositeKeyResult  first_argument_type;
  typedef first_argument_type second_argument_type;
  typedef bool                result_type;

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const composite_key_result<CompositeKey1> & x,
    const composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const composite_key_result<CompositeKey>& x,
    const tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const tuple<Value0,...,Valuen>& x,
    const composite_key_result<CompositeKey>& y)const;
};

CompositeKeyResult must be an instantiation of composite_key_result for some type composite_key<KeyFromValue0,...,KeyFromValuen>. composite_key_result_equal_to<CompositeKeyResult>::operator() is then equivalent to composite_key_equal_to<Pred0,...,Predn>::operator(), taking

Predi = std::equal_to<KeyFromValuei::result_type> for all i = 0,...,n.

In addition to the requirements on Predi imposed by composite_key_equal_to, each of these types must be Default Constructible. composite_key_result_equal_to is Default Constructible and Assignable.

Specialization of std::equal_to for composite_key results

std::equal_to<CompositeKeyResult>, for CompositeKeyResult being an instantiation of composite_key_result, has the same interface and functionality as composite_key_result_equal_to<CompositeKeyResult>.

namespace std{

template<typename CompositeKey>
struct equal_to<boost::multi_index::composite_key_result<CompositeKey> >
{
  typedef
    boost::multi_index::composite_key_result<
      CompositeKey>                           first_argument_type;
  typedef first_argument_type                 second_argument_type;
  typedef bool                                result_type;

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey1> & x,
    const boost::multi_index::composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey>& x,
    const boost::tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const boost::tuple<Value0,...,Valuen>& x,
    const boost::multi_index::composite_key_result<CompositeKey>& y)const;
};

} // namespace std

Comparison

Class template composite_key_compare

composite_key_compare compares composite_key_result instantiations between them and with tuples of values using an internally stored collection of elementary comparison predicates.

template<typename Compare0,...,typename Comparen>
struct composite_key_compare
{
  typedef tuple<Compare0,...,Comparen> key_comp_tuple;

  composite_key_compare(
    const Compare0& c0=Compare0(),
    ...
    const Comparen& cn=Comparen());

  composite_key_compare(const key_comp_tuple& x);

  const key_comp_tuple& key_comps()const;
  key_comp_tuple&       key_comps();

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const composite_key_result<CompositeKey1> & x,
    const composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const composite_key_result<CompositeKey>& x,
    const tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const tuple<Value0,...,Valuen>& x,
    const composite_key_result<CompositeKey>& y)const;

  template<typename CompositeKey,typename Value>
  bool operator()(
    const composite_key_result<CompositeKey>& x,const Value& y)const;

  template<typename Value,typename CompositeKey>
  bool operator()(
    const Value& x,const composite_key_result<CompositeKey>& y)const;
};

Compare0, ... , Comparen are the types of the comparison predicates stored by composite_key_compare. Each of these types must be a Binary Predicate. At least a comparison predicate must be provided. The maximum number of comparison predicates of a composite_key_compare instantiation is implementation defined. composite_key_compare is Assignable. It is also Default Constructible if each Comparei is Default Constructible.

Note that formally it is not required that the Comparei types behave as comparison predicates in any definite way. However, the semantics of composite_key_compare is well defined if this is the case, as explained in the section on the semantics of composite_key_result.

Notation

In what follows we use the same notation introduced for composite_key_result.

composite_key_compare members

composite_key_compare(
  const Compare0& c0=Compare0(),
  ...
  const Comparen& cn=Comparen());
Effects: Constructs a composite_key_compare that stores copies of the comparison predicates supplied.
composite_key_compare(const key_comp_tuple& x);
Effects: Constructs a composite_key_compare that stores copies of the comparison predicate objects supplied in x.
const key_comp_tuple& key_comps()const;
Returns: a constant reference to a tuple holding the comparison predicate objects internally stored by the composite_key_compare.
key_comp_tuple& key_comps();
Returns: a reference to a tuple holding the comparison predicate objects internally stored by the composite_key_compare.
template<typename CompositeKey1,typename CompositeKey2>
bool operator()(
  const composite_key_result<CompositeKey1> & x,
  const composite_key_result<CompositeKey2> & y)const;
template<typename CompositeKey,typename Value0,...,typename Valuen>
bool operator()(
  const composite_key_result<CompositeKey>& x,
  const tuple<Value0,...,Valuen>& y)const;
template<typename Value0,...,typename Valuen,typename CompositeKey>
bool operator()(
  const tuple<Value0,...,Valuen>& x,
  const composite_key_result<CompositeKey>& y)const;
Requires: The expressions key_comps().get<i>()(xi,yi) and key_comps().get<i>()(yi,xi) are valid for all i in [0,min(length(x),length(y))).
Returns: true if and only if there exists some j in the range [0,min(length(x),length(y))) such that
!key_comps().get<i>()(xi,yi) && !key_comps().get<i>()(yi,xi) for all i in [0,j),
 key_comps().get<j>()(xj,yj).
Complexity: No more key extraction operations and comparisons are performed than those necessary for the evaluation of the expression above, starting at i==0. The evaluation is short-circuited as soon as the result is determined to be false.
template<typename CompositeKey,typename Value>
bool operator()(
  const composite_key_result<CompositeKey>& x,const Value& y)const;
Effects:
return operator()(x,make_tuple(cref(y)));

template<typename Value,typename CompositeKey>
bool operator()(
  const Value& x,const composite_key_result<CompositeKey>& y)const;
Effects:
return operator()(make_tuple(cref(x)),y);

Class template composite_key_result_less

composite_key_result_less acts as a particularization of composite_key_compare where all the comparison predicates supplied are instantiations of std::less.

template<typename CompositeKeyResult>
struct composite_key_result_less
{
  typedef CompositeKeyResult  first_argument_type;
  typedef first_argument_type second_argument_type;
  typedef bool                result_type;

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const composite_key_result<CompositeKey1> & x,
    const composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const composite_key_result<CompositeKey>& x,
    const tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const tuple<Value0,...,Valuen>& x,
    const composite_key_result<CompositeKey>& y)const;

  template<typename CompositeKey,typename Value>
  bool operator()(
    const composite_key_result<CompositeKey>& x,const Value& y)const;

  template<typename Value,typename CompositeKey>
  bool operator()(
    const Value& x,const composite_key_result<CompositeKey>& y)const;
};

CompositeKeyResult must be an instantiation of composite_key_result for some type composite_key<KeyFromValue0,...,KeyFromValuen>. composite_key_result_less<CompositeKeyResult>::operator() is then equivalent to composite_key_compare<Compare0,...,Comparen>::operator(), taking

Comparei = std::less<KeyFromValuei::result_type> for all i = 0,...,n.

In addition to the requirements on Comparei imposed by composite_key_compare, each of these types must be Default Constructible. composite_key_result_less is Default Constructible and Assignable.

Class template composite_key_result_greater

composite_key_result acts as a particularization of composite_key_compare where all the comparison predicates supplied are instantiations of std::greater.

template<typename CompositeKeyResult>
struct composite_key_result_greater
{
  typedef CompositeKeyResult  first_argument_type;
  typedef first_argument_type second_argument_type;
  typedef bool                result_type;

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const composite_key_result<CompositeKey1> & x,
    const composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const composite_key_result<CompositeKey>& x,
    const tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const tuple<Value0,...,Valuen>& x,
    const composite_key_result<CompositeKey>& y)const;

  template<typename CompositeKey,typename Value>
  bool operator()(
    const composite_key_result<CompositeKey>& x,const Value& y)const;

  template<typename Value,typename CompositeKey>
  bool operator()(
    const Value& x,const composite_key_result<CompositeKey>& y)const;
};

CompositeKeyResult must be an instantiation of composite_key_result for some type composite_key<KeyFromValue0,...,KeyFromValuen>. composite_key_result_greater<CompositeKeyResult>::operator() is then equivalent to composite_key_compare<Compare0,...,Comparen>::operator(), taking

Comparei = std::greater<KeyFromValuei::result_type> for all i = 0,...,n.

In addition to the requirements on Comparei imposed by composite_key_compare, each of these types must be Default Constructible. composite_key_result_greater is Default Constructible and Assignable.

Specialization of std::less for composite_key results

std::less<CompositeKeyResult>, for CompositeKeyResult being an instantiation of composite_key_result, has the same interface and functionality as composite_key_result_less<CompositeKeyResult>.

namespace std{

template<typename CompositeKey>
struct less<boost::multi_index::composite_key_result<CompositeKey> >
{
  typedef
    boost::multi_index::composite_key_result<
      CompositeKey>                           first_argument_type;
  typedef first_argument_type                 second_argument_type;
  typedef bool                                result_type;

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey1> & x,
    const boost::multi_index::composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey>& x,
    const boost::tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const boost::tuple<Value0,...,Valuen>& x,
    const boost::multi_index::composite_key_result<CompositeKey>& y)const;

  template<typename CompositeKey,typename Value>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey>& x,
    const Value& y)const;

  template<typename Value,typename CompositeKey>
  bool operator()(
    const Value& x,
    const boost::multi_index::composite_key_result<CompositeKey>& y)const;
};

} // namespace std

Specialization of std::greater for composite_key results

std::greater<CompositeKeyResult>, for CompositeKeyResult being an instantiation of composite_key_result, has the same interface and functionality as composite_key_result_greater<CompositeKeyResult>.

namespace std{

template<typename CompositeKey>
struct greater<boost::multi_index::composite_key_result<CompositeKey> >
{
  typedef
    boost::multi_index::composite_key_result<
      CompositeKey>                           first_argument_type;
  typedef first_argument_type                 second_argument_type;
  typedef bool                                result_type;

  template<typename CompositeKey1,typename CompositeKey2>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey1> & x,
    const boost::multi_index::composite_key_result<CompositeKey2> & y)const;

  template<typename CompositeKey,typename Value0,...,typename Valuen>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey>& x,
    const boost::tuple<Value0,...,Valuen>& y)const;

  template<typename Value0,...,typename Valuen,typename CompositeKey>
  bool operator()(
    const boost::tuple<Value0,...,Valuen>& x,
    const boost::multi_index::composite_key_result<CompositeKey>& y)const;

  template<typename CompositeKey,typename Value>
  bool operator()(
    const boost::multi_index::composite_key_result<CompositeKey>& x,
    const Value& y)const;

  template<typename Value,typename CompositeKey>
  bool operator()(
    const Value& x,
    const boost::multi_index::composite_key_result<CompositeKey>& y)const;
};

} // namespace std

Hashing

Class template composite_key_hash

composite_key_hash produces hash values for composite_key_result instantiations based on a collection of elementary hash functors.

template<typename Hash0,...,typename Hashn>
struct composite_key_hash
{
  typedef tuple<Hash0,...,Hashn> key_hasher_tuple;

  composite_key_hash(
    const Hash0& h0=Hash0(),
    ...
    const Hashn& hn=Hashn());

  composite_key_hash(const key_hasher_tuple& x);

  const key_hasher_tuple& key_hash_functions()const;
  key_hasher_tuple&       key_hash_functions();

  template<typename CompositeKey>
  std::size_t operator()(
    const composite_key_result<CompositeKey>& x)const;

  template<typename Value0,...,typename Valuen>
  std::size_t operator()(
    const tuple<Value0,...,Valuen>& x)const;
};

Hash0, ... , Hashn are the types of the hash functors stored by composite_key_hash. Each of these types must be a Unary Function returning a value of type std::size_t in the range [0, std::numeric_limits<std::size_t>::max()). At least a hash functor must be provided. The maximum number of hash functors of a composite_key_hash instantiation is implementation defined. composite_key_hash is Assignable . It is also Default Constructible if each Hashi is Default Constructible.

Notation

In what follows we use the same notation introduced for composite_key_result.

composite_key_hash members

composite_key_hash(
  const Hash0& h0=Hash0(),
  ...
  const Hashn& hn=Hashn());
Effects: Constructs a composite_key_hash that stores copies of the hash functors supplied.
composite_key_hash(const key_hasher_tuple& x);
Effects: Constructs a composite_key_hash that stores copies of the hash functors supplied in x.
const key_hasher_tuple& key_hash_functions()const;
Returns: a constant reference to a tuple holding the hash functors internally stored by the composite_key_hash.
key_hasher_tuple& key_hash_functions();
Returns: a reference to a tuple holding the hash functors internally stored by the composite_key_hash.
template<typename CompositeKey>
bool operator()(
  const composite_key_result<CompositeKey>& x)const;
template<typename Value0,...,typename Valuen>
bool operator()(
  const tuple<Value0,...,Valuen>& x)const;
Requires: length(x)==length(key_hash_functions()). The expression key_hash_functions().get<i>()(xi) is valid for all i in [0,length(x)).
Returns: A value in the range [0, std::numeric_limits<std::size_t>::max()) that solely depends on the numerical tuple
(key_hash_functions().get<0>()(x0), ... ,
key_hash_functions().get<N>()(xN)),
with N=length(x)-1.

Class template composite_key_result_hash

composite_key_result_hash acts as a particularization of composite_key_hash where all the hash functors supplied are instantiations of boost::hash.

template<typename CompositeKeyResult>
struct composite_key_result_hash
{
  typedef CompositeKeyResult argument_type;
  typedef std::size_t        result_type;

  template<typename CompositeKey>
  std::size_t operator()(
    const composite_key_result<CompositeKey>& x)const;

  template<typename Value0,...,typename Valuen>
  std::size_t operator()(
     const tuple<Value0,...,Valuen>& x)const;
};

CompositeKeyResult must be an instantiation of composite_key_result for some type composite_key<KeyFromValue0,...,KeyFromValuen>. composite_key_result_hash<CompositeKeyResult>::operator() is then equivalent to composite_key_hash<Hash0,...,Hashn>::operator(), taking

Hashi = boost::hash<KeyFromValuei::result_type> for all i = 0,...,n.

In addition to the requirements on Hashi imposed by composite_key_hash, each of these types must be Default Constructible. composite_key_result_hash is Default Constructible and Assignable.

Specialization of boost::hash for composite_key results

boost::hash<CompositeKeyResult>, for CompositeKeyResult being an instantiation of composite_key_result, has the same interface and functionality as composite_key_result_hash<CompositeKeyResult>.

namespace boost{

template<typename CompositeKey>
struct hash<multi_index::composite_key_result<CompositeKey> >
{
  typedef multi_index::composite_key_result<CompositeKey> argument_type;
  typedef std::size_t                                     result_type;

  template<typename CompositeKey>
  std::size_t operator()(
    const multi_index::composite_key_result<CompositeKey>& x)const;

  template<typename Value0,...,typename Valuen>
  std::size_t operator()(
    const tuple<Value0,...,Valuen>& x)const;
};

} // namespace boost

Semantics of composite_key_result

The design of equality, comparison and hash operations for composite_key_result objects is based on the following rationale: a composite_key_result is regarded as a "virtual" tuple, each of its elements being the result of the corresponding elementary key extractor. Accordingly, any given operation resolves to a combination of the corresponding elementwise operations. This mapping preserves the fundamental properties of the elementary operations involved; for instance, it defines a true equivalence relation if the basic predicates induce equivalence relations themselves. We can state these facts in a formal way as follows.

Consider an instantiation of composite_key_equal_to with types Pred0, ... , Predn such that each Predi induces an equivalence relation on a certain type Ti, and let CompositeKey be a type of the form composite_key<Value,KeyFromValue0,...,KeyFromValuej>, with j <= n, such that

KeyFromValuei::result_type = Ti, for all i = 0,...,j.
Then, composite_key_equal_to induces an equivalence relation on elements of type composite_key_result<CompositeKey>; such two objects are equivalent if all its elementary key extractor values are also equivalent. Additionally, given an instantiation composite_key_hash<Hash0,...,Hashj>, the following types are Compatible Keys of (composite_key_hash, composite_key_equal_to) with respect to composite_key_result<CompositeKey>:
tuple<Q0,...,Qj>,
composite_key_result<composite_key<K0,...,Kj> >, with Ki::result_type = Qi for all i = 0,...,j.
provided that each Qi is either Ti or a Compatible Ke y of (Hashi, Predi).

As for comparison, consider an instantiation of composite_key_compare with types Compare0, ... , Comparen such that each Comparei is a Strict Weak Ordering on the type Ti. Then, for a CompositeKey type defined in the same manner as above, composite_key_compare is a Strict Weak Ordering on elements of type composite_key_result<CompositeKey>, and the order induced is lexicographical. Also, the following types are Compatible Keys of composite_key_compare with respect to composite_key_result<CompositeKey>:

tuple<Q0,...,Qk>, k <= n
composite_key_result<composite_key<K0,...,Kk> >, with Ki::result_type = Qi for all i = 0,...,k.
provided that each Qi is either Ti or a Compatible Key of Comparei. In this case, the comparison is done lexicographically only on the first 1+min(j,k) elements.

Analogous properties hold for the equality and comparison operators of composite_key_result. Note, however, that equality is only defined for objects of the same length, whilst comparison takes the minimum length of the operands considered. Therefore, the equivalence classes induced by x==y are subsets of those associated to !(x<y)&&!(y<x).

Revised July 13th 2006
© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Documentation - multi_index_container reference

Boost.MultiIndex multi_index_container reference

  • Header "boost/multi_index_container_fwd.hpp" synopsis

  • Header "boost/multi_index_container.hpp" synopsis

    • Class template multi_index_container

      • Complexity

      • Instantiation types

      • Nested types

      • Nested class templates

      • Constructors, copy and assignment

      • Index retrieval operations

      • Projection operations

      • Serialization

Header "boost/multi_index_container_fwd.hpp" synopsis

namespace boost{

namespace multi_index{

template<
  typename Value,
  typename IndexSpecifierList=indexed_by<ordered_unique<identity<Value> > >,
  typename Allocator=std::allocator<Value> >
class multi_index_container;

} // namespace boost::multi_index

using multi_index::multi_index_container;

} // namespace boost

multi_index_container_fwd.hpp forward declares the class template multi_index_container and specifies its default parameters.

Header "boost/multi_index_container.hpp" synopsis

namespace boost{

namespace multi_index{

template<typename Value,typename IndexSpecifierList,typename Allocator>
class multi_index_container;

// multi_index_container associated global class templates:

template<typename MultiIndexContainer,int N> struct nth_index;
template<typename MultiIndexContainer,typename Tag> struct index;
template<typename MultiIndexContainer,int N> struct nth_index_iterator;
template<typename MultiIndexContainer,int N> struct nth_index_const_iterator;
template<typename MultiIndexContainer,typename Tag> struct index_iterator;
template<typename MultiIndexContainer,typename Tag> struct index_const_iterator;

// multi_index_container global functions for index retrieval:

template<
  int N,typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type&
get(multi_index_container<Value,IndexSpecifierList,Allocator>& m);

template<
  int N,typename Value,typename IndexSpecifierList,typename Allocator
>
const typename nth_index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type&
get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m);

template<
  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
>
typename index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type&
get(multi_index_container<Value,IndexSpecifierList,Allocator>& m);

template<
  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
>
const typename index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type&
get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m);

// multi_index_container global functions for projection of iterators:

template<
  int N,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type
project(
  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

template<
  int N,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index_const_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type
project(
  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

template<
  typename Tag,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename index_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type
project(
  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

template<
  typename Tag,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename index_const_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type
project(
  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it);

// comparison:

// OP is any of ==,<,!=,>,>=,<=

template<
  typename Value1,typename IndexSpecifierList1,typename Allocator1,
  typename Value2,typename IndexSpecifierList2,typename Allocator2
>
bool operator OP(
  const multi_index_container<Value1,IndexSpecifierList1,Allocator1>& x,
  const multi_index_container<Value2,IndexSpecifierList2,Allocator2>& y);

// specialized algorithms:

template<typename Value,typename IndexSpecifierList,typename Allocator>
void swap(
  multi_index_container<Value,IndexSpecifierList,Allocator>& x,
  multi_index_container<Value,IndexSpecifierList,Allocator>& y);

} // namespace boost::multi_index

using multi_index::multi_index_container;
using multi_index::get;
using multi_index::project;

} // namespace boost

Class template multi_index_container

This is the main component of Boost.MultiIndex. A multi_index_container is a container class template holding a compile-time user-defined list of indices. These indices provide different interfaces for the management of the elements of the multi_index_container. By itself, multi_index_container only provides basic functionality for construction and for access to the indices held.

A multi_index_container type is instantiated with the type of the elements contained and a non-empty MPL Forward Sequence specifying which indices conform the class.

For convenience of use, all public methods and types of the first index specified are inherited by multi_index_container. This also includes global operators and functions associated with the index (vg. comparison and swap.)

template<
  typename Value,
  typename IndexSpecifierList=indexed_by<ordered_unique<identity<Value> > >,
  typename Allocator=std::allocator<Value> >
class multi_index_container
{
public:

  // types:

  typedef implementation defined   ctor_args_list;
  typedef implementation defined   index_specifier_type_list;
  typedef implementation defined   index_type_list;
  typedef implementation defined   iterator_type_list;
  typedef implementation defined   const_iterator_type_list;
  typedef Allocator                allocator_type;

  // nested class templates:

  template<int N>
  struct nth_index                {typedef implementation defined type;};
  template<typename Tag>
  struct index                    {typedef implementation defined type;};
  template<int N>
  struct nth_index_iterator       {typedef implementation defined type;};
  template<int N>
  struct nth_index_const_iterator {typedef implementation defined type;};
  template<typename Tag>
  struct index_iterator           {typedef implementation defined type;};
  template<typename Tag>
  struct index_const_iterator     {typedef implementation defined type;};

  // construct/copy/destroy:

  explicit multi_index_container(
    const ctor_args_list& args_list=ctor_args_list(),
    const allocator_type& al=allocator_type());
  template<typename InputIterator>
  multi_index_container(
    InputIterator first,InputIterator last,
    const ctor_args_list& args_list=ctor_args_list(),
    const allocator_type& al=allocator_type());
  multi_index_container(
    const multi_index_container<Value,IndexSpecifierList,Allocator>& x);

  ~multi_index_container();

  multi_index_container<Value,IndexSpecifierList,Allocator>& operator=(
    const multi_index_container<Value,IndexSpecifierList,Allocator>& x);

  allocator_type get_allocator()const;

  // retrieval of indices

  template<int N> typename nth_index<N>::type& get();
  template<int N> const typename nth_index<N>::type& get()const;
  template<typename Tag> typename index<Tag>::type& get()
  template<typename Tag> const typename index<Tag>::type& get()const;

  // projection of iterators

  template<int N,typename IteratorType>
    typename nth_index_iterator<N>::type project(IteratorType it);
  template<int N,typename IteratorType>
    typename nth_index_const_iterator<N>::type project(IteratorType it)const;
  template<typename Tag,typename IteratorType>
    typename index_iterator<Tag>::type project(IteratorType it);
  template<typename Tag,typename IteratorType>
    typename index_const_iterator<Tag>::type project(IteratorType it)const;
};

// multi_index_container associated global class templates:

template<typename MultiIndexContainer,int N> struct nth_index
{
  typedef typename MultiIndexContainer::nth_index<N>::type type;
};

template<typename MultiIndexContainer,typename Tag> struct index
{
  typedef typename MultiIndexContainer::index<Tag>::type type;
};

template<typename MultiIndexContainer,int N> struct nth_index_iterator
{
  typedef typename MultiIndexContainer::nth_index_iterator<N>::type type;
};

template<typename MultiIndexContainer,int N> struct nth_index_const_iterator
{
  typedef typename MultiIndexContainer::nth_index_const_iterator<N>::type type;
};

template<typename MultiIndexContainer,typename Tag> struct index_iterator
{
  typedef typename MultiIndexContainer::index_iterator<Tag>::type type;
};

template<typename MultiIndexContainer,typename Tag> struct index_const_iterator
{
  typedef typename MultiIndexContainer::index_const_iterator<Tag>::type type;
};

// multi_index_container global functions for index retrieval:

template<
  int N,typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type&
get(multi_index_container<Value,IndexSpecifierList,Allocator>& m)
{
  return m.get<N>();
}

template<
  int N,typename Value,typename IndexSpecifierList,typename Allocator
>
const typename nth_index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type&
get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m)
{
  return m.get<N>();
}

template<
  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
>
typename index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type&
get(multi_index_container<Value,IndexSpecifierList,Allocator>& m)
{
  return m.get<Tag>();
}

template<
  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
>
const typename index<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type&
get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m)
{
  return m.get<Tag>();
}

// multi_index_container global functions for projection of iterators:

template<
  int N,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type
project(
  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it)
{
  return m.project<N>(it);
}

template<
  int N,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename nth_index_const_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,N
>::type
project(
  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it)
{
  return m.project<N>(it);
}

template<
  typename Tag,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename index_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type
project(
  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it)
{
  return m.project<Tag>(it);
}

template<
  typename Tag,typename IteratorType,
  typename Value,typename IndexSpecifierList,typename Allocator
>
typename index_const_iterator<
  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
>::type
project(
  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
  IteratorType it)
{
  return m.project<Tag>(it);
}

// comparison:

// OP is any of ==,<,!=,>,>=,<=

template<
  typename Value1,typename IndexSpecifierList1,typename Allocator1,
  typename Value2,typename IndexSpecifierList2,typename Allocator2
>
bool operator OP(
  const multi_index_container<Value1,IndexSpecifierList1,Allocator1>& x,
  const multi_index_container<Value2,IndexSpecifierList2,Allocator2>& y)
  {
    return get<0>(x) OP get<0>(y);
  }

// specialized algorithms:

template<typename Value,typename IndexSpecifierList,typename Allocator>
void swap(
  multi_index_container<Value,IndexSpecifierList,Allocator>& x,
  multi_index_container<Value,IndexSpecifierList,Allocator>& y)
  {
    x.swap(y);
  }

} // namespace boost::multi_index

} // namespace boost

Complexity

In the descriptions of operations of multi_index_container, adopt the scheme outlined in the complexity signature section.

Instantiation types

multi_index_container is instantiated with the following types:

  1. Value is the Assignable type of the elements contained.

  2. IndexSpecifierList specifies the indices that the multi_index_container is composed of. It must be a non-empty MPL Forward Sequence (and, preferrably, an MPL Random Access Sequence) of index specifiers. For syntactic convenience, the indexed_by MPL sequence can be used.

  3. Allocator must comply with the C++ requirements for allocators [lib.allocator.requirements].

Indices of a given multi_index_container instantiation cannot have duplicate tags, either within a single index or in two different indices.

Nested types

ctor_args_list

Although the exact definition of ctor_args_list is implementation defined, from the user point of view this type can be treated as equivalent to ::boost::tuple<C0,...,CI-1>, where Ci is the ctor_args type of the i-th index held by the multi_index_container, in the same order as they were specified. Strictly speaking, there is an implicit conversion from const ::boost::tuple<C0,...,CI-1>& to const ctor_args_list&. This type is used for providing the construction arguments of the indices of the multi_index_container. ctor_args_list is Default Constructible, provided that all ctor_args types involved are default constructible.

index_specifier_type_list

Same type as IndexSpecifierList.

index_type_list

Model of MPL Random Access Sequence and MPL Extensible Sequence containing the types of the indices held by the multi_index_container, in the same order as they were specified.

iterator_type_list

Model of MPL Random Access Sequence and MPL Extensible Sequence containing the types of the iterators of the indices held by the multi_index_container, in the same order as they were specified.

const_iterator_type_list

Model of MPL Random Access Sequence and MPL Extensible Sequence containing the types of the constant iterators of the indices held by the multi_index_container, in the same order as they were specified.

Nested class templates

template<int N> struct nth_index

nth_index<N>::type yields the type of the N-th (0-based) index held by the multi_index_container, in the same order as they were specified.
Requires: 0 <= N < I.

template<typename Tag> struct index

index<Tag>::type yields the type of the index which has Tag as an associated tag type.
Requires: Some index of the multi_index_container has Tag as an associated tag type.

template<int N> struct nth_index_iterator

nth_index_iterator<N>::type is equivalent to nth_index<N>::type::iterator.

template<int N> struct nth_index_const_iterator

nth_index_const_iterator<N>::type is equivalent to nth_index<N>::type::const_iterator.

template<typename Tag> struct index_iterator

index_iterator<Tag>::type is equivalent to index<Tag>::type::iterator.

template<typename Tag> struct index_const_iterator

index_const_iterator<Tag>::type is equivalent to index<Tag>::type::const_iterator.

Constructors, copy and assignment

explicit multi_index_container(
 &nbs  const allocator_type& al=allocator_type());

Effects: Constructs an empty multi_index_container using the specified argument list and allocator.
Complexity: Constant.

template<typename InputIterator>
multi_index_container(
  InputIterator first,InputIterator last,
  const ctor_args_list& comp=ctor_a  const allocator_type& al=allocator_type());

Requires: InputIterator is a model of Input Iterator over elements of type Value or a type convertible to Value. last is reachable from first.
Effects: Constructs and empty multi_index_container using the specified argument list and allocator and fills it with the elements in the range [first,last). Insertion of each element may or may not succeed depending on the acceptance by all the indices of the multi_index_container.
Complexity: O(m*H(m)), where m is the number of elements in [first,last).

mu  const multi_index_container<Value,IndexSpecifierList,Allocator>& x);

Effects: Constructs a copy of x, copying its elements as well as its internal objects (key extractors, comparison objects, allocator.)
Postconditions: *this==x. The order on every index of the multi_index_container is preserved as well.
Complexity: O(x.size()*log(x.size()) + C(x.size())).

~multi_index_container()

Effects: Destroys the multi_index_container and all the elements contained. The order in which the elements are destroyed is not specified.
Complexity: O(n).

multi_index_container<Value,Index  const multi_index_container<Value,IndexSpecifierList,Allocator>& x);

Replaces the elements and internal objects of the multi_index_container with copies from x.
 Postconditions: *this==x. The order on every index of the multi_index_container is preserved as well.
Returns: *this.
Complexity: O(n + x.size()*log(x.size()) + C(x.size())).
Exception safety: Strong, provided the copy and assignment operations of the types of ctor_args_list do not throw.

allocator_type get_allocator()const;

Returns a copy of the allocator_type object used to construct the multi_index_container.
Complexity: Constant.

Index retrieval operations

template<int N> typename nth_index<N>::type& get();

Requires: 0 <= N < I.
Effects: Returns a reference to the nth_index<N>::type index held by *this.
Complexity: Constant.
Exception safety: nothrow.

template<int N> const typename nth_index<N>::type& get()const;

Requires: 0 <= N < I.
Effects: Returns a const reference to the nth_index<N>::type index held by *this.
Complexity: Constant.
Exception safety: nothrow.

template<typename Tag> typename index<Tag>::type& get()

Requires: Tag is such that index<Tag>::type is valid.
Effects: Returns a reference to the index<Tag>::type index held by *this.
Complexity: Constant.
Exception safety: nothrow.

template<typename Tag> const typename index<Tag>::type& get()const;

Requires: Tag is such that index<Tag>::type is valid.
Effects: Returns a const reference to the index<Tag>::type index held by *this.
Complexity: Constant.
Exception safety: nothrow.

Projection operations

Given a multi_index_container with indices i1 and i2, we say than an i1-iterator it1 and an i2-iterator it2 are equivalent if:

  • it1==i1.end() AND it2==i2.end(),

  • OR it1 and it2 point to the same element.

template<int N,typename IteratorType>
typename nth_index_iterator<N>::type project(IteratorType it);

Requires: 0 <= N < I. IteratorType belongs to iterator_type_list. it is a valid iterator of some index of *this (i.e. does not refer to some other multi_index_container.)
Effects: Returns an nth_index_iterator<N>::type iterator equivalent to it.
Complexity: Constant.
 Exception safety: nothrow.

template<int N,typename IteratorType>
typename nth_index_const_iterator<N>::type project(IteratorType it)const;

Requires: 0 <= N < I. IteratorType belongs to const_iterator_type_list or iterator_type_list. it is a valid (constant or non-constant) iterator of some index of *this (i.e. does not refer to some other multi_index_container.)
Effects: Returns an nth_index_const_iterator<N>::type iterator equivalent to it.
Complexity: Constant.
Exception safety: nothrow.

template<typename Tag,typename IteratorType>
typename index_iterator<Tag>::type project(IteratorType it);

Requires: Tag is such that index_iterator<Tag>::type is valid. IteratorType belongs to iterator_type_list. it is a valid iterator of some index of *this (i.e. does not refer to some other multi_index_container.)
Effects: Returns an index_iterator<Tag>::type iterator equivalent to it.
Complexity: Constant.
Exception safety: nothrow.

template<typename Tag,typename IteratorType>
typename index_const_iterator<Tag>::type project(IteratorType it)const;

Requires: Tag is such that index_const_iterator<Tag>::type is valid. IteratorType belongs to const_iterator_type_list or iterator_type_list. it is a valid (constant or non-constant) iterator of some index of *this (i.e. does not refer to some other multi_index_container.)
Effects: Returns an index_const_iterator<Tag>::type iterator equivalent to it.
Complexity: Constant.
Exception safety: nothrow.

Serialization

multi_index_containers can be archived/retrieved by means of Boost.Serialization. Boost.MultiIndex does not expose a public serialization interface, as this is provided by Boost.Serialization itself. Both regular and XML archives are supported.

Each of the indices comprising a given multi_index_container contributes its own preconditions as well as guarantees on the retrieved containers. In describing these, the following concepts are used. A type T is serializable (resp. XML-serializable) if any object of type T can be saved to an output archive (XML archive) and later retrieved from an input archive (XML archive) associated to the same storage. If x' of type T is loaded from the serialization information saved from another object x, say that x' is a restored copy of x. Given a Binary Predicate Pred over (T, T), and objects p and q of type Pred, say that q is serialization-compatible with p if

p(x,y) == q(x',y')

for every x and y of type T and x' and y' being restored copies of x and y, respectively.

Operation: saving of a multi_index_container m to an output archive (XML archive) ar.

Requires: Value is serializable (XML-serializable). Additionally, each of the indices of m can impose another requirements.
Exception safety: Strong with respect to m. If an exception is thrown, ar may be left in an inconsistent state.

Operation: loading of a multi_index_container m' from an input archive (XML archive) ar.

Requires: Value is serializable (XML-serializable). Additionally, each of the indices of m' can impose another requirements.
Exception safety: Basic. If an exception is thrown, ar may be left in an inconsistent state.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Ordered indices reference

Contents

  • Header "boost/multi_index/ordered_index_fwd.hpp" synopsis
  • Header "boost/multi_index/ordered_index.hpp" synopsis
    • Index specifiers ordered_unique and ordered_non_unique
    • Ordered indices
      • Complexity signature
      • Instantiation types
      • Constructors, copy and assignment
      • Modifiers
      • Observers
      • Set operations
      • Range operations
      • Serialization

Header "boost/multi_index/ordered_index_fwd.hpp" synopsis

namespace boost{

namespace multi_index{

// index specifiers ordered_unique and ordered_non_unique

template<consult ordered_unique reference for arguments>
struct ordered_unique;
template<consult ordered_non_unique reference for arguments>
struct ordered_non_unique;

// indices

namespace detail{

template<implementation defined> class index name is implementation defined;

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

ordered_index_fwd.hpp provides forward declarations for index specifiers ordered_unique and ordered_non_unique and their associated ordered index classes.

Header "boost/multi_index/ordered_index.hpp" synopsis

namespace boost{

namespace multi_index{

// index specifiers ordered_unique and ordered_non_unique

template<consult ordered_unique reference for arguments>
struct ordered_unique;
template<consult ordered_non_unique reference for arguments>
struct ordered_non_unique;

// indices

namespace detail{

template<implementation defined> class index class name implementation defined;

// index comparison:

// OP is any of ==,<,!=,>,>=,<=

template<arg set 1,arg set 2>
bool operator OP(
  const index class name<arg set 1>& x,const index class name<arg set 2>& y);

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

Index specifiers ordered_unique and ordered_non_unique

These index specifiers allow for insertion of ordered indices without and with allowance of duplicate elements, respectively. The syntax of ordered_unique and ordered_non_unique coincide, thus describe them in a grouped manner. ordered_unique and ordered_non_unique can be instantiated in two different forms, according to whether a tag list for the index is provided or not:

template<
  typename KeyFromValue,
  typename Compare=std::less<KeyFromValue::result_type>
>
struct (ordered_unique | ordered_non_unique);

template<
  typename TagList,
  typename KeyFromValue,
  typename Compare=std::less<KeyFromValue::result_type>
>
struct (ordered_unique | ordered_non_unique);

If provided, TagList must be an instantiation of the class template tag. The template arguments are used by the corresponding index implementation, refer to the ordered indices reference section for further explanations on their acceptable type values.

Ordered indices

An ordered index provides a set-like interface to the underlying heap of elements contained in a multi_index_container. An ordered index is particularized according to a given Key Extractor that retrieves keys from elements of multi_index_container and a comparison predicate.

There are two variants of ordered indices: unique, which do not allow duplicate elements (with respect to its associated comparison predicate) and non-unique, which accept those duplicates. The interface of these two variants is the same, so they are documented together, with minor differences explicitly stated when they exist.

Except where noted, ordered indices (both unique and non-unique) are models of Sorted Associative Container and Unique Associative Container, much as std::sets are. Accordingly, validity of iterators and references to elements is preserved. Provide descriptions of those types and operations that are either not present in the concepts modeled or do not exactly conform to the requirements for these types of containers.

namespace boost{

namespace multi_index{

namespace{ implementation defined unbounded; } // see range()

namespace detail{

template<implementation defined: dependent on types Value, Allocator,
  TagList, KeyFromValue, Compare>
class name is implementation defined
{
public:
  // types:

  typedef typename KeyFromValue::result_type         key_type;
  typedef Value                                      value_type;
  typedef KeyFromValue                               key_from_value;
  typedef Compare                                    key_compare;
  typedef implementation defined                     value_compare;
  typedef tuple<key_from_value,key_compare>          ctor_args;
  typedef Allocator                                  allocator_type;
  typedef typename Allocator::reference              reference;
  typedef typename Allocator::const_reference        const_reference;
  typedef implementation defined                     iterator;
  typedef implementation defined                     const_iterator;
  typedef implementation defined                     size_type;
  typedef implementation defined                     difference_type;
  typedef typename Allocator::pointer                pointer;
  typedef typename Allocator::const_pointer          const_pointer;
  typedef equivalent to
    std::reverse_iterator<iterator>                  reverse_iterator;
  typedef equivalent to
    std::reverse_iterator<const_iterator>            const_reverse_iterator;

  // construct/copy/destroy:

  index class name& operator=(const index class name& x);

  allocator_type get_allocator()const;

  // iterators:

  iterator               begin();
  const_iterator         begin()const;
  iterator               end();
  const_iterator         end()const;
  reverse_iterator       rbegin();
  const_reverse_iterator rbegin()const;
  reverse_iterator       rend();
  const_reverse_iterator rend()const;

  // capacity:

  bool      empty()const;
  size_type size()const;
  size_type max_size()const;

  // modifiers:

  std::pair<iterator,bool> insert(const value_type& x);
  iterator insert(iterator position,const value_type& x);
  template<typename InputIterator>
  void insert(InputIterator first,InputIterator last);

  iterator  erase(iterator position);
  size_type erase(const key_type& x);
  iterator  erase(iterator first,iterator last);

  bool replace(iterator position,const value_type& x);
  template<typename Modifier> bool modify(iterator position,Modifier mod);
  template<typename Modifier> bool modify_key(iterator position,Modifier mod);

  void swap(index class name& x);
  void clear();

  // observers:

  key_from_value key_extractor()const;
  key_compare    key_comp()const;
  value_compare  value_comp()const;

  // set operations:

  template<typename CompatibleKey>
  iterator find(const CompatibleKey& x)const;
  template<typename CompatibleKey,typename CompatibleCompare>
  iterator find(
    const CompatibleKey& x,const CompatibleCompare& comp)const;

  template<typename CompatibleKey>
  size_type count(const CompatibleKey& x)const;
  template<typename CompatibleKey,typename CompatibleCompare>
  size_type count(const CompatibleKey& x,const CompatibleCompare& comp)const;

  template<typename CompatibleKey>
  iterator lower_bound(const CompatibleKey& x)const;
  template<typename CompatibleKey,typename CompatibleCompare>
  iterator lower_bound(
    const CompatibleKey& x,const CompatibleCompare& comp)const;

  template<typename CompatibleKey>
  iterator upper_bound(const CompatibleKey& x)const;
  template<typename CompatibleKey,typename CompatibleCompare>
  iterator upper_bound(
    const CompatibleKey& x,const CompatibleCompare& comp)const;

  template<typename CompatibleKey>
  std::pair<iterator,iterator> equal_range(
    const CompatibleKey& x)const;
  template<typename CompatibleKey,typename CompatibleCompare>
  std::pair<iterator,iterator> equal_range(
    const CompatibleKey& x,const CompatibleCompare& comp)const;

  // range:

  template<typename LowerBounder,typename UpperBounder>
  std::pair<iterator,iterator> range(
    LowerBounder lower,UpperBounder upper)const;
};

// index comparison:

template<arg set 1,arg set 2>
bool operator==(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin());
}

template<arg set 1,arg set 2>
bool operator<(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}

template<arg set 1,arg set 2>
bool operator!=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x==y);
}

template<arg set 1,arg set 2>
bool operator>(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return y<x;
}

template<arg set 1,arg set 2>
bool operator>=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x<y);
}

template<arg set 1,arg set 2>
bool operator<=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x>y);
}

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

Complexity signature

Here and in the descriptions of operations of ordered indices, we adopt the scheme outlined in the complexity signature section. The complexity signature of ordered indices is:

  • copying: c(n)=n*log(n),
  • insertion: i(n)=log(n),
  • hinted insertion: h(n)=1 (constant) if the hint element precedes the point of insertion, h(n)=log(n) otherwise,
  • deletion: d(n)=1 (amortized constant),
  • replacement: r(n)=1 (constant) if the element position does not change, r(n)=log(n) otherwise,
  • modifying: m(n)=1 (constant) if the element position does not change, m(n)=log(n) otherwise.

Instantiation types

Ordered indices are instantiated internally to multi_index_container and specified by means of indexed_by with index specifiers ordered_unique and ordered_non_unique. Instantiations are dependent on the following types:

  • Value from multi_index_container,
  • Allocator from multi_index_container,
  • TagList from the index specifier (if provided),
  • KeyFromValue from the index specifier,
  • Compare from the index specifier.
TagList must be an instantiation of tag. The type KeyFromValue, which determines the mechanism for extracting a key from Value, must be a model of Key Extractor from V alue. Compare is a Strict Weak Ordering on elements of KeyFromValue::result_type.

Constructors, copy and assignment

As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided.

index class name& operator=(const index class name& x);
Effects:
a=b;
where a and b are the multi_index_container objects to which *this and x belong, respectively.
Returns: *this.

Modifiers

std::pair<iterator,bool> insert(const value_type& x);
Effects: Inserts x into the multi_index_container to which the index belongs if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND insertion is allowed by all other indices of the multi_index_container.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(I(n)).
Exception safety: Strong.
iterator insert(iterator position,const value_type& x);
Requires: position is a valid iterator of the index.
Effects: Inserts x into the multi_index_container to which the index belongs if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND insertion is allowed by all other indices of the multi_index_container.
position is used as a hint to improve the efficiency of the operation.
Returns: On successful insertion, an iterator to the newly inserted element. Otherwise, an iterator to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(H(n)).
Exception safety: Strong.
template<typename InputIterator>
void insert(InputIterator first,InputIterator last);
Requires: InputIterator is a model of Input Iterator over elements of type value_type or a type convertible to value_type. first and last are not iterators into any index of the multi_index_container to which this index belongs. last is reachable from first.
Effects:
iterator hint=end();
while(first!=last)hint=insert(hint,*first++);
Complexity: O(m*H(n+m)), where m is the number of elements in [first, last).
Exception safety: Basic.
iterator erase(iterator position);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Deletes the element pointed to by position.
Returns: An iterator pointing to the element immediately following the one that was deleted, or end() if no such element exists.
Complexity: O(D(n)).
Exception safety: nothrow.
size_type erase(const key_type& x);
Effects: Deletes the elements with key equivalent to x.
Returns: Number of elements deleted.
Complexity: O(log(n) + m*D(n)), where m is the number of elements deleted.
Exception safety: Basic.
iterator erase(iterator first,iterator last);
Requires: [first,last) is a valid range of the index.
Effects: Deletes the elements in [first,last).
Returns: last.
Complexity: O(log(n) + m*D(n)), where m is the number of elements in [first,last).
Exception safety: nothrow.
bool replace(iterator position,const value_type& x);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Assigns the value x to the element pointed to by position into the multi_index_container to which the index belongs if, for the value x
  • the index is non-unique OR no other element exists (except possibly *position) with equivalent key,
  • AND replacing is allowed by all other indices of the multi_index_container.
Postconditions: Validity of position is preserved in all cases.
Returns: true if the replacement took place, false otherwise.
Complexity: O(R(n)).
Exception safety: Strong. If an exception is thrown by some user-provided operation the multi_index_container to which the index belongs remains in its original state.
template<typename Modifier> bool modify(iterator position,Modifier mod);
Requires: Modifier is a model of Unary Function accepting arguments of type value_type&. position is a valid dereferenceable iterator of the index.
Effects: Calls mod(e) where e is the element pointed to by position and rearranges *position into all the indices of the multi_index_container. Rearrangement is successful if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND rearrangement is allowed by all other indices of the multi_index_container.
If the rearrangement fails, the element is erased.
Postconditions: Validity of position is preserved if the operation succeeds.
Returns: true if the operation succeeded, false otherwise.
Complexity: O(M(n)).
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod), then the element pointed to by position is erased.
template<typename Modifier> bool modify_key(iterator position,Modifier mod);
Requires: key_from_value is a read/write Key Extractor from value_type. Modifier is a model of Unary Function accepting arguments of type key_type&. position is a valid dereferenceable iterator of the index.
Effects: Calls mod(k) where k is the key obtained by the internal K eyFromValue object of the index from the element pointed to by position, and rearranges *position into all the indices of the multi_index_container. Rearrangement is successful if
  • the index is non-unique OR no other element exists with equivalent key,
  • AND rearrangement is allowed by all other indices of the multi_index_container.
If the rearrangement fails, the element is erased.
 Postconditions: Validity of position is preserved if the operation succeeds.
Returns: true if the operation succeeded, false otherwise.
Complexity: O(M(n)).
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod), then the element pointed to by position is erased.

Observers

Apart from standard key_comp and value_comp, ordered indices have a member function for retrieving the internal key extractor used.

key_from_value key_extractor()const;
Returns a copy of the key_from_value object used to construct the index.
Complexity: Constant.

Set operations

Ordered indices provide the full lookup functionality required by Sorted Associative Containers and Unique Associative Containers, namely find, count, lower_bound, upper_bound and equal_range. Additionally, these member functions are templatized to allow for non-standard arguments, so extending the types of search operations allowed. The kind of arguments permissible when invoking the lookup member functions is defined by the following concept.

Consider a Strict Weak Ordering Compare over values of type Key. A pair of types (CompatibleKey, CompatibleCompare) is said to be a compatible extension of C ompare if

  1. CompatibleCompare is a Binary Predicate over (Key, CompatibleKey),
  2. CompatibleCompare is a Binary Predicate over (CompatibleKey, Key),
  3. if c_comp(ck,k1) then !c_comp(k1,ck),
  4. if !c_comp(ck,k1) and
    !comp(k1,k2) then
    !c_comp(ck,k2),
  5. if !c_comp(k1,ck) and !comp(k2,k1) then !c_comp(k2,ck),
for every c_comp of type CompatibleCompare, comp of type Compare, ck of type CompatibleKey and k1, k2 of type Key.

Additionally, a type CompatibleKey is said to be a compatible key of Compare if (CompatibleKey, Compare) is a compatible extension of Compare. This implies that Compare, as well as being a strict weak ordering, accepts arguments of type CompatibleKey, which usually means it has several overloads of operator().

In the context of a compatible extension or a compatible key, the expressions "equivalent", "less than" and "greater than" take on their obvious interpretations.

template<typename CompatibleKey> iterator find(const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of key_compare.
Effects: Returns a pointer to an element whose key is equivalent to x, or end() if such an element does not exist.
Complexity: O(log(n)).
template<typename CompatibleKey,typename CompatibleCompare>
iterator find(const CompatibleKey& x,const CompatibleCompare& comp)const;
Requires: (CompatibleKey, CompatibleCompare) is a compatible extension of key_compare.
Effects: Returns a pointer to an element whose key is equivalent to x, or end() if such an element does not exist.
Complexity: O(log(n)).

template<typename CompatibleKey> size_type
count(const CompatibleKey& x)const;

Requires: CompatibleKey is a compatible key of key_compare.
Effects: Returns the number of elements with key equivalent to x.
Complexity: O(log(n) + count(x)).
template<typename CompatibleKey,typename CompatibleCompare>
size_type count(const CompatibleKey& x,const CompatibleCompare& comp)const;
Requires: (CompatibleKey, CompatibleCompare) is a compatible extension of key_compare.
Effects: Returns the number of elements with key equivalent to x.
Complexity: O(log(n) + count(x,comp)).
template<typename CompatibleKey>
iterator lower_bound(const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of key_compare.
 Effects: Returns an iterator pointing to the first element with key not less than x, or end() if such an element does not exist.
Complexity: O(log(n)).
template<typename CompatibleKey,typename CompatibleCompare>
iterator lower_bound(const CompatibleKey& x,const CompatibleCompare& comp)const;
Requires: (CompatibleKey, CompatibleCompare) is a compatible extension of key_compare.
Effects: Returns an iterator pointing to the first element with key not less than x, or end() if such an element does not exist.
Complexity: O(log(n)).
template<typename CompatibleKey>
iterator upper_bound(const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of key_compare.
Effects: Returns an iterator pointing to the first element with key greater than x, or end() if such an element does not exist.
Complexity: O(log(n)).
template<typename CompatibleKey,typename CompatibleCompare>
iterator upper_bound(const CompatibleKey& x,const CompatibleCompare& comp)const;
Requires: (CompatibleKey, CompatibleCompare) is a compatible extension of key_compare.
Effects: Returns an iterator pointing to the first element with key greater than x, or end() if such an element does not exist.
Complexity: O(log(n)).
template<typename CompatibleKey>
std::pair<iterator,iterator> equal_range(
  const CompatibleKey& x)const;
Requires: CompatibleKey is a compatible key of key_compare.
Effects: Equivalent to make_pair(lower_bound(x),upper_bound(x)).
Complexity: O(log(n)).
template<typename CompatibleKey,typename CompatibleCompare>
std::pair<iterator,iterator> equal_range(
  const CompatibleKey& x,const CompatibleCompare& comp)const;
Requires: (CompatibleKey, CompatibleCompare) is a compatible extension of key_compare.
Effects: Equivalent to make_pair(lower_bound(x,comp),upper_bound(x,comp)).
Complexity: O(log(n)).

Range operations

The member function range is not defined for sorted associative containers, but ordered indices provide it as a convenient utility. A range or interval is defined by two conditions for the lower and upper bounds, which are modeled after the following concepts.

Consider a Strict Weak Ordering Compare over values of type Key. A type LowerBounder is said to be a lower bounder of Compare if

  1. LowerBounder is a Predicate over Key,
  2. if lower(k1) and !comp(k2,k1) then lower(k2),
for every lower of type LowerBounder, comp of type Compare, and k1, k2 of type Key. Similarly, an upper bounder is a type UpperBounder such that
  1. UpperBounder is a Predicate over Key,
  2. if upper(k1) and !comp(k1,k2) then upper(k2),
for every upper of type UpperBounder, comp of type Compare, and k1, k2 of type Key.

template<typename LowerBounder,typename UpperBounder>
std::pair<iterator,iterator> range(
  LowerBounder lower,UpperBounder upper)const;
Requires: LowerBounder and UpperBounder are a lower and upper bounder of key_compare, respectively.
Effects: Returns a pair of iterators pointing to the beginning and one past the end of the subsequence of elements satisfying lower and upper simultaneously. If no such elements exist, the iterators both point to the first element satisfying lower, or else are equal to end() if this latter element does not exist.
Complexity: O(log(n)).
Variants: In place of lower or upper (or both), the singular value boost::multi_index::unbounded can be provided. This acts as a predicate which all values of type key_type satisfy.

Serialization

Indices cannot be serialized on their own, but only as part of the multi_index_container into which they are embedded. In describing the additional preconditions and guarantees associated to ordered indices with respect to serialization of their embedding containers, Use the concepts defined in the multi_index_container serialization section.

Operation: saving of a multi_index_container m to an output archive (XML archive) ar.
Requires: No additional requirements to those imposed by the container.
Operation: loading of a multi_index_container m' from an input archive (XML archive) ar.
Requires: Additionally to the general requirements, value_comp() must be serialization-compatible with m.get<i>().value_comp(), where i is the position of the ordered index in the container.
Postconditions: On succesful loading, each of the elements of [begin(), end()) is a restored copy of the corresponding element in [m.get<i>().begin(), m.get<i>().end()).
Operation: saving of an iterator or const_iterator it to an output archive (XML archive) ar.
Requires: it is a valid iterator of the index. The associated multi_index_container has been previously saved.
Operation: loading of an iterator or const_iterator it' from an input archive (XML archive) ar.
Postconditions: On succesful loading, if it was dereferenceable then *it' is the restored copy of *it, otherwise it'==end().
Note: It is allowed that it be a const_iterator and the restored it' an iterator, or viceversa.
Revised July 13th 2006© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Random access indices reference

Contents

  • Header "boost/multi_index/random_access_index_fwd.hpp" synopsis
  • Header "boost/multi_index/random_access_index.hpp" synopsis
    • random_access index specifier
    • Random access indices
      • Complexity signature
      • Instantiation types
      • Constructors, copy and assignment
      • Capacity operations
      • Modifiers
      • List operations
      • Rearrange operations
      • Serialization

Header "boost/multi_index/random_access_index_fwd.hpp" synopsis

namespace boost{

namespace multi_index{

// random_access index specifier

template<typename TagList=tag<> > struct random_access;

// indices

namespace detail{

template<implementation defined> class index class name implementation defined;

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

random_access_index_fwd.hpp provides forward declarations for the random_access index specifier and its associated random access index class.

Header "boost/multi_index/random_access_index.hpp" synopsis

namespace boost{

namespace multi_index{

// random_access index specifier

template<typename TagList=tag<> > struct random_access;

// indices

namespace detail{

template<implementation defined> class index class name implementation defined;

// index comparison:

// OP is any of ==,<,!=,>,>=,<=

template<arg set 1,arg set 2>
bool operator OP(
  const index class name<arg set 1>& x,const index class name<arg set 2>& y);

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

random_access index specifier

This index specifier allows for insertion of a random access index.

template<typename TagList=tag<> > struct random_access;

If provided, TagList must be an instantiation of tag.

Random access indices

Random access indices are free-order sequences with constant time positional access and random access iterators. Elements in a random access index are by default sorted according to their order of insertion: this means that new elements inserted through a different index of the multi_index_container are appended to the end of the random access index; additionally, facilities are provided for further rearrangement of the elements. The public interface of random access indices includes that of sequenced indices, with differences in the complexity of the operations, plus extra operations for positional access (operator[] and at()) and for capacity handling. Validity of iterators and references to elements is preserved in all operations, regardless of the capacity status.

As is the case with sequenced indices, random access indices have the following limitations with respect to STL sequence containers:

  • Random access indices are not Assignable (like any other index.)
  • Insertions into a random access index may fail due to clashings with other indices. This alters the semantics of the operations provided with respect to their analogues in STL sequence containers.
  • Elements in a random access index are not mutable, and can only be changed by means of replace and modify member functions.
Having these restrictions into account, random access indices are models of Random Access Container and Back Insertion Sequence. Although these indices do not model Front Insertion Sequence, because front insertion and deletion take linear time, front operations are nonetheless provided to match the interface of sequenced indices. Describe those types and operations that are either not present in the concepts modeled or do not exactly conform to the requirements for these types of containers.

namespace boost{

namespace multi_index{

namespace detail{

template<implementation defined: dependent on types Value, Allocator, TagList>
class name is implementation defined
{
public:
  // types:

  typedef typename node_type::value_type             value_type;
  typedef tuples::null_type                          ctor_args;
  typedef typename Allocator                         allocator_type;
  typedef typename allocator_type::reference         reference;
  typedef typename allocator_type::const_reference   const_reference;
  typedef implementation defined                     iterator;
  typedef implementation defined                     const_iterator;
  typedef std::size_t                                size_type;
  typedef std::ptrdiff_t                             difference_type;
  typedef typename allocator_type::pointer           pointer;
  typedef typename allocator_type::const_pointer     const_pointer;
  typedef equivalent to
    std::reverse_iterator<iterator>                  reverse_iterator;
  typedef equivalent to
    std::reverse_iterator<const_iterator>            const_reverse_iterator;

  // construct/copy/destroy:

  index class name& operator=(const index class name& x);

  template <class InputIterator>
  void assign(InputIterator first,InputIterator last);
  void assign(size_type n,const value_type& value);

  allocator_type get_allocator()const;

  // iterators:

  iterator               begin();
  const_iterator         begin()const;
  iterator               end();
  const_iterator         end()const;
  reverse_iterator       rbegin();
  const_reverse_iterator rbegin()const;
  reverse_iterator       rend();
  const_reverse_iterator rend()const;

  // capacity:

  bool      empty()const;
  size_type size()const;
  size_type max_size()const;
  size_type capacity()const;
  void      reserve(size_type m);

  void resize(size_type n,const value_type& x=value_type());

  // access:

  const_reference operator[](size_type n)const;
  const_reference at(size_type n)const;
  const_reference front()const;
  const_reference back()const;

  // modifiers:

  std::pair<iterator,bool> push_front(const value_type& x);
  void                     pop_front();
  std::pair<iterator,bool> push_back(const value_type& x);
  void                     pop_back();

  std::pair<iterator,bool> insert(iterator position,const value_type& x);
  void insert(iterator position,size_type m,const value_type& x);
  template<typename InputIterator>
  void insert(iterator position,InputIterator first,InputIterator last);

  iterator erase(iterator position);
  iterator erase(iterator first,iterator last);

  bool replace(iterator position,const value_type& x);
  template<typename Modifier> bool modify(iterator position,Modifier mod);

  void swap(index class name& x);

  void clear();

  // list operations:

  void splice(iterator position,index class name& x);
  void splice(iterator position,index class name& x,iterator i);
  void splice(
    iterator position,index class name& x,iterator first,iterator last);

  void remove(const value_type& value);
  template<typename Predicate> void remove_if(Predicate pred);

  void unique();
  template <class BinaryPredicate>
  void unique(BinaryPredicate binary_pred);

  void merge(index class name& x);
  template <typename Compare> void merge(index class name& x,Compare comp);

  void sort();
  template <typename Compare> void sort(Compare comp);

  void reverse();

  // rearrange operations:

  void relocate(iterator position,iterator i);
  void relocate(iterator position,iterator first,iterator last);
  template<typename InputIterator> void rearrange(InputIterator first);
}

// index comparison:

template<arg set 1,arg set 2>
bool operator==(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin());
}

template<arg set 1,arg set 2>
bool operator<(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}

template<arg set 1,arg set 2>
bool operator!=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x==y);
}

template<arg set 1,arg set 2>
bool operator>(
  const index class name<arg set 1>& x
  ,const index class name<arg set 2>& y)
{
  return y<x;
}

template<arg set 1,arg set 2>
bool operator>=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x<y);
}

template<arg set 1,arg set 2>
bool operator<=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x>y);
}

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

Complexity signature

Here and in the descriptions of operations of random access indices, adopt the scheme outlined in the complexity signature section. The complexity signature of random access indices is:

  • copying: c(n)=n*log(n),
  • insertion: i(n)=1 (amortized constant),
  • hinted insertion: h(n)=1 (amortized constant),
  • deletion: d(n)=m, where m is the distance from the deleted element to the end of the sequence,
  • replacement: r(n)=1 (constant),
  • modifying: m(n)=1 (constant).
The following expressions are also used as a convenience for writing down some of the complexity formulas:

shl(a,b) = a+b if a is nonzero, 0 otherwise.
rel(a,b,c) = if a<b, c-a, else a-b,

(shl and rel stand for shift left and relocate, respetively.)

Instantiation types

Random access indices are instantiated internally to multi_index_container and specified by means of indexed_by with the random_access index specifier. Instantiations are dependent on the following types:

  • Value from multi_index_container,
  • Allocator from multi_index_container,
  • TagList from the index specifier (if provided).
TagList must be an instantiation of tag.

Constructors, copy and assignment

As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided.

index class name& operator=(const index class name& x);
Effects:
a=b;
where a and b are the multi_index_container objects to which *this and x belong, respectively.
Returns: *this.
template <class InputIterator>
void assign(InputIterator first,InputIterator last);
Requires: InputIterator is a model of Input Iterator over elements of type value_type or a type convertible to value_type. first and last are not iterators into any index of the multi_index_container to which this index belongs. last is reachable from first.
Effects:
clear();
insert(end(),first,last);
void assign(size_type n,const value_type& value);
Effects:
clear();
for(size_type i=0;i<n;++n)push_back(v);

Capacity operations

size_type capacity()const;
Returns: The total number of elements c such that, when size()<c, back insertions happen in constant time (the general case as described by i(n) is amortized constant time.)
 Note: Validity of iterators and references to elements is preserved in all insertions, regardless of the capacity status.
void reserve(size_type m);
Effects: If the previous value of capacity() was greater than or equal to m, nothing is done; otherwise, the internal capacity is changed so that capacity()>=m.
Complexity: If the capacity is not changed, constant; otherwise O(n).
Exception safety: If the capacity is not changed, nothrow; otherwise, strong.
void resize(size_type n,const value_type& x=value_type());
Effects:
if(n>size())insert(end(),n-size(),x);
else if(n<size())erase(begin()+n,end());
Note: If an expansion is requested, the size of the index is not guaranteed to be n after this operation (other indices may ban insertions.)

Modifiers

std::pair<iterator,bool> push_front(const value_type& x);
Effects: Inserts x at the beginning of the sequence if no other index of the multi_index_container bans the insertion.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(n+I(n)).
Exception safety: Strong.
std::pair<iterator,bool> push_back(const value_type& x);
Effects: Inserts x at the end of the sequence if no other index of the multi_index_container bans the insertion.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(I(n)).
Exception safety: Strong.
std::pair<iterator,bool> insert(iterator position,const value_type& x);
Requires: position is a valid iterator of the index.
Effects: Inserts x before position if insertion is allowed by all other indices of the multi_index_container.
Returns: The return value is a pair p. p.secon d is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(shl(end()-position,1) + I(n)).
Exception safety: Strong.
void insert(iterator position,size_type m,const value_type& x);
Requires: position is a valid iterator of the index.
Effects:
for(size_type i=0;i<m;++i)insert(position,x);
Complexity: O(shl(end()-position,m) + m*I(n+m)).
template<typename InputIterator>
void insert(iterator position,InputIterator first,InputIterator last);
Requires: position is a valid iterator of the index. InputIterator is a model of Input Iterator over elements of type value_type or a type convertible to value_type. first and last are not iterators into any index of the multi_index_container to which this index belongs. last is reachable from first.
Effects:
while(first!=last)insert(position,*first++);
Complexity: O(shl(end()-position,m) + m*I(n+m)), where m is the number of elements in [first,last).
Exception safety: Basic.
iterator erase(iterator position);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Deletes the element pointed to by position.
Returns: An iterator pointing to the element immediately following the one that was deleted, or end() if no such element exists.
Complexity: O(D(n)).
Exception safety: nothrow.
iterator erase(iterator first,iterator last);
Requires: [first,last) is a valid range of the index.
Effects: Deletes the elements in [first,last).
Returns: last.
Complexity: O(m*D(n)), where m is the number of elements in [first,las t).
Exception safety: nothrow.
bool replace(iterator position,const value_type& x);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Assigns the value x to the element pointed to by position into the multi_index_container to which the index belongs if replacing is allowed by all other indices of the multi_index_container.
Postconditions: Validity of position is preserved in all cases.
Returns: true if the replacement took place, false otherwise.
Complexity: O(R(n)).
Exception safety: Strong. If an exception is thrown by some user-provided operation the multi_index_container to which the index belongs remains in its original state.
template<typename Modifier> bool modify(iterator position,Modifier mod);
Requires: Modifier is a model of Unary Function accepting arguments of type value_type&. position is a valid dereferenceable iterator of the index.
Effects: Calls mod(e) where e is the element pointed to by position and rearranges *position into all the indices of the multi_index_container. Rearrangement on rancom access indices does not change the position of the element with respect to the index; rearrangement on other indices may or might not suceed. If the rearrangement fails, the element is erased.
Postconditions: Validity of position is preserved if the operation succeeds.
Returns: true if the operation succeeded, false otherwise.
Complexity: O(M(n)).
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod), then the element pointed to by position is erased.

List operations

Random access indices replicate the interface of sequenced indices, which in turn includes the list operations provided by std::list. The syntax and behavior of these operations exactly matches those of sequenced indices, but the associated complexity bounds differ in general.

void splice(iterator position,index class name& x);
Requires: position is a valid iterator of the index. &x!=this.
Effects: Inserts the contents of x before position, in the same order as they were in x. Those elements succesfully inserted are erased from x.
Complexity: O(shl(end()-position,x.size()) + x.size()*I(n+x.size()) + x.size()*D(x.size())).
Exception safety: Basic.
void splice(iterator position,index class name& x,iterator i);
Requires: position is a valid iterator of the index. i is a valid dereferenceable iterator x.
Effects: Inserts the element pointed to by i before position: if insertion is succesful, the element is erased from x. In the special case &x==this, no copy or deletion is performed, and the operation is always succesful. If position==i, no operation is performed.
Postconditions: If &x==this, no iterator or reference is invalidated.
Complexity: If &x==this, O(rel(position,i,i+1)); otherwise O(shl(end()-position,1) + I(n) + D(n)).
Exception safety: If &x==this, nothrow; otherwise, strong.
void splice(iterator position,index class name& x,iterator first,iterator last);
Requires: position is a valid iterator of the index. first and last are valid iterators of x. last is reachable from first. position is not in the range [first,last).
Effects: For each element in the range [first,last), insertion is tried before position; if the operation is succesful, the element is erased from x. In the special case &x==this, no copy or deletion is performed, and insertions are always succesful.
Postconditions: If &x==this, no iterator or reference is invalidated.
Complexity: If &x==this, O(rel(position,first,last)); otherwise O(shl(end()-position,m) + m*I(n+m) + m*D(x.size())) where m is the number of elements in [first,last).
Exception safety: If &x==this, nothrow; otherwise, basic.
void remove(const value_type& value);
Effects: Erases all elements of the index which compare equal to value.
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Except ion safety: Basic.
template<typename Predicate> void remove_if(Predicate pred);
Effects: Erases all elements x of the index for which pred(x) holds.
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
void unique();
Effects: Eliminates all but the first element from every consecutive group of equal elements referred to by the iterator i in the range [first+1,last) for which *i==*(i-1).
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
Effects: Eliminates all but the first element from every consecutive group of elements referred to by the iterator i in the range [first+1,last) for which binary_pred(*i,*(i-1)) holds.
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
void merge(index class name& x);
Requires: std::less<value_type> is a Strict Weak Ordering over value_type. Both the index and x are sorted according to std::less<value_type>.
Effects: Attempts to insert every element of x into the corresponding position of the index (according to the order). Elements successfully inserted are erased from x. The resulting sequence is stable, i.e. equivalent elements of either container preserve their relative position. In the special case &x==this, no operation is performed.
Postconditions: Elements in the index and remaining elements in x are sorted. Validity of iterators to the index and of non-erased elements of x references is preserved.
Complexity: If &x==this, constant; otherwise O(n + x.size()*I(n+x.size()) + x.size()*D(x.size())).
Exception safety: If &x==this, nothrow; otherwise, basic.
template <typename Compare> void merge(index class name& x,Compare comp);
Requires: Compare is a Strict Weak Ordering over value_type. Both the index and x are sorted according to comp.
Effects: Attempts to insert every element of x into the corresponding position of the index (according to comp). Elements successfully inserted are erased from x. The resulting sequence is stable, i.e. equivalent elements of either container preserve their relative position. In the special case &x==this, no operation is performed.
Postconditions: Elements in the index and remaining elements in x are sorted according to comp. Validity of iterators to the index and of non-erased elements of x references is preserved.
Complexity: If &x==this, constant; otherwise O(n + x.size()*I(n+x.size()) + x.size()*D(x.size())).
Exception safety: If &x==this, nothrow; otherwise, basic.
void sort();
Requires: std::less<value_type> is a Strict Weak Ordering over value_type.
Effects: Sorts the index according to std::less<value_type>. The sorting is stable, i.e. equivalent elements preserve their relative position.
Postconditions: Validity of iterators and references is preserved.
Complexity: O(n*log(n)).
Exception safety: Basic.
template <typename Compare> void sort(Compare comp);
Requires: Compare is a Strict Weak Ordering over value_type.
Effects: Sorts the index according to comp. The sorting is stable, i.e. equivalent elements preserve their relative position.
Postconditions: Validity of iterators and references is preserved.
Complexity: O(n*log(n)).
Exception safety: Basic.
void reverse();
Effects: Reverses the order of the elements in the index.
Postconditions: Validity of iterators and references is preserved.
Complexity: O(n).
Exception safety: nothrow.

Rearrange operations

These operations, without counterpart in STL sequence containers (although std::list::splice provides partially overlapping functionality), perform individual and global repositioning of elements inside the index.

void relocate(iterator position,iterator i);
Requires: position is a valid iterator of the index. i is a valid dereferenceable iterator of the index.
Effects: Inserts the element pointed to by i before position. If position==i, no operation is performed.
Postconditions: No iterator or reference is invalidated.
Complexity: O(rel(position,i,i+1)).
Exception safety: nothrow.
void relocate(iterator position,iterator first,iterator last);
Requires: position is a valid iterator of the index. first and last are valid iterators of the index. last is reachable from first. position is not in the range [first,last).
Effects: The range of elements [first,last) is repositioned just before position.
Postconditions: No iterator or reference is invalidated.
Complexity: O(rel(position,first,last)).
Exception safety: nothrow.
template<typename InputIterator> void rearrange(InputIterator first);
Requires: The range [first, std::advance(first,n)), where n is the size of the index, is a free view of the index.
Effects: The elements are rearranged so as to match the order of the previously described view.
Postconditions: No iterator or reference is invalidated.
Complexity: O(n).
Exception safety: Basic.

Serialization

Indices cannot be serialized on their own, but only as part of the multi_index_container into which they are embedded. In describing the additional preconditions and guarantees associated to random access indices with respect to serialization of their embedding containers, use the concepts defined in the multi_index_container serialization section.

Operation: saving of a multi_index_container m to an output archive (XML archive) ar.
Requires: No additional requirements to those imposed by the container.
Operation: loading of a multi_index_container m' from an input archive (XML archive) ar.
Requires: No additional requirements to those imposed by the container.
Postconditions: On succesful loading, each of the elements of [begin(), end()) is a restored copy of the corresponding element in [m.get<i>().begin(), m.get<i>().end()), where i is the position of the random access index in the container.
Operation: saving of an iterator or const_iterator it to an output archive (XML archive) ar.
Requires: it is a valid iterator of the index. The associated multi_index_container has been previously saved.
Operation: loading of an iterator or const_iterator it' from an input archive (XML archive) ar.
Postconditions: On succesful loading, if it was dereferenceable then *it' is the restored copy of *it, otherwise it'==end().
Note: It is allowed that it be a const_iterator and the restored it' an iterator, or viceversa.
Revised February 6th 2006© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Sequenced indices reference

Contents

  • Header "boost/multi_index/sequenced_index_fwd.hpp" synopsis
  • Header "boost/multi_index/sequenced_index.hpp" synopsis
    • sequenced index specifier
    • Sequenced indices
      • Complexity signature
      • Instantiation types
      • Constructors, copy and assignment
      • Capacity operations
      • Modifiers
      • List operations
      • Rearrange operations
      • Serialization

Header "boost/multi_index/sequenced_index_fwd.hpp" synopsis

namespace boost{

namespace multi_index{

// sequenced index specifier

template<typename TagList=tag<> > struct sequenced;

// indices

namespace detail{

template<implementation defined> class index class name implementation defined;

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

sequenced_index_fwd.hpp provides forward declarations for the sequenced index specifier and its associated sequenced index class.

Header "boost/multi_index/sequenced_index.hpp" synopsis

namespace boost{

namespace multi_index{

// sequenced index specifier

template<typename TagList=tag<> > struct sequenced;

// indices

namespace detail{

template<implementation defined> class index class name implementation defined;

// index comparison:

// OP is any of ==,<,!=,>,>=,<=

template<arg set 1,arg set 2>
bool operator OP(
  const index class name<arg set 1>& x,const index class name<arg set 2>& y);

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

sequenced index specifier

This index specifier allows for insertion of a sequenced index.

template<typename TagList=tag<> > struct sequenced;

If provided, TagList must be an instantiation of tag.

Sequenced indices

Sequenced indices are modeled after the semantics of a bidirectional list like std::list. Elements in a sequenced index are by default sorted according to their order of insertion: this means that new elements inserted through a different index of the multi_index_container are appended to the end of the sequenced index. Additionally, the index allows for free reordering of elements in the same vein as std::list does. Validity of iterators and references to elements is preserved in all operations.

There are a number of differences with respect to std::lists:

  • Sequenced indices are not Assignable (like any other index.)
  • Unlike as in std::list, insertions into a sequenced index may fail due to clashings with other indices. This alters the semantics of the operations provided with respect to their analogues in std::list.
  • Elements in a sequenced index are not mutable, and can only be changed by means of replace and modify member functions.
Having these restrictions into account, sequenced indices are models of Reversible Container, Front Insertion Sequence and Back Insertion Sequence. We only provide descriptions of those types and operations that are either not present in the concepts modeled or do not exactly conform to the requirements for these types of containers.

namespace boost{

namespace multi_index{

namespace detail{

template<implementation defined: dependent on types Value, Allocator, TagList>
class name is implementation defined
{
public:
  // types:

  typedef typename node_type::value_type             value_type;
  typedef tuples::null_type                          ctor_args;
  typedef typename Allocator                         allocator_type;
  typedef typename allocator_type::reference         reference;
  typedef typename allocator_type::const_reference   const_reference;
  typedef implementation defined                     iterator;
  typedef implementation defined                     const_iterator;
  typedef std::size_t                                size_type;
  typedef std::ptrdiff_t                             difference_type;
  typedef typename allocator_type::pointer           pointer;
  typedef typename allocator_type::const_pointer     const_pointer;
  typedef equivalent to
    std::reverse_iterator<iterator>                  reverse_iterator;
  typedef equivalent to
    std::reverse_iterator<const_iterator>            const_reverse_iterator;

  // construct/copy/destroy:

  index class name& operator=(const index class name& x);

  template <class InputIterator>
  void assign(InputIterator first,InputIterator last);
  void assign(size_type n,const value_type& value);

  allocator_type get_allocator()const;

  // iterators:

  iterator               begin();
  const_iterator         begin()const;
  iterator               end();
  const_iterator         end()const;
  reverse_iterator       rbegin();
  const_reverse_iterator rbegin()const;
  reverse_iterator       rend();
  const_reverse_iterator rend()const;

  // capacity:

  bool      empty()const;
  size_type size()const;
  size_type max_size()const;

  void resize(size_type n,const value_type& x=value_type());

  // access:

  const_reference front()const;
  const_reference back()const;

  // modifiers:

  std::pair<iterator,bool> push_front(const value_type& x);
  void                     pop_front();
  std::pair<iterator,bool> push_back(const value_type& x);
  void                     pop_back();

  std::pair<iterator,bool> insert(iterator position,const value_type& x);
  void insert(iterator position,size_type n,const value_type& x);
  template<typename InputIterator>
  void insert(iterator position,InputIterator first,InputIterator last);

  iterator erase(iterator position);
  iterator erase(iterator first,iterator last);

  bool replace(iterator position,const value_type& x);
  template<typename Modifier> bool modify(iterator position,Modifier mod);

  void swap(index class name& x);

  void clear();

  // list operations:

  void splice(iterator position,index class name& x);
  void splice(iterator position,index class name& x,iterator i);
  void splice(
    iterator position,index class name& x,iterator first,iterator last);

  void remove(const value_type& value);
  template<typename Predicate> void remove_if(Predicate pred);

  void unique();
  template <class BinaryPredicate>
  void unique(BinaryPredicate binary_pred);

  void merge(index class name& x);
  template <typename Compare> void merge(index class name& x,Compare comp);

  void sort();
  template <typename Compare> void sort(Compare comp);

  void reverse();

  // rearrange operations:

  void relocate(iterator position,iterator i);
  void relocate(iterator position,iterator first,iterator last);
  template<typename InputIterator> void rearrange(InputIterator first);
}

// index comparison:

template<arg set 1,arg set 2>
bool operator==(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin());
}

template<arg set 1,arg set 2>
bool operator<(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}

template<arg set 1,arg set 2>
bool operator!=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x==y);
}

template<arg set 1,arg set 2>
bool operator>(
  const index class name<arg set 1>& x
  ,const index class name<arg set 2>& y)
{
  return y<x;
}

template<arg set 1,arg set 2>
bool operator>=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x<y);
}

template<arg set 1,arg set 2>
bool operator<=(
  const index class name<arg set 1>& x,
  const index class name<arg set 2>& y)
{
  return !(x>y);
}

// index specialized algorithms:

template<implementation defined>
void swap(index class name& x,index class name& y);

} // namespace boost::multi_index::detail

} // namespace boost::multi_index

} // namespace boost

Complexity signature

Here and in the descriptions of operations of sequenced indices, we adopt the scheme outlined in the complexity signature section. The complexity signature of sequenced indices is:

  • copying: c(n)=n*log(n),
  • insertion: i(n)=1 (constant),
  • hinted insertion: h(n)=1 (constant),
  • deletion: d(n)=1 (constant),
  • replacement: r(n)=1 (constant),
  • modifying: m(n)=1 (constant).

Instantiation types

Sequenced indices are instantiated internally to multi_index_container and specified by means of indexed_by with the sequenced index specifier. Instantiations are dependent on the following types:

  • Value from multi_index_container,
  • Allocator from multi_index_container,
  • TagList from the index specifier (if provided).
TagList must be an instantiation of tag.

Constructors, copy and assignment

As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided.

index class name& operator=(const index class name& x);
Effects:
a=b;
where a and b are the multi_index_container objects to which *this and x belong, respectively.
Returns: *this.
template <class InputIterator>
void assign(InputIterator first,InputIterator last);
Requires: InputIterator is a model of Input Iterator over elements of type value_type or a type convertible to value_type. first and last are not iterators into any index of the multi_index_container to which this index belongs. last is reachable from first.
Effects:
clear();
insert(end(),first,last);
void assign(size_type n,const value_type& value);
Effects:
clear();
for(size_type i=0;i<n;++n)push_back(v);

Capacity operations

void resize(size_type n,const value_type& x=value_type());
Effects:
if(n>size())insert(end(),n-size(),x);
else if(n<size()){
  iterator it=begin();
  std::advance(it,n);
  erase(it,end());
}
Note: If an expansion is requested, the size of the index is not guaranteed to be n after this operation (other indices may ban insertions.)

Modifiers

std::pair<iterator,bool> push_front(const value_type& x);
Effects: Inserts x at the beginning of the sequence if no other index of the multi_index_container bans the insertion.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(I(n)).
Exception safety: Strong.
std::pair<iterator,bool> push_back(const value_type& x);
Effects: Inserts x at the end of the sequence if no other index of the multi_index_container bans the insertion.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(I(n)).
Exception safety: Strong.
std::pair<iterator,bool> insert(iterator position,const value_type& x);
Requires: position is a valid iterator of the index.
Effects: Inserts x before position if insertion is allowed by all other indices of the multi_index_container.
Returns: The return value is a pair p. p.second is true if and only if insertion took place. On successful insertion, p.first points to the element inserted; otherwise, p.first points to an element that caused the insertion to be banned. Note that more than one element can be causing insertion not to be allowed.
Complexity: O(I(n)).
Exception safety: Strong.
void insert(iterator position,size_type n,const value_type& x);
Requires: position is a valid iterator of the index.
Effects:
for(size_type i=0;i<n;++i)insert(position,x);
template<typename InputIterator>
void insert(iterator position,InputIterator first,InputIterator last);
Requires: position is a valid iterator of the index. InputIterator is a model of Input Iterator over elements of type value_type or a type convertible to value_type. first and last are not iterators into any index of the multi_index_container to which this index belongs. last is reachable from first.
Effects:
while(first!=last)insert(position,*first++);
Complexity: O(m*I(n+m)), where m is the number of elements in [first,last).
Exception safety: Basic.
iterator erase(iterator position);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Deletes the element pointed to by position.
Returns: An iterator pointing to the element immediately following the one that was deleted, or end() if no such element exists.
Complexity: O(D(n)).
Exception safety: nothrow.
iterator erase(iterator first,iterator last);
Requires: [first,last) is a valid range of the index.
Effects: Deletes the elements in [first,last).
Returns: last.
Complexity: O(m*D(n)), where m is the number of elements in [first,last).
Exception safety: nothrow.
bool replace(iterator position,const value_type& x);
Requires: position is a valid dereferenceable iterator of the index.
Effects: Assigns the value x to the element pointed to by position into the multi_index_container to which the index belongs if replacing is allowed by all other indices of the multi_index_container.
Postconditions: Validity of position is preserved in all cases.
Returns: true if the replacement took place, false otherwise.
Complexity: O(R(n)).
Exception safety: Strong. If an exception is thrown by some user-provided operation the multi_index_container to which the index belongs remains in its original state.
template<typename Modifier> bool modify(iterator position,Modifier mod);
Requires: Modifier is a model of Unary Function accepting arguments of type value_type&. position is a valid dereferenceable iterator of the index.
Effects: Calls mod(e) where e is the element pointed to by position and rearranges *position into all the indices of the multi_index_container. Rearrangement on sequenced indices does not change the position of the element with respect to the index; rearrangement on other indices may or might not suceed. If the rearrangement fails, the element is erased.
Postconditions: Validity of position is preserved if the operation succeeds.
Returns: true if the operation succeeded, false otherwise.
Complexity: O(M(n)).
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod), then the element pointed to by position is erased.

List operations

Sequenced indices provide the full set of list operations found in std::list; the semantics of these member functions, however, differ from that of std::list in some cases as insertions might not succeed due to banning by other indices. Similarly, the complexity of the operations may depend on the other indices belonging to the same multi_index_container.

void splice(iterator position,index class name& x);
Requires: position is a valid iterator of the index. &x!=this.
Effects: Inserts the contents of x before position, in the same order as they were in x. Those elements succesfully inserted are erased from x.
Complexity: O(x.size()*I(n+x.size()) + x.size()*D(x.size())).
Exception safety: Basic.
void splice(iterator position,index class name& x,iterator i);
Requires: position is a valid iterator of the index. i is a valid dereferenceable iterator x.
Effects: Inserts the element pointed to by i before position: if insertion is succesful, the element is erased from x. In the special case &x==this, no copy or deletion is performed, and the operation is always succesful. If position==i, no operation is performed.
Postconditions: If &x==this, no iterator or reference is invalidated.
Complexity: If &x==this, constant; otherwise O(I(n) + D(n)).
Exception safety: If &x==this, nothrow; otherwise, strong.
void splice(iterator position,index class name& x,iterator first,iterator last);
Requires: position is a valid iterator of the index. first and last are valid iterators of x. last is reachable from first. position is not in the range [first,last).
Effects: For each element in the range [first,last), insertion is tried before position; if the operation is succesful, the element is erased from x. In the special case &x==this, no copy or deletion is performed, and insertions are always succesful.
Postconditions: If &x==this, no iterator or reference is invalidated.
Complexity: If &x==this, constant; otherwise O(m*I(n+m) + m*D(x.size())) where m is the number of elements in [first,last).
Exception safety: If &x==this, nothrow; otherwise, basic.
void remove(const value_type& value);
Effects: Erases all elements of the index which compare equal to value.
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
template<typename Predicate> void remove_if(Predicate pred);
Effects: Erases all elements x of the index for which pred(x) holds.
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
void unique();
Effects: Eliminates all but the first element from every consecutive group of equal elements referred to by the iterator i in the range [first+1,last) for which *i==*(i-1).
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
Effects: Eliminates all but the first element from every consecutive group of elements referred to by the iterator i in the range [first+1,last) for which binary_pred(*i,*(i-1)) holds.
Complexity: O(n + m*D(n)), where m is the number of elements erased.
Exception safety: Basic.
void merge(index class name& x);
Requires: std::less<value_type> is a Strict Weak Ordering over value_type. Both the index and x are sorted according to std::less<value_type>.
Effects: Attempts to insert every element of x into the corresponding position of the index (according to the order). Elements successfully inserted are erased from x. The resulting sequence is stable, i.e. equivalent elements of either container preserve their relative position. In the special case &x==this, no operation is performed.
 Postconditions: Elements in the index and remaining elements in x are sorted. Validity of iterators to the index and of non-erased elements of x references is preserved.
Complexity: If &x==this, constant; otherwise O(n + x.size()*I(n+x.size()) + x.size()*D(x.size())).
Exception safety: If &x==this, nothrow; otherwise, basic.
template <typename Compare> void merge(index class name& x,Compare comp);
Requires: Compare is a Strict Weak Ordering over value_type. Both the index and x are sorted according to comp.
Effects: Attempts to insert every element of x into the corresponding position of the index (according to comp). Elements successfully inserted are erased from x. The resulting sequence is stable, i.e. equivalent elements of either container preserve their relative position. In the special case &x==this, no operation is performed.
 Postconditions: Elements in the index and remaining elements in x are sorted according to comp. Validity of iterators to the index and of non-erased elements of x references is preserved.
Complexity: If &x==this, constant; otherwise O(n + x.size()*I(n+x.size()) + x.size()*D(x.size())).
Exception safety: If &x==this, nothrow; otherwise, basic.
void sort();
Requires: std::less<value_type> is a Strict Weak Ordering over value_type.
Effects: Sorts the index according to std::less<value_type>. The sorting is stable, i.e. equivalent elements preserve their relative position.
Postconditions: Validity of iterators and references is preserved.
Complexity: O(n*log(n)).
Exception safety: nothrow if std::less<value_type> does not throw; otherwise, basic.
template <typename Compare> void sort(Compare comp);
Requires: Compare is a Strict Weak Ordering over value_type.
Effects: Sorts the index according to comp. The sorting is stable, i.e. equivalent elements preserve their relative position.
Postconditions: Validity of iterators and references is preserved.
Complexity: O(n*log(n)).
Exception safety: nothrow if comp does not throw; otherwise, basic.
void reverse();
Effects: Reverses the order of the elements in the index.
Postconditions: Validity of iterators and references is preserved.
Complexity: O(n).
Exception safety: nothrow.

Rearrange operations

These operations, without counterpart in std::list (although splice provides partially overlapping functionality), perform individual and global repositioning of elements inside the index.

void relocate(iterator position,iterator i);
Requires: position is a valid iterator of the index. i is a valid dereferenceable iterator of the index.
Effects: Inserts the element pointed to by i before position. If position==i, no operation is performed.
Postconditions: No iterator or reference is invalidated.
Complexity: Constant.
Exception safety: nothrow.
void relocate(iterator position,iterator first,iterator last);
Requires: position is a valid iterator of the index. first and last are valid iterators of the index. last is reachable from first. position is not in the range [first,last).
Effects: The range of elements [first,last) is repositioned just before position.
Postconditions: No iterator or reference is invalidated.
Complexity: Constant.
safety: nothrow..
template<typename InputIterator> void rearrange(InputIterator first); Requires: The range [first, std::advance(first,n)), where n is the size of the index, is a free view of the index.
Effects: The elements are rearranged so as to match the order of the previously described view.
Postconditions: No iterator or reference is invalidated.
Complexity: O(n).
 Exception safety: Basic.

Serialization

Indices cannot be serialized on their own, but only as part of the multi_index_container into which they are embedded. In describing the additional preconditions and guarantees associated to sequenced indices with respect to serialization of their embedding containers, we use the concepts defined in the multi_index_container serialization section.

Operation: saving of a multi_index_container m to an output archive (XML archive) ar.
Requires: No additional requirements to those imposed by the container.
Operation: loading of a multi_index_container m' from an input archive (XML archive) ar.
Requires: No additional requirements to those imposed by the container.
Postconditions: On succesful loading, each of the elements of [begin(), end()) is a restored copy of the corresponding element in [m.get<i>().begin(), m.get<i>().end()), where i is the position of the sequenced index in the container.
Operation: saving of an iterator or const_iterator it to an output archive (XML archive) ar.
Requires: it is a valid iterator of the index. The associated multi_index_container has been previously saved.
Operation: loading of an iterator or const_iterator it' from an input archive (XML archive) ar.
Postconditions: On succesful loading, if it was dereferenceable then *it' is the restored copy of *it, otherwise it'==end().
Note: It is allowed that it be a const_iterator and the restored it' an iterator,
Revised February 6th 2006© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.html or copy at http://www.boost.org/LICENSE_1_0.txt) 		Top


Boost.MultiIndex Documentation - Release notes

Boost.MultiIndex Release notes

  • Boost 1.34 release

  • Boost 1.33.1 release

  • Boost 1.33 release

Boost 1.34 release

  • Added random access indices.

  • Non key-based indices provide new rearrange facilities allowing for interaction with external mutating algorithms.

  • All predefined Boost.MultiIndex key extractors instantiated for a given type T can handle objects of types derived from or convertible to T (and chained pointers to those). Previously, only objects of the exact type specified (along with reference_wrappers and chained pointers to them) were accepted.

  • composite_key_compare and related classes accept operands not included in tuples as if they were passed in a tuple of length 1; this allows the user to omit tuple enclosing in lookup operations involving composite keys when only the first key is provided.

  • The core algorithms of ordered indices have been optimized, yielding an estimated reduction of about 5% in insertion times.

  • Size of ordered indices node headers have been reduced by 25% on most platforms, using a well known optimization technique.

  • The tutorial has been restructured, new examples added.

  • Maintenance fixes.

Boost 1.33.1 release

  • For ordered and hashed indices, erase(it) and erase(first,last) now return an iterator to the element following those being deleted (previously nothing was returned), in accordance with the C++ Standard Library Defect Report 130 and issue 6.19 of TR1 Issues List.

  • Boost.MultiIndex offers the usual guarantees with respect to multithreading code provided by most STL implementations:

    1. Concurrent access to different containers is safe.

    2. Concurrent read-only access to the same container is safe.

    In previous versions of the library, the latter guarantee was not properly maintained if the safe mode was set. This problem has been fixed now.

  • Maintenance fixes.

Boost 1.33 release

  • Added hashed indices, whose interface is based on the specification for unordered associative containers by the C++ Standard Library Technical Report (TR1).

  • Added serialization support for Boost.Serialization.

  • Destruction of multi_index_containers and memfuns now perform faster.

  • Internal changes aimed at reducing the length of symbol names generated by the compiler; cuts of up to a 50% can be achieved with respect to the Boost 1.32 release. This results in much shorter and more readable error messages and has also a beneficial impact on compilers with strict limits on symbol name lengths. Additionally, a section on further reduction of symbol name lengths has been added.

  • Restructured some parts of the documentation, new examples.

  • Maintenance fixes.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tests

Boost.MultiIndex Tests

The Boost.MultiIndex test suite exercises the whole spectrum of functionalities provided by the library. Although the tests are not meant to serve as a learning guide, the interested reader may find it useful to inspect the source code to gain familiarity with some of the least common features offered by Boost.MultiIndex.

Boost.MultiIndex test suite.
Program | Description
test_basic.cpp | Simple program along the lines of the employees example studied in the tutorial.
test_capacity.cpp | empty, size, resize (non key-based indices) and reserve/capacity (random access indices only).
test_comparison.cpp | Comparison between indices.
test_composite_key.cpp | composite_key and composite_key_compare.
test_conv_iterators.cpp | Checks convertibility of non-constant to constant iterators.
test_copy_assignment.cpp | Various forms of assignment: copy, operator =, insertion, (non key-based indices only) assign .
test_hash_ops.cpp | Hashing operations.
test_iterators.cpp | Constant and non-constant iterators and their reverse variants.
test_key_extractors.cpp | Covers all use cases of key extractors shipped with the library.
test_list_ops.cpp | List-like operations particular to sequenced and random access indices.
test_modifiers.cpp | Checks the family of insertion and erasing operations.
test_mpl_ops.cpp | Metaprogramming manipulations of multi_index_container types.
test_observers.cpp | Checks observer member functions of ordered and hashed indices.
test_projection.cpp | Projection of iterators among indices.
test_range.cpp | Exercises the range facility (ordered indices only).
test_rearrange.cpp | Rearrange functions of sequenced and random access indices.
test_safe_mode.cpp | Comprehensive coverage of all conditions checked in safe mode.
test_serialization1.cpp
test_serialization2.cpp 		Serialization support.
test_set_ops.cpp | Set-like operations particular to ordered indices.
test_special_set_ops.cpp | Checks special lookup operations using compatible sorting criteria.
test_update.cpp | replace, modify and modify_key.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial - Basics

Boost.MultiIndex Tutorial: Basics

  • Introduction

    • Multiple sorts on a single set

    • A bidirectional list with fast lookup

  • Index specification

  • Tagging

  • Iterator access

  • Index types

    • Ordered indices

      • Unique and non-unique variants

      • Specification

      • Key extraction

      • Comparison predicates

      • Special lookup operations

      • Retrieval of ranges

      • Updating

    • Sequenced indices

      • Specification

      • List operations

      • Updating

  • Projection of iterators

  • Complexity and exception safety

Introduction

This section introduces the main concepts of Boost.MultiIndex through the study of two typical use cases.

Multiple sorts on a single set

STL sets and multisets are varying-length containers where elements are efficiently sorted according to a given comparison predicate. These container classes fall short of functionality when the programmer wishes to efficiently sort and look up the elements following a different sorting criterion. Consider for instance:

struct employee
{
  int         id;
  std::string name;

  employee(int id,const std::string& name):id(id),name(name){}

  bool operator<(const employee& e)const{return id<e.id;}
};

The fact that IDs are unique to each employee is reflected by the way operator< is defined, so a natural data structure for storing of employees is just a std::set<employee>. Now, if one wishes to print out a listing of all employees in alphabetical order, available solutions may have disadvantages either in terms of storage space, complexity or ease of maintenance:

  • Copy the employee set into a vector or similar and sort this by a comparison functor dependent upon employee::name.

  • Keep a secondary data structure of pointers to the elements of the set, appropriately sorted by name.

Of these, probably the second solution will be the one adopted by most programmers concerned about efficiency, but it imposes the burden of keeping the two structures in sync. If the code is additionally required to be exception-safe, this construct easily becomes unmaintainable.

Boost.MultiIndex features ordered indices, which sort the elements according to a particular key, and are designed to help programmers in need of sequences of elements for which more than one sorting criteria are relevant. Define a multi_index_container instantiation composed of several ordered indices: each index, viewed in isolation, behaves much as an ordered std::set (or std::multiset), whilst the overall integrity of the entire data structure is preserved. The example problem thus can be solved with Boost.MultiIndex as follows:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>

// define a multiply indexed set with indices by id and name
typedef multi_index_container<
  employee,
  indexed_by<
    // sort by employee::operator<
    ordered_unique<identity<employee> >,

    // sort by less<string> on name
    ordered_non_unique<member<employee,std::string,&employee::name> >
  >
> employee_set;

void print_out_by_name(const employee_set& es)
{
  // get a view to index #1 (name)
  const employee_set::nth_index<1>::type& name_index=es.get<1>();
  // use name_index as a regular std::set
  std::copy(
    name_index.begin(),name_index.end(),
    std::ostream_iterator<employee>(std::cout));
}

Instead of a single comparison predicate type, as it happens for STL associative containers, multi_index_container is passed a typelist of index specifications (indexed_by), each one inducing the corresponding index. Indices are accessed via get<N>() where N ranges between 0 and the number of comparison predicates minus one. The functionality of index #0 can be accessed directly from a multi_index_container object without using get<0>(): for instance, es.begin() is equivalent to es.get<0>().begin().

Note that get returns a reference to the index, and not an index object. Indices cannot be constructed as separate objects from the container they belong to, so the following

// Wrong: we forgot the & after employee_set::nth_index<1>::type
const employee_set::nth_index<1>::type name_index=es.get<1>();

does not compile, since it is trying to construct the index object name_index. This is a common source of errors in user code.

A bidirectional list with fast lookup

This case study introduces the so-called sequenced indices, and how they interact with ordered indices to construct powerful containers. Suppose there is a text parsed into words and stored in a list like this:

typedef std::list<std::string> text_container;

std::string text=
  "Alice was beginning to get very tired of sitting by her sister on the "
  "bank, and of having nothing to do: once or twice she had peeped into the "
  "book her sister was reading, but it had no pictures or conversations in "
  "it, 'and what is the use of a book,' thought Alice 'without pictures or "
  "conversation?'";

// feed the text into the list
text_container tc;
boost::tokenizer<boost::char_separator<char> > tok
  (text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));
std::copy(tok.begin(),tok.end(),std::back_inserter(tc));

To count the occurrences of a given word into the text we will resort to std::count:

std::size_t occurrences(const std::string& word)
{
  return std::count(tc.begin(),tc.end(),word);
}

But this implementation of occurrences performs in linear time, which could be unacceptable for large quantities of text. Similarly, other operations like deletion of selected words are just too costly to carry out on a std::list:

void delete_word(const std::string& word)
{
  tc.remove(word); // scans the entire list looking for word
}

When performance is a concern, the user needs an additional data structure that indexes the elements in tc, presumably in alphabetical order. Boost.MultiIndex does precisely this through the combination of sequenced and ordered indices:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>

// define a multi_index_container with a list-like index and an ordered index
typedef multi_index_container<
  std::string,
  indexed_by<
    sequenced<>, // list-like index
    ordered_non_unique<identity<std::string> > // words by alphabetical order
  >
> text_container;

std::string text=...

// feed the text into the list
text_container tc;
boost::tokenizer<boost::char_separator<char> > tok
  (text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));
std::copy(tok.begin(),tok.end(),std::back_inserter(tc));

So far, the substitution of multi_index_container for std::list does not show any advantage. The code for inserting the text into the container does not change as sequenced indices provide an interface similar to that of std::list (no explicit access to this index through get<0>() is needed as multi_index_container inherits the functionality of index #0.) But the specification of an additional ordered index allows us to implement occurrences and delete_word in a much more efficient manner:

std::size_t occurrences(const std::string& word)
{
  // get a view to index #1
  text_container::nth_index<1>::type& sorted_index=tc.get<1>();

  // use sorted_index as a regular std::set
  return sorted_index.count(word);
}

void delete_word(const std::string& word)
{
  // get a view to index #1
  text_container::nth_index<1>::type& sorted_index=tc.get<1>();

  // use sorted_index as a regular std::set
  sorted_index.erase(word);
}

Now, occurrences and delete_word have logarithmic complexity. The programmer can use index #0 for accessing the text as with std::list, and use index #1 when logarithmic lookup is needed.

Index specification

The indices of a multi_index_container instantiation are specified by means of the indexed_by construct. For instance, the instantiation

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >
  >
> employee_set;

is comprised of a unique ordered index and a non-unique ordered index, while in

typedef multi_index_container<
  std::string,
  indexed_by<
    sequenced<>,
    ordered_non_unique<identity<std::string> >
  >
> text_container;

Specify two indices, the first of sequenced type, the second a non-unique ordered index. In general, specify an arbitrary number of indices: each of the arguments of indexed_by is called an index specifier. Depending on the type of index being specified, the corresponding specifier will need additional information: for instance, the specifiers ordered_unique and ordered_non_unique are provided with a key extractor and an optional comparison predicate which jointly indicate how the sorting of elements will be performed.

A multi_index_container instantiation can be declared without supplying the indexed_by part: in this case, default index values are taken so that the resulting type is equivalent to a regular std::set. Concretely, the instantiation

multi_index_container<(element)>

is equivalent to

multi_index_container<
  (element),
  indexed_by<
    ordered_unique<identity<(element)> >
  >
>

Tagging

In order to retrieve (a reference to) an index of a given multi_index_container, the programmer must provide its order number, which is cumbersome and not very self-descriptive. Optionally, indices can be assigned tags (C++ types) that act as more convenient mnemonics. If provided, tags must be passed as the first parameter of the corresponding index specifier. The following is a revised version of employee_set with inclusion of tags:

// tags
struct name{};

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<tag<name>,member<employee,std::string,&employee::name> >
  >
> employee_set;

Tags have to be passed inside the tag construct. Any type can be used as a tag for an index, although in general one will choose names that are descriptive of the index they are associated with. The tagging mechanism allows the user to write expressions like,

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name::iterator it=es.get<name>().begin();

If no tag is provided for an index (as is the case for index #0 of the previous example), access to that index can only be performed by number. Note the existence of two different typedefs nth_index and index for referring to an index by number and by tag, respectively; for instance,

  • employee_set::nth_index<1>::type is the type of index #1,

  • employee_set::index<name>::type is the type of the index tagged with name (the same index #1 in this case.)

get(), on the other hand, is overloaded to serve both styles of access:

employee_set::index<name>::type& name_index=es.get<name>();
employee_set::nth_index<1>::type& name_index2=es.get<1>(); // same index

Additionally, the tag class template accepts several tags for one index, that are used interchangeably: for instance, the specification of index #1 in the previous example can be rewritten to hold two different tags name and by_name:

// tags
struct name{};
struct by_name{};

typedef multi_index_container<
  ...
    ordered_non_unique<
      tag<name,by_name>,
      member<employee,std::string,&employee::name>
    >
  ...
> employee_set;

Iterator access

Each index of a multi_index_container uses its own iterator types, which are different from those of another indices. As is the rule with STL containers, these iterators are defined as nested types of the index:

employee_set::nth_index<1>::type::iterator it=
  es.get<1>().find("Judy Smith");

This kind of expressions can be rendered more readable by means of user-defined typedefs:

typedef employee_set::nth_index<1>::type employee_set_by_name;
employee_set_by_name::iterator it=
  es.get<1>().find("Judy Smith");

Additionally, multi_index_containers provide shortcut definitions to the iterator types of their constituent indices:

employee_set::nth_index_iterator<1>::type it=
  es.get<1>().find("Judy Smith");

There is a variation of the expression above for use with tags:

employee_set::index_iterator<name>::type it=
  es.get<name>().find("Judy Smith");         // get<1> would also work

Index types

Currently, Boost.MultiIndex provides the following index types:

  • Ordered indices sort the elements like std::sets do and provide a similar interface. There are unique and non-unique variants: the former do not allow for duplicates, while the latter permit them (like std::multiset.)

  • Sequenced indices are modeled after the semantics and interface of std::list: they arrange the elements as if in a bidirectional list.

  • Hashed indices provide fast access to the elements through hashing techniques, in a similar way as non-standard hash_sets provided by some vendors. Recently, unordered associative containers have been proposed as part of an extension of the C++ standard library known in the standardization committee as TR1. Hashed indices closely model this proposal.

  • Random access indices provide an interface similar to that of sequenced indices, and additionally feature random access iterators and positional access to the elements.

The examples in the introduction exercise ordered and sequenced indices, which are the most commonly used; the other kinds of indices are presented in the index types section of the tutorial.

Ordered indices

Ordered indices sort the elements in a multi_index_container according to a specified key and an associated comparison predicate. These indices can be viewed as analogues of the standard container std::set, and in fact they do replicate its interface, albeit with some minor differences dictated by the general constraints of Boost.MultiIndex.

Unique and non-unique variants

Ordered indices are classified into unique, which prohibit two elements to have the same key value, and non-unique indices, which allow for duplicates. Consider again the definition

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >
  >
> employee_set;

In this instantiation of multi_index_container, the first index is to be treated as unique (since IDs are exclusive to each employee) and thus is declared using ordered_unique, whereas the second index is non-unique (as the possibility exists that say two John Smiths are hired in the same company), which is specified by the use of ordered_non_unique.

The classification of ordered indices in unique and non-unique has an impact on which elements are allowed to be inserted into a given multi_index_container; briefly put, unique ordered indices mimic the behavior of std::sets while non-unique ordered indices are similar to std::multisets. For instance, an employee_set can hold the objects employee(0,"George Brown") and employee(1,"George Brown"), but will not accept the insertion of an employee object whose ID coincides with that of some previously inserted employee.

More than one unique index can be specified. For instance, if we augment employee to include an additional member for the Social Security number, which is reasonably treated as unique, the following captures this design:

struct employee
{
  int         id;
  std::string name;
  int         ssnumber;

  employee(int id,const std::string& name,int ssnumber):
    id(id),name(name),ssnumber(ssnumber){}

  bool operator<(const employee& e)const{return id<e.id;}
};

typedef multi_index_container<
  employee,
  indexed_by<
    // sort by employee::operator<
    ordered_unique<identity<employee> >,

    // sort by less<string> on name
    ordered_non_unique<member<employee,std::string,&employee::name> >,

    // sort by less<int> on ssnumber
    ordered_unique<member<employee,int,&employee::ssnumber> >
  >
> employee_set;

Specification

Ordered index specifiers in indexed_by must conform to one of the following syntaxes:

(ordered_unique | ordered_non_unique)
  <[(tag)[,(key extractor)[,(comparison predicate)]]]>

(ordered_unique | ordered_non_unique)
  <[(key extractor)[,(comparison predicate)]]>

The first optional argument is used if tags are associated with the index. We now proceed to briefly discuss the remaining arguments of an ordered index specifier.

Key extraction

The first template parameter (or the second, if tags are supplied) in the specification of an ordered index provides a key extraction predicate. This predicate takes a whole element (in our example, a reference to an employee object) and returns the piece of information by which the sorting is performed. In most cases, one of the following two situations arises:

  • The whole element serves as the key, as is the case of the first index in employee_set. The predefined identity predicate can be used here as a key extractor; identity returns as the key the same object passed as argument.

  • The comparison is performed on a particular data member of the element; this closely follows the specification of indices on a column of a table in relational databases. Boost.MultiIndex provides member, which returns as the key a member of the element specified by a given pointer.

As an example, consider again the definition of employee_set. The definition of the first index:

ordered_unique<identity<employee> >

specifies by means of identity that element objects themselves serve as key for this index. On the other hand, in the second index:

ordered_non_unique<member<employee,std::string,&employee::name> >

Use member to extract the name part of the employee object. The key type of this index is then std::string.

Apart from identity and member, Boost.MultiIndex provides several other predefined key extractors and powerful ways to combine them. Key extractors can also be defined by the user. Consult the key extraction section of the tutorial for a more detailed exposition of this topic.

Comparison predicates

The last part of the specification of an ordered index is the associated comparison predicate, which must order the keys in a less-than fashion. These comparison predicates are not different from those used by STL containers like std::set. By default (i.e. if no comparison predicate is provided), an index with keys of type key_type sorts the elements by std::less<key_type>. Should other comparison criteria be needed, they can be specified as an additional parameter in the index declaration:

// define a multiply indexed set with indices by id and by name
// in reverse alphabetical order
typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >, // as usual
    ordered_non_unique<
      member<employee,std::string,&employee::name>,
      std::greater<std::string>  // default would be std::less<std::string>
    >
  >
> employee_set;

Special lookup operations

A given ordered index allows for lookup based on its key type, rather than the whole element. For instance, to find Veronica Cruz in an employee_set write:

employee_set es;
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name::iterator it=es.get<name>().find("Veronica Cruz");

As a plus, Boost.MultiIndex provides lookup operations accepting search keys different from the key_type of the index, which is a specially useful facility when key_type objects are expensive to create. Ordered STL containers fail to provide this functionality, which often leads to inelegant workarounds: consider for instance the problem of determining the employees whose IDs fall in the range [0,100]. Given that the key of employee_set index #0 is employee itself, on a first approach write the following:

employee_set::iterator p0=es.lower_bound(employee(0,""));
employee_set::iterator p1=es.upper_bound(employee(100,""));

Note however that std::less<employee> actually only depends on the IDs of the employees, so it would be more convenient to avoid the creation of entire employee objects just for the sake of their IDs. Boost.MultiIndex allows for this: define an appropriate comparison predicate

struct comp_id
{
  // compare an ID and an employee
  bool operator()(int x,const employee& e2)const{return x<e2.id;}

  // compare an employee and an ID
  bool operator()(const employee& e1,int x)const{return e1.id<x;}
};

and now write the search as

employee_set::iterator p0=es.lower_bound(0,comp_id());
employee_set::iterator p1=es.upper_bound(100,comp_id());

Here not only are the IDs passed instead of employee objects: an alternative comparison predicate is passed as well. In general, lookup operations of ordered indices are overloaded to accept compatible sorting criteria. The somewhat cumbersome definition of compatibility in this context is given in the reference, but roughly speaking say that a comparison predicate C1 is compatible with C2 if any sequence sorted by C2 is also sorted with respect to C1. The following shows a more interesting use of compatible predicates:

// sorting by name's initial
struct comp_initial
{
  bool operator()(char ch,const std::string& s)const{
    if(s.empty())return false;
    return ch<s[0];
  }

  bool operator()(const std::string& s,char ch)const{
    if(s.empty())return true;
    return s[0]<ch;
  }
};

// obtain first employee whose name begins with 'J' (ordered by name)
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();
employee_set_by_name::const_iterator it=
  name_index.lower_bound('J',comp_initial());

Retrieval of ranges

Range searching that is, the lookup of all elements in a given interval, is a very frequent operation for which standard lower_bound and upper_bound can be resorted to, though in a cumbersome manner. For instance, the following code retrieves the elements of an multi_index_container<double> in the interval [100,200]:

typedef multi_index_container<double> double_set;
// note: default template parameters resolve to
// multi_index_container<double,indexed_by<unique<identity<double> > > >.

double_set s;
...
double_set::iterator it0=s.lower_bound(100.0);
double_set::iterator it1=s.upper_bound(200.0);
// range [it0,it1) contains the elements in [100,200]

Subtle changes to the code are required when strict inequalities are considered. To retrieve the elements greater than 100 and less than 200, rewrite the code as:

double_set::iterator it0=s.upper_bound(100.0);
double_set::iterator it1=s.lower_bound(200.0);
// range [it0,it1) contains the elements in (100,200)

To add to this complexity, the programmer has to take into account that the lower and upper bounds of the interval searched are compatible: for instance, if the lower bound is 200 and the upper bound is 100, the iterators it0 and it1 produced by the code above will be in reverse order, with possibly catastrophic results if a traversal from it0 to it1 is tried. All these details make range searching a tedious and error prone task.

The range member function, often in combination with Boost.Lambda expressions, can greatly help alleviate this situation:

using namespace boost::lambda;

typedef multi_index_container<double> double_set;
double_set s;
...
std::pair<double_set::iterator,double_set::iterator> p=
  s.range(100.0<=_1,_1<=200); // 100<= x <=200
...
p=s.range(100.0<_1,_1<200);   // 100<  x < 200
...
p=s.range(100.0<=_1,_1<200);  // 100<= x < 200

range simply accepts predicates specifying the lower and upper bounds of the interval searched. Please consult the reference for a detailed explanation of the permissible predicates passed to range.

One or both bounds can be omitted with the special unbounded marker:

p=s.range(100.0<=_1,unbounded); // 100 <= x
p=s.range(unbounded,_1<200.0);  //   x <  200
p=s.range(unbounded,unbounded); // equiv. to std::make_pair(s.begin(),s.end())

Updating

The replace member function performs in-place replacement of a given element as the following example shows:

typedef index<employee_set,name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
employee anna=*it;
anna.name="Anna Smith";      // she just got married to Calvin Smith
name_index.replace(it,anna); // update her record

replace performs this substitution in such a manner that:

  • The complexity is constant time if the changed element retains its original order with respect to all indices; it is logarithmic otherwise.

  • Iterator and reference validity are preserved.

  • The operation is strongly exception-safe, i.e. the multi_index_contai ner remains unchanged if some exception (originated by the system or the user's data types) is thrown.

replace is a powerful operation not provided by standard STL containers, and one that is specially handy when strong exception-safety is required.

The observant reader might have noticed that the convenience of replace comes at a cost: namely the whole element has to be copied twice to do the updating (when retrieving it and inside replace). If elements are expensive to copy, this may be quite a computational cost for the modification of just a tiny part of the object. To cope with this situation, Boost.MultiIndex provides an alternative updating mechanism called modify:

struct change_name
{
  change_name(const std::string& new_name):new_name(new_name){}

  void operator()(employee& e)
  {
    e.name=new_name;
  }

private:
  std::string new_name;
};
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
name_index.modify(it,change_name("Anna Smith"));

modify accepts a functor (or pointer to function) that is passed a reference to the element to be changed, thus eliminating the need for spurious copies. Like replace, modify does preserve the internal orderings of all the indices of the multi_index_container. However, the semantics of modify is not entirely equivalent to replace. Consider what happens if a collision occurs as a result of modifying the element, i.e. the modified element clashes with another with respect to some unique ordered index. In the case of replace, the original value is kept and the method returns without altering the container, but modify cannot afford such an approach, since the modifying functor leaves no trace of the previous value of the element. Integrity constraints thus lead to the following policy: when a collision happens in the process of calling modify, the element is erased and the method returns false. This difference in behavior between replace and modify has to be considered by the programmer on a case-by-case basis.

A key-based version of modify, named modify_key , is provided as well. In this case, the modifying functor is passed a reference to the key_value part of the element instead of the whole object.

struct change_str
{
  change_str(const std::string& new_str):new_str(new_str){}

  // note this is passed a string, not an employee
  void operator()(std::string& str)
  {
    str=new_str;
  }

private:
  std::string new_str;
};
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
name_index.modify_key(it,change_str("Anna Smith"));

Just as modify does, modify_key erases the element if the modification results in collisions in some index. modify and modify_key are particularly well suited to use in conjunction to Boost.Lambda for defining the modifying functors:

using namespace boost::lambda;

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
name_index.modify_key(it,_1="Anna Smith");

modify_key requires that the key extractor be of a special type called read/write: this is usually, but not always, the case.

Sequenced indices

Unlike ordered indices, sequenced indices do not impose a fixed order on the elements: instead, these can be arranged in any position on the sequence, in the same way as std::list permits. The interface of sequenced indices is thus designed upon that of std::list; nearly every operation provided in the standard container is replicated here, occasionally with changes in the syntax and/or semantics to cope with the constraints imposed by Boost.MultiIndex. In particular, there is an important limitation of sequenced indices with respect to std::lists, namely that elements of an multi_index_container are not mutable through an iterator:

multi_index_container<
  int,
  indexed_by<sequenced<> >
> s;            // list-like container

s.push_front(0);
*(s.begin())=1; // ERROR: the element cannot be changed

That is, iterators of a sequenced index (of all types of indices, actually) point to constant elements. This limitation might come as a surprise, but it is imposed by the way multi_index_containers work; if elements were allowed to be changed in this manner, it could introduce inconsistencies in other ordered indices of the multi_index_container. Element modification can nevertheless be done by means of update operations.

Consider a multi_index_container with two or more indices, one of them of sequenced type. If an element is inserted through another index, then it will be automatically appended to the end of the sequenced index. An example will help to clarify this:

multi_index_container<
  int,
  indexed_by<
    sequenced<>,           // sequenced type
    ordered_unique<identity<int> > // another index
  >
> s;

s.get<1>().insert(1); // insert 1 through index #1
s.get<1>().insert(0); // insert 0 through index #1

// list elements through sequenced index #0
std::copy(s.begin(),s.end(),std::ostream_iterator<int>(std::cout));

// result: 1 0

Thus the behavior of sequenced indices when insertions are not made through them is to preserve insertion order.

Specification

Sequenced indices are specified with the sequenced construct:

sequenced<[(tag)]>

The tag parameter is optional.

List operations

As mentioned before, sequenced indices mimic the interface of std::list, and most of the original operations therein are provided as well. The semantics and complexity of these operations, however, do not always coincide with those of the standard container. Differences result mainly from the fact that insertions into a sequenced index are not guaranteed to succeed, due to the possible banning by other indices of the multi_index_container. Consult the reference for further details.

Updating

Like ordered indices, sequenced indices provide replace and modify operations, with identical functionality. There is however no analogous modify_key, since sequenced indices are not key-based.

Projection of iterators

Given indices i1 and i2 on the same multi_index_container, project can be used to retrieve an i2-iterator from an i1-iterator, both of them pointing to the same element of the container. This functionality allows the programmer to move between different indices of the same multi_index_container when performing elaborate operations:

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

// list employees by ID starting from Robert Brown's ID

employee_set_by_name::iterator it1=name_index.find("Robert Brown");

// obtain an iterator of index #0 from it1
employee_set::iterator it2=es.project<0>(it1);

std::copy(it2,es.end(),std::ostream_iterator<employee>(std::cout));

A slightly more interesting example:

text_container tc;

// get a view to index #1 (ordered index on the words)
text_container::nth_index<1>::type& sorted_index=tc.get<1>();

// prepend "older" to all occurrences of "sister"

text_container::nth_index_iterator<1>::type it1=
  sorted_index.lower_bound("sister");

while(it1!=sorted_index.end()&&*it1=="sister"){
  // convert to an iterator to the sequenced index
  text_container::iterator it2=tc.project<0>(it1);

  tc.insert(it2,"older");
  ++it1;
}

When provided, project can also be used with tags.

Complexity and exception safety

multi_index_container provides the same complexity and exception safety guarantees as the equivalent STL containers do. Iterator and reference validity is preserved in the face of insertions, even for replace and modify operations.

Appropriate instantiations of multi_index_container can in fact simulate , std::multiset and (with more limitations) std::list, as shown in the techniques section. These simulations are as nearly as efficient as the original STL containers; consult the reference for further information on complexity guarantees and the performance section for practical measurements of efficiency.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial - Container creation

Boost.MultiIndex Tutorial: Container creation

  • Value semantics

  • Use of ctor_args_list

  • Serialization

Value semantics

multi_index_containers have the usual value semantics associated to copy construction and assignment, i.e. copies of the elements from the source container are created and inserted into the destination container. More interestingly, copying also recreates the original order in which elements are arranged for every index of the container. This implies that equality of all indices is preserved under copying or assignment, for those index types where equality is defined. This behavior can be regarded as a natural extension to the general rule on copy semantics stating that if y is a copy of x, then y==x.

Use of ctor_args_list

Although in most cases multi_index_containers will be default constructed (or copied from a preexisting multi_index_container), sometimes it is necessary to specify particular values for the internal objects used (key extractors, comparison predicates, allocator), for instance if some of these objects do not have a default constructor. The same situation can arise with standard STL containers, which allow for the optional specification of such objects:

// example of non-default constructed std::set
template<typename IntegralType>
struct modulo_less
{
  modulo_less(IntegralType m):modulo(m){}

  bool operator()(IntegralType x,IntegralType y)const
  {
    return (x%modulo)<(y%modulo);
  }

private:
  IntegralType modulo;
};

typedef std::set<unsigned int,modulo_less<unsigned int> > modulo_set;

modulo_set m(modulo_less<unsigned int>(10));

multi_index_container does also provide this functionality, though in a considerably more complex fashion, due to the fact that the constructor of a multi_index_container has to accept values for all the internal objects of its indices. The full form of multi_index_container constructor is

explicit multi_index_container(
    const ctor_args_list& args_list=ctor_args_list(),
    const allocator_type& al=allocator_type());

The specification of the allocator object poses no particular problems; as for the ctor_args_list, this object is designed so as to hold the necessary construction values for every index in the multi_index_container. From the point of view of the user, ctor_args_list is equivalent to the type

boost::tuple<C0,...,CI-1>

where I is the number of indices, and Ci is

nth_index<i>::type::ctor_args

that is, the nested type ctor_args of the i-th index. Each ctor_args type is in turn a tuple holding values for constructor arguments of the associated index: so, ordered indices demand a key extractor object and a comparison predicate, hashed indices take an initial number of buckets, a key extractor, a hash function and an equality predicate; while sequenced and random access indices do not need any construction argument. For instance, given the definition

typedef multi_index_container<
  unsigned int,
  indexed_by<
    hashed_unique<identity<unsigned int> >,
    ordered_non_unique<identity<unsigned int>, modulo_less<unsigned int> >,
    sequenced<>,
    random_access<>
  >
> modulo_indexed_set;

the corresponding ctor_args_list type is equivalent to

boost::tuple<
  // ctr_args of index #0
  boost::tuple<
    std::size_t, // initial number of buckets; 0 if unspecified
    identity<unsigned int>,
    boost::hash<unsigned int>,
    std::equal_to<unsigned int> >,

  // ctr_args of index #1
  boost::tuple<
    identity<unsigned int>,
    modulo_less<unsigned int> >,

  // sequenced indices do not have any construction argument
  boost::tuple<>,

  // neither do random access indices
  boost::tuple<>
>

Such a modulo_indexed_set cannot be default constructed, because modulo_less does not provide a default constructor. The following shows how the construction can be done:

modulo_indexed_set::ctor_args_list args_list=
  boost::make_tuple(
    // ctor_args for index #0 is default constructible
    modulo_indexed_set::nth_index<0>::type::ctor_args(),

    boost::make_tuple(identity<unsigned int>(),modulo_less<unsigned int>(10)),

    // these are also default constructible (actually, empty tuples)
    modulo_indexed_set::nth_index<2>::type::ctor_args(),
    modulo_indexed_set::nth_index<3>::type::ctor_args()
  );

modulo_indexed_set m(args_list);

A program is provided in the examples section that puts in practice these concepts.

Serialization

multi_index_containers can be archived and retrieved by means of the Boost Serialization Library. Both regular and XML archives are supported. The usage is straightforward and does not differ from that of any other serializable type. For instance:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>

...

void save(const employee_set& es)
{
  std::ofstream ofs("data");
  boost::archive::text_oarchive oa(ofs);
  oa<<es;
}

void load(employee_set& es)
{
  std::ifstream ifs("data");
  boost::archive::text_iarchive ia(ifs);
  ia>>es;
}

...

employee_set es;
... // fill it with data
save(es);

...

employee_set restored_es;
load(restored_es);

Serialization capabilities are automatically provided by just linking with the appropriate Boost.Serialization library module: it is not necessary to explicitly include any header from Boost.Serialization, apart from those declaring the type of archive used in the process. If not used, however, serialization support can be disabled by globally defining the macro BOOST_MULTI_INDEX_DISABLE_SERIALIZATION. Disabling serialization for Boost.MultiIndex can yield a small improvement in build times, and may be necessary in those defective compilers that fail to correctly process Boost.Serialization headers.

In accordance with Boost.MultiIndex value semantics, retrieving an archived multi_index_container restores not only the elements, but also the order they were arranged into for every index of the container. There is an exception to this rule, though: for hashed indices, no guarantee is made about the order in which elements will be iterated in the restored container; in general, it is unwise to rely on the ordering of elements of a hashed index, since it can change in arbitrary ways during insertion or rehashing --this is precisely the reason why hashed indices and TR1 unordered associative containers do not define an equality operator.

Iterators to indices of a multi_index_container can also be serialized. Serialization of iterators must be done only after serializing their corresponding container.

Example 9 in the examples section shows the serialization capabilities of Boost.MultiIndex.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial -Debugging support

Boost.MultiIndex Tutorial: Debugging support

  • Debugging support

  • Safe mode

    • Serialization and safe mode

  • Invariant-checking mode

Debugging support

The concept of Design by Contract, originally developed as part of Bertrand Meyer's Eiffel language, revolves around the formulation of a contract between the user of a library and the implementor, by which the first is required to respect some preconditions on the values passed when invoking methods of the library, and the implementor guarantees in return that certain constraints on the results are met (postconditions), as well as the honoring of specified internal consistency rules, called invariants. Eiffel natively supports the three parts of the contract just described by means of constructs require, ensure and invariant, respectively.

C++ does not enjoy direct support for Design by Contract techniques: these are customarily implemented as assertion code, often turned off in release mode for performance reasons. Following this approach, Boost.MultiIndex provides two distinct debugging modes:

  • Safe mode checks preconditions on the invocations to the facilities of the library,

  • invariant-checking mode performs post-execution checks aimed at ensuring that the internal consistency of the library is preserved.

These two modes are independent of each other and can be set on or off individually. It is important to note that errors detected by safe mode are due in principle to faulty code in the user's program, while invariant-checking mode detects potential internal bugs in the implementation of Boost.MultiIndex.

Safe mode

The idea of adding precondition checking facilities to STL as a debugging aid was first introduced by Cay S. Horstmann in his Safe STL library and later adopted by STLport Debug Mode. Similarly, Boost.MultiIndex features the so-called safe mode in which all sorts of preconditions are checked when dealing with iterators and functions of the library.

Boost.MultiIndex safe mode is set by globally defining the macro BOOST_MULTI_INDEX_ENABLE_SAFE_MODE. Error conditions are checked via the macro BOOST_MULTI_INDEX_SAFE_MODE_ASSERT, which by default resolves to a call to BOOST_ASSERT .

If the user decides to define her own version of BOOST_MULTI_INDEX_SAFE_MODE_ASSERT, it has to take the form

BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code)

where expr is the condition checked and error_code is one value of the safe_mode::error_code enumeration:

namespace boost{

namespace multi_index{

namespace safe_mode{

enum error_code
{
  invalid_iterator,             // vg. default cted or pointing to erased element
  not_dereferenceable_iterator, // iterator is not dereferenceable
  not_incrementable_iterator,   // iterator points to end of sequence
  not_decrementable_iterator,   // iterator points to beginning of sequence
  not_owner,                    // iterator does not belong to the container
  not_same_owner,               // iterators belong to different containers
  invalid_range,                // last not reachable from first
  inside_range,                 // iterator lies within a range (and it mustn't)
  out_of_bounds,                // move attempted beyond container limits
  same_container                // containers ought to be different
};

} // namespace multi_index::safe_mode

} // namespace multi_index

} // namespace boost

For instance, the following replacement of BOOST_MULTI_INDEX_SAFE_MODE_ASSERT throws an exception instead of asserting:

#include <boost/multi_index_container/safe_mode_errors.hpp>

struct safe_mode_exception
{
  safe_mode_exception(boost::multi_index::safe_mode::error_code error_code):
    error_code(error_code)
  {}

  boost::multi_index::safe_mode::error_code error_code;
};

#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) \
if(!(expr)){throw safe_mode_exception(error_code);}

// This has to go before the inclusion of any header from Boost.MultiIndex,
// except possibly safe_error_codes.hpp.

Other possibilites, like outputting to a log or firing some kind of alert, are also implementable.

Warning: Safe mode adds a very important overhead to the program both in terms of space and time used, so in general it should not be set for NDEBUG builds. Also, this mode is intended solely as a debugging aid, and programs must not rely on it as part of their normal execution flow: in particular, no guarantee is made that all possible precondition errors are diagnosed, or that the checks remain stable across different versions of the library.

Serialization and safe mode

Iterators restored from an archive are not subject to safe mode checks. This is so because it is not possible to automatically know the associated multi_index_container of an iterator from the serialization information alone. However, if desired, a restored iterator can be converted to a checked value by using the following workaround:

employee_set es;
employee_set::nth_index<1>::iterator it;

// restore es and it from an archive ar
ar>>es;
ar>>it; // it won't benefit from safe mode checks

// Turn it into a checked value by providing Boost.MultiIndex
// with info about the associated container.
// This statement has virtually zero cost if safe mode is turned off.
it=es.project<1>(it);

Invariant-checking mode

The so called invariant-checking mode of Boost.MultiIndex can be set by globally defining the macro BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING. When this mode is in effect, all public functions of Boost.MultiIndex will perform post-execution tests aimed at ensuring that the basic internal invariants of the data structures managed are preserved.

If an invariant test fails, Boost.MultiIndex will indicate the failure by means of the unary macro BOOST_MULTI_INDEX_INVARIANT_ASSERT. Unless the user provides a definition for this macro, it defaults to BOOST_ASSERT . Any assertion of this kind should be regarded in principle as a bug in the library. Please report such problems, along with as much contextual information as possible, to the maintainer of the library.

It is recommended that users of Boost.MultiIndex always set the invariant-checking mode in debug builds.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial

Boost.MultiIndex Tutorial

  • Rationale

  • Namespace

  • Basics

  • Index types

  • Key extraction

  • Container creation

  • Debugging support

  • Techniques

Rationale

STL containers are designed around the concept that each container controls its own collection of elements, giving access to them in a manner specified by the container's type: so, an std::set maintains the elements ordered by a specified sorting criterion, std::list allows for free positioning of elements along a linear sequence, and so on.

Sometimes, the necessity arises of having different access interfaces to the same underlying collection: for instance, some data might need to be sorted according to more than one comparison predicate, or a bidirectional list might benefit from a supplemental logarithmic lookup interface. In these situations, programmers typically resort to manual compositions of different containers, a solution that generally involves a fair amount of code devoted to preserve the synchronization of the different parts of the composition. Boost.MultiIndex allows for the specification of multi_index_containers comprised of one or more indices with different interfaces to the same collection of elements. The resulting constructs are conceptually cleaner than manual compositions, and often perform much better. An important design decision has been taken that the indices of a given multi_index_container instantiation be specified at compile time: this gives ample room for static type checking and code optimization.

Boost.MultiIndex takes inspiration from basic concepts of indexing arising in the theory of relational databases, though it is not intended to provide a full-fledged relational database framework. multi_index_container integrates seamlessly into the STL container/algorithm design, and features some extra capabilities regarding lookup operations and element updating which are useful extensions even for single-indexed containers.

diagram of a multi_index_container with three indices
Fig. 1: Diagram of a multi_index_container with three indices.

The figure above depicts a multi_index_container composed of three indices: the first two present a set-like interface to the elements sorted by shape and id, respectively, while the latter index provides the functionality of a bidirectional list in the spirit of std::list. These indices act as "views" to the internal collection of elements, but they do not only provide read access to the set: insertion/deletion methods are also implemented much as those of std::sets or std::lists. Insertion of an element through one given index will only succeed if the uniqueness constraints of all indices are met.

Namespace

All the public types of Boost.MultiIndex reside in namespace ::boost::multi_index. Additionaly, the main class template multi_index_container and global functions get and project are lifted to namespace ::boost by means of using declarations. For brevity of exposition, the fragments of code in the documentation are written as if the following declarations were in effect:

using namespace ::boost;
using namespace ::boost::multi_index;

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial - Index types

Boost.MultiIndex Tutorial: Index types

  • Classification

  • Hashed indices

    • Unique and non-unique variants

    • Specification

    • Lookup

    • Updating

    • Guarantees on iterator validity and exception safety

  • Random access indices

    • Specification

    • Interface

    • Comparison with std::vector

  • Index rearranging

  • Ordered indices node compression

Classification

Boost.MultiIndex provides six different index types, which can be classified as shown in the table below. Ordered and sequenced indices, which are the most commonly used, have been explained in the basics section; the rest of index types can be regarded as variations of the former providing some added benefits, functionally or in the area of performance.

Boost.MultiIndex indices
type |  | specifier
key-based | ordered | ordered_unique
 |  | ordered_non_unique
 | hashed | hashed_unique
 |  | hashed_non_unique
non key-based |  | sequenced
 |  | random_access

Key-based indices, of which ordered indices are the usual example, provide efficient lookup of elements based on some piece of information called the element key: There is an extensive suite of key extraction utility classes allowing for the specification of such keys. Fast lookup imposes an internally managed order on these indices that the user is not allowed to modify; non key-based indices, on the other hand, can be freely rearranged at the expense of lacking lookup facilities. Sequenced indices, modeled after the interface of std::list, are the customary example of a non key-based index.

Hashed indices

Hashed indices constitute a trade-off with respect to ordered indices: if correctly used, they provide much faster lookup of elements, at the expense of losing sorting information. Revisit the employee_set example: suppose a field for storing the Social Security number is added, with the requisite that lookup by this number should be as fast as possible. Instead of the usual ordered index, a hashed index can be resorted to:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>

struct employee
{
  int         id;
  std::string name;
  int         ssnumber;

  employee(int id,const std::string& name,int ssnumber):
    id(id),name(name),ssnumber(ssnumber){}

  bool operator<(const employee& e)const{return id<e.id;}
};

typedef multi_index_container<
  employee,
  indexed_by<
    // sort by employee::operator<
    ordered_unique<identity<employee> >,

    // sort by less<string> on name
    ordered_non_unique<member<employee,std::string,&employee::name> >,

    // hashed on ssnumber
    hashed_unique<member<employee,int,&employee::ssnumber> >
  >
> employee_set

Note that the hashed index does not guarantee any particular ordering of the elements: so, for instance, the user cannot efficiently query the employees whose SSN is greater than a given number. Consider these restrictions when determining whether a hashed index is preferred over an ordered one.

If familiar with non-standard hash_sets provided by some compiler vendors, then learning to use hashed indices should be straightforward. However, the interface of hashed indices is modeled after the specification for unordered associative containers by the C++ Standard Library Technical Report (TR1), which differs in some significant aspects from existing pre-standard implementations:

  • As there is no notion of ordering between keys, the lookup interface does not offer lower_bound or upper_bound member functions (unlike Dinkumware's solution.)

  • A set of member functions is provided for handling the internal bucket structure on which hashed indices rely. This includes facilities for rehashing, control of the load factor (number of elements divided by number of buckets), and inspection of the buckets contents. Pre-standard implementations do not have such an extensive functionality.

Check the reference for a complete specification of the interface of hashed indices, and example 8 and example 9 for practical applications.

Unique and non-unique variants

Just like ordered indices, hashed indices have unique and non-unique variants, selected with the specifiers hashed_unique and hashed_non_unique, respectively. In the latter case, elements with equivalent keys are kept together and can be jointly retrieved by means of the equal_range member function.

Specification

Hashed indices specifiers have two alternative syntaxes, depending on whether tags are provided or not:

(hashed_unique | hashed_non_unique)
  <[(tag)[,(key extractor)[,(hash function)[,(equality predicate)]]]]>

(hashed_unique | hashed_non_unique)
  <[(key extractor)[,(hash function)[,(equality predicate)]]]>

The key extractor parameter works in exactly the same way as for ordered indices; lookup, insertion, etc., are based on the key returned by the extractor rather than the whole element.

The hash function is the very core of the fast lookup capabilities of this type of indices: a hasher is just a Unary Function returning an std::size_t value for any given key. In general, it is impossible that every key map to a different hash value, for the space of keys can be greater than the number of permissible hash codes: what makes for a good hasher is that the probability of a collision (two different keys with the same hash value) is as close to zero as possible. This is a statistical property depending on the typical distribution of keys in a given application, so it is not feasible to have a general-purpose hash function with excellent results in every possible scenario; the default value for this parameter uses Boost.Hash, which often provides good enough results.

The equality predicate is used to determine whether two keys are to be treated as the same. The default value std::equal_to<KeyFromValue::result_type> is in most cases exactly what is needed, so the user very rarely provides an own predicate. Note that hashed indices require that two equivalent keys have the same hash value, which in practice reduces the freedom in choosing an equality predicate.

Lookup

The lookup interface of hashed indices consists in member functions find, count and equal_range. Note that lower_bound and upper_bound are not provided, as there is no intrinsic ordering of keys in this type of indices.

Just as with ordered indices, these member functions take keys as their search arguments, rather than entire objects. Remember that ordered indices lookup operations are further augmented to accept compatible keys, which can roughly be regarded as "subkeys". For hashed indices, a concept of compatible key is also supported, though its usefulness is much more limited: basically, a compatible key is an object which is entirely equivalent to a native object of key_type value, though maybe with a different internal representation:

// US SSN numbering scheme
struct ssn
{
  ssn(int area_no,int group_no,int serial_no):
    area_no(area_no),group_no(group_no),serial_no(serial_no)
  {}

  int to_int()const
  {
    return serial_no+10000*group_no+1000000*area_no;
  }

private:
  int area_no;
  int group_no;
  int serial_no;
};

// interoperability with SSNs in raw int form

struct ssn_equal
{
  bool operator()(const ssn& x,int y)const
  {
    return x.to_int()==y;
  }

  bool operator()(int x,const ssn& y)const
  {
    return x==y.to_int();
  }
};

struct ssn_hash
{
  std::size_t operator()(const ssn& x)const
  {
    return boost::hash<int>()(x.to_int());
  }

  std::size_t operator()(int x)const
  {
    return boost::hash<int>()(x);
  }
};

typedef employee_set::nth_index<2>::type employee_set_by_ssn;

employee_set         es;
employee_set_by_ssn& ssn_index=es.get<2>();
...
// find an employee by ssn
employee e=*(ssn_index.find(ssn(12,1005,20678),ssn_hash(),ssn_equal()));

In the example, provided a hash functor ssn_hash and an equality predicate ssn_equal allowing for interoperability between ssn objects and the raw ints stored as SSNs in employee_set.

By far, the most useful application of compatible keys in the context of hashed indices lies in the fact that they allow for seamless usage of composite keys.

Updating

Hashed indices have replace , modify and modify_key member functions, with the same functionality as in ordered indices.

Guarantees on iterator validity and exception safety

Due to the internal constraints imposed by the Boost.MultiIndex framework, hashed indices provide guarantees on iterator validity and exception safety that are actually stronger than required by the C++ Standard Library Technical Report (TR1) with respect to unordered associative containers:

  • Iterator validity is preserved in any case during insertion or rehashing: TR1 allows for iterator invalidation when a rehash (implicit or explicit) is performed.

  • Erasing an element or range of elements via iterators does not throw ever, as the internal hash function and equality predicate objects are not actually invoked.

  • rehash provides the strong exception safety guarantee unconditionally. TR1 only warrants it if the internal hash function and equality predicate objects do not throw. The somewhat surprising consequence is that a TR1-compliant unordered associative container might erase elements if an exception is thrown during rehashing!

In general, these stronger guarantees play in favor of the user's convenience, specially that which refers to iterator stability. A (hopefully minimal) degradation in performance might result in exchange for these commodities.

Random access indices

Random access indices offer the same kind of functionality as sequenced indices, with the extra advantages that their iterators are random access, and operator[] and at() are provided for accessing elements based on their position in the index. Let us rewrite a container used in a previous example, using random access instead of sequenced indices:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>

// text container with fast lookup based on a random access index
typedef multi_index_container<
  std::string,
  indexed_by<
    random_access<>,
    ordered_non_unique<identity<std::string> >
  >
> text_container;

// global text container object
text_container tc;

Random access capabilities allow the user to write a code like the following:

void print_page(std::size_t page_num)
{
  static const std::size_t words_per_page=50;

  std::size_t pos0=std::min(tc.size(),page_num*words_per_page);
  std::size_t pos1=std::min(tc.size(),pos0+words_per_page);

  // note random access iterators can be added offsets
  std::copy(
    tc.begin()+pos0,tc.begin()+pos1,
    std::ostream_iterator<std::string>(std::cout));
}

void print_random_word()
{
  std::cout<<tc[rand()%tc.size()];
}

This added flexibility comes at a price: insertions and deletions at positions other than the end of the index have linear complexity, whereas these operations are constant time for sequenced indices. This situation is reminiscent of the differences in complexity behavior between std::list and std::vector: in the case of random access indices, however, insertions and deletions never incur any element copying, so the actual performance of these operations can be acceptable, despite the theoretical disadvantage with respect to sequenced indices.

Example 10 and example 11 in the examples section put random access indices in practice.

Specification

Random access indices are specified with the random_access construct, where the tag parameter is, as usual, optional:

random_access<[(tag)]>

Interface

All public functions offered by sequenced indices are also provided by random access indices, so that the latter can act as a drop-in replacement of the former (save with respect to their complexity bounds, as explained above). Besides, random access indices have operator[] and at() for positional access to the elements, and member functions capacity and reserve that control internal reallocation in a similar manner as the homonym facilities in std::vect or. Check the reference for details.

Comparison with std::vector

It is tempting to see random access indices as an analogue of std::vector for use in Boost.MultiIndex, but this metaphor can be misleading, as both constructs, though similar in many respects, show important semantic differences. An advantage of random access indices is that their iterators, as well as references to their elements, are stable, that is, they remain valid after any insertions or deletions. On the other hand, random access indices have several disadvantages with respect to std::vectors:

  • They do not provide memory contiguity, a property of std::vectors by which elements are stored adjacent to one another in a single block of memory.

  • As usual in Boost.MultiIndex, elements of random access indices are immutable and can only be modified through member functions replace and modify . This precludes the usage of many mutating algorithms that are nonetheless applicable to std::vectors.

The latter shortcoming can be partially remedied by means of the rearranging interface these indices provide.

In general, it is more instructive to regard random access indices as a variation of sequenced indices providing random access semantics, instead of insisting on the std::vector analogy.

Index rearranging

By design, index elements are immutable, i.e. iterators only grant const access to them, and only through the provided updating interface (replace, modify and modify_key) can the elements be modified. This restriction is set up so that the internal invariants of key-based indices are not broken (for instance, ascending order traversal in ordered indices), but induces important limitations in non key-based indices:

typedef multi_index_container<
  int,
  indexed_by<
    random_access<>,
    ordered_unique<identity<int> >
  >
> container;

container c;
...
// compiler error: assignment to read-only objects
std::random_shuffle(c.begin(),c.end());

What is unfortunate about the previous example is that the operation performed by std::random_shuffle is potentially compatible with multi_index_container invariants, as its result can be described by a permutation of the elements in the random access index with no actual modifications to the elements themselves. There are many more examples of such compatible algorithms in the C++ standard library, like for instance all sorting and partition functions.

Sequenced and random access indices provide a means to take advantage of such external algorithms. In order to introduce this facility we need a preliminary concept: a view of an index is defined as some iterator range [first,last) over the elements of the index such that all its elements are contained in the range exactly once. Continuing with the example, apply std::random_suffle on an ad hoc view obtained from the container:

// note that the elements of the view are not copies of the elements
// in c, but references to them
std::vector<boost::reference_wrapper<const int> > v;
BOOST_FOREACH(const int& i,c)v.push_back(boost::cref(i));

// this compiles OK, as reference_wrappers are assignable
std::random_shuffle(v.begin(),v.end());

Elements of v are reference_wrappers (from Boost.Ref) to the actual elements in the multi-index container. These objects still do not allow modification of the referenced entities, but they are Assignable, which is the only requirement std::random_suffle imposes. Once the desired rearrange is stored in the view, transfer it to the container with

c.rearrange(v.begin());

rearrange accepts an input iterator signaling the beginning of the external view (and end iterator is not needed since the length of the view is the same as that of the index) and internally relocates the elements of the index so that their traversal order matches the view. Albeit with some circumventions, rearrange allows for the application of a varied range of algorithms to non key-based indices. Please note that the view concept is very general, and in no way tied to the particular implementation example shown above. For instance, indices of a multi_index_container are indeed views with respect to its non key-based indices:

// rearrange as index #1 (ascending order)
c.rearrange(c.get<1>().begin());

// rearrange in descending order
c.rearrange(c.get<1>().rbegin());

The only important requirement imposed on views is that they must be free, i.e. they are not affected by relocations on the base index: thus, rearrange does not accept the following:

// undefined behavior: [rbegin(),rend()) is not free with respect
// to the base index
c.rearrange(c.rbegin());

The view concept is defined in detail in the reference. See example 11 in the examples section for a demonstration of use of rearrange.

Ordered indices node compression

Ordered indices are implemented by means of a data structure known as a red-black tree. Nodes of a red-black tree contain pointers to the parent and the two children nodes, plus a 1-bit field referred to as the node color (hence the name of the structure). Due to alignment issues, on most architectures the color field occupies one entire word, that is, 4 bytes in 32-bit systems and 8 bytes in 64-bit environments. This waste of space can be avoided by embedding the color bit inside one of the node pointers, provided not all the bits of the pointer representation contain useful information: this is precisely the case in many architectures where such nodes are aligned to even addresses, which implies that the least significant bit of the address must always be zero.

Boost.MultiIndex ordered indices implement this type of node compression whenever applicable. As compared with common implementations of the STL container std::set, node compression can result in a reduction of header overload by 25% (from 16 to 12 bytes on typical 32-bit architectures, and from 32 to 24 bytes on 64-bit systems). The impact on performance of this optimization has been checked to be negligible for moderately sized containers, whereas containers with many elements (hundreds of thousands or more) perform faster with this optimization, most likely due to L1 and L2 cache effects.

Node compression can be disabled by globally setting the macro BOOST_MULTI_INDEX_DISABLE_COMPRESSED_ORDERED_INDEX_NODES.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial - Key extraction

Boost.MultiIndex Tutorial: Key extraction

  • Introduction

    • Read/write key extractors

  • Predefined key extractors

    • identity

    • member

    • const_mem_fun and mem_fun

  • User-defined key extractors

  • Composite keys

    • Composite keys and hashed indices

  • Advanced features of Boost.MultiIndex key extractors

Introduction

STL associative containers have a notion of key, albeit in a somewhat incipient form. So, the keys of such containers are identified by a nested type key_type; for std::sets and std::multisets, key_type coincides with value_type, i.e. the key is the element itself. std::map and std::multimap manage elements of type std::pair<const Key,T>, where the first member is the key. In either case, the process of obtaining the key from a given element is implicitly fixed and cannot be customized by the user.

Fixed key extraction mechanisms like those performed by STL associative containers do not scale well in the context of Boost.MultiIndex, where several indices share their value_type definition but might feature completely different lookup semantics. For this reason, Boost.MultiIndex formalizes the concept of a Key Extractor in order to make it explicit and controllable in the definition of key-based indices.

Intuitively speaking, a key extractor is a function object that accepts a reference to an element and returns its associated key. The formal concept also imposes some reasonable constraints about the stability of the process, in the sense that extractors are assumed to return the same key when passed the same element: this is in consonance with the informal understanding that keys are actually some "part" of the element and do not depend on external data.

Read/write key extractors

A key extractor is called read/write if it returns a non-constant reference to the key when passed a non-constant element, and it is called read-only otherwise. Boost.MultiIndex requires that the key extractor be read/write when using the modify_key member function of ordered and hashed indices. In all other situations, read-only extractors suffice. The section on advanced features of Boost.MultiIndex key extractors details which of the predefined key extractors are read/write.

Predefined key extractors

identity

The identity key extractor returns the entire base object as the associated key:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>

multi_index_container<
  int,
  indexed_by<
    ordered_unique<
      identity<int> // the key is the entire element
    >
  >
> cont;

member

member key extractors return a reference to a specified data field of the base object. For instance, in the following version of the familiar employee container:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >,
    ordered_unique<member<employee,int,&employee::ssnumber> >
  >
> employee_set;

the second and third indices use member extractors on employee::name and employee::ssnumber, respectively. The specification of an instantiation of member is simple yet a little contrived:

member<(base type),(key type),(pointer to member)>

It might seem that the first and second parameters are superfluous, since the type of the base object and of the associated data field are already implicit in the pointer to member argument: unfortunately, it is not possible to extract this information with current C++ mechanisms, which makes the syntax of member a little too verbose.

const_mem_fun and mem_fun

Sometimes, the key of an index is not a concrete data member of the element, but rather it is a value returned by a particular member function. This resembles the notion of calculated indices supported by some relational databases. Boost.MultiIndex supports this kind of key extraction through const_mem_fun . Consider the following container where sorting on the third index is based upon the length of the name field:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>

struct employee
{
  int         id;
  std::string name;

  employee(int id,const std::string& name):id(id),name(name){}

  bool operator<(const employee& e)const{return id<e.id;}

  // returns the length of the name field
  std::size_t name_length()const{return name.size();}
};

typedef multi_index_container<
  employee,
  indexed_by<
    // sort by employee::operator<
    ordered_unique<identity<employee> >,

    // sort by less<string> on name
    ordered_non_unique<member<employee,std::string,&employee::name> >,

    // sort by less<int> on name_length()
    ordered_non_unique<
      const_mem_fun<employee,std::size_t,&employee::name_length>
    >
  >
> employee_set;

const_mem_fun usage syntax is similar to that of member:

const_mem_fun<(base type),(key type),(pointer to member function)>

The member function referred to must be const, take no arguments and return a value of the specified key type. The user almost always uses a const member function, since elements in a multi_index_container are treated as constant, much as elements of an std::set. However, a mem_fun counterpart is provided for use with non-constant member functions, whose applicability is discussed on the paragraph on advanced features of Boost.MultiIndex key extractors.

Example 2 in the examples section provides a complete program showing how to use const_mem_fun.

User-defined key extractors

Although the predefined key extractors provided by Boost.MultiIndex are intended to serve most cases, the user can also provide her own key extractors in more exotic situations, as long as these conform to the Key Extractor concept.

// some record class
struct record
{
  boost::gregorian::date d;
  std::string            str;
};

// extracts a record's year
struct record_year
{
  // result_type typedef required by Key Extractor concept
  typedef boost::gregorian::greg_year result_type;

  result_type operator()(const record& r)const // operator() must be const
  {
    return r.d.year();
  }
};

// example of use of the previous key extractor
typedef multi_index_container<
  record,
  indexed_by<
    ordered_non_unique<record_year> // sorted by record's year
  >
> record_log;

Example 6 in the examples section applies some user-defined key extractors in a complex scenario where keys are accessed via pointers.

Composite keys

In relational databases, composite keys depend on two or more fields of a given table. The analogous concept in Boost.MultiIndex is modeled by means of composite_key , as shown in the example:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>

struct phonebook_entry
{
  std::string family_name;
  std::string given_name;
  std::string phone_number;

  phonebook_entry(
    std::string family_name,
    std::string given_name,
    std::string phone_number):
    family_name(family_name),given_name(given_name),phone_number(phone_number)
  {}
};

// define a multi_index_container with a composite key on
// (family_name,given_name)
typedef multi_index_container<
  phonebook_entry,
  indexed_by<
    //non-unique as some subscribers might have more than one number
    ordered_non_unique<
      composite_key<
        phonebook_entry,
        member<phonebook_entry,std::string,&phonebook_entry::family_name>,
        member<phonebook_entry,std::string,&phonebook_entry::given_name>
      >
    >,
    ordered_unique< // unique as numbers belong to only one subscriber
      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
    >
  >
> phonebook;

composite_key accepts two or more key extractors on the same value (here, phonebook_entry). Lookup operations on a composite key are accomplished by passing tuples with the values searched:

phonebook pb;
...
// search for Dorothea White's number
phonebook::iterator it=pb.find(boost::make_tuple("White","Dorothea"));
std::string number=it->phone_number;

Composite keys are sorted by lexicographical order, i.e. sorting is performed by the first key, then the second key if the first one is equal, etc. This order allows for partial searches where only the first keys are specified:

phonebook pb;
...
// look for all Whites
std::pair<phonebook::iterator,phonebook::iterator> p=
  pb.equal_range(boost::make_tuple("White"));

As a notational convenience, when only the first key is specified it is possible to pass the argument directly without including it into a tuple:

phonebook pb;
...
// look for all Whites
std::pair<phonebook::iterator,phonebook::iterator> p=pb.equal_range("White");

On the other hand, partial searches without specifying the first keys are not allowed.

By default, the corresponding std::less predicate is used for each subkey of a composite key. Alternate comparison predicates can be specified with composite_key_compare :

// phonebook with given names in reverse order

typedef multi_index_container<
  phonebook_entry,
  indexed_by<
    ordered_non_unique<
      composite_key<
        phonebook_entry,
        member<phonebook_entry,std::string,&phonebook_entry::family_name>,
        member<phonebook_entry,std::string,&phonebook_entry::given_name>
      >,
      composite_key_compare<
        std::less<std::string>,   // family names sorted as by default
        std::greater<std::string> // given names reversed
      >
    >,
    ordered_unique<
      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
    >
  >
> phonebook;

See example 7 in the examples section for an application of composite_key.

Composite keys and hashed indices

Composite keys can also be used with hashed indices in a straightforward manner:

struct street_entry
{
  // quadrant coordinates
  int x;
  int y;

  std::string name;

  street_entry(int x,int y,const std::string& name):x(x),y(y),name(name){}
};

typedef multi_index_container<
  street_entry,
  indexed_by<
    hashed_non_unique< // indexed by quadrant coordinates
      composite_key<
        street_entry,
        member<street_entry,int,&street_entry::x>,
        member<street_entry,int,&street_entry::y>
      >
    >,
    hashed_non_unique< // indexed by street name
      member<street_entry,std::string,&street_entry::name>
    >
  >
> street_locator;

street_locator sl;
...
void streets_in_quadrant(int x,int y)
{
  std::pair<street_locator::iterator,street_locator::iterator> p=
    sl.equal_range(boost::make_tuple(x,y));

  while(p.first!=p.second){
    std::cout<<p.first->name<<std::endl;
    ++p.first;
  }
}

Note that hashing is automatically taken care of: boost::hash is specialized to hash a composite key as a function of the boost::hash values of its elements. Should we need to specify different hash functions for the elements of a composite key, we can explicitly do so by using the composite_key_hash utility:

struct tuned_int_hash
{
  int operator()(int x)const
  {
    // specially tuned hash for this application
  }
};

typedef multi_index_container<
  street_entry,
  indexed_by<
    hashed_non_unique< // indexed by quadrant coordinates
      composite_key<
        street_entry,
        member<street_entry,int,&street_entry::x>,
        member<street_entry,int,&street_entry::y>
      >,
      composite_key_hash<
        tuned_int_hash,
        tuned_int_hash
      >
    >,
    hashed_non_unique< // indexed by street name
      member<street_entry,std::string,&street_entry::name>
    >
  >
> street_locator;

Also, equality of composite keys can be tuned with composite_key_equal_to , though in most cases the default equality predicate (relying on the std::equal_to instantiations for the element types) will be the right choice.

Unlike with ordered indices, the user cannot perform partial searches specifying only the first elements of a composite key:

// try to locate streets in quadrants with x==0
// compile-time error: hashed indices do not allow such operations
std::pair<street_locator::iterator,street_locator::iterator> p=
  sl.equal_range(boost::make_tuple(0));

The reason for this limitation is quite logical: as the hash value of a composite key depends on all of its elements, it is impossible to calculate it from partial information.

Advanced features of Boost.MultiIndex key extractors

The Key Extractor concept allows the same object to extract keys from several different types, possibly through suitably defined overloads of operator():

// example of a name extractor from employee and employee *
struct name_extractor
{
  typedef std::string result_type;

  const result_type& operator()(const employee& e)const{return e.name;}
  result_type&       operator()(employee* e)const{return e->name;}
};

// name_extractor can handle elements of type employee...
typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<name_extractor>
  >
> employee_set;

// ...as well as elements of type employee *
typedef multi_index_container<
  employee*,
  indexed_by<
    ordered_unique<name_extractor>
  >
> employee_ptr_set;

This possibility is fully exploited by predefined key extractors provided by Boost.MultiIndex, making it simpler to define multi_index_containers where elements are pointers or references to the actual objects. The following specifies a multi_index_container of pointers to employees sorted by their names.

typedef multi_index_container<
  employee *,
  indexed_by<
    ordered_non_unique<member<employee,std::string,&employee::name> > >
> employee_set;

Note that this is specified in exactly the same manner as a multi_index_container of actual employee objects: member takes care of the extra dereferencing needed to gain access to employee::name. A similar functionality is provided for interoperability with reference wrappers from Boost.Ref:

typedef multi_index_container<
  boost::reference_wrapper<const employee>,
  indexed_by<
    ordered_non_unique<member<employee,std::string,&employee::name> > >
> employee_set;

In fact, support for pointers is further extended to accept chained pointers. Such a chained pointer is defined by induction as a raw or smart pointer or iterator to the actual element, to a reference wrapper of the element or to another chained pointer; that is, chained pointers are arbitrary compositions of pointer-like types ultimately dereferencing to the element from where the key is to be extracted. Examples of chained pointers to employee are:

  • employee *,

  • const employee *,

  • std::auto_ptr<employee>,

  • std::list<boost::reference_wrapper<employee> >::iterator,

  • employee **,

  • boost::shared_ptr<const employee *>.

In general, chained pointers with dereferencing distance greater than 1 are not likely to be used in a normal program, but they can arise in frameworks which construct "views" as multi_index_containers from preexisting multi_index_containers.

In order to present a short summary of the different usages of Boost.MultiIndex key extractors in the presence of reference wrappers and pointers, consider the following final type:

struct T
{
  int       i;
  const int j;
  int       f()const;
  int       g();
};

The table below lists the appropriate key extractors to be used for different pointer and reference wrapper types based on T, for each of its members.

Use cases for Boost.MultiIndex key extractors.
element type key key extractor applicable to
const elements? 		read/write?
T | i | member<T,int,&T::i> | yes | yes
j | member<T,const int,&T::j> | yes | no
f() | const_mem_fun<T,int,&T::f> | yes | no
g() | mem_fun<T,int,&T::g> | no | no
reference_wrapper<T> | i | member<T,int,&T::i> | yes | yes
j | member<T,const int,&T::j> | yes | no
f() | const_mem_fun<T,int,&T::f> | yes | no
g() | mem_fun<T,int,&T::g> | yes | no
reference_wrapper<const T> | i | member<T,const int,&T::i> | yes | no
j | member<T,const int,&T::j> | yes | no
f() | const_mem_fun<T,int,&T::f> | yes | no

g()

chained pointer to T
or to reference_wrapper<T> 		i member<T,int,&T::i> yes yes
j | member<T,const int,&T::j> | yes | no
f() | const_mem_fun<T,int,&T::f> | yes | no
g() | mem_fun<T,int,&T::g> | yes | no
chained pointer to const T
or to reference_wrapper<const T> 		i member<T,const int,&T::i> yes no
j | member<T,const int,&T::j> | yes | no
f() | const_mem_fun<T,int,&T::f> | yes | no
g()

The column "applicable to const elements?" states whether the corresponding key extractor can be used when passed constant elements (this relates to the elements specified in the first column, not the referenced T objects). The only negative case is for T::g when the elements are raw T objects, which make sense as we are dealing with a non-constant member function: this also implies that multi_index_containers of elements of T cannot be sorted by T::g, because elements contained within a multi_index_container are treated as constant.

The column "read/write?" shows which combinations yield read/write key extractors.

Some care has to be taken to preserve const-correctness in the specification of the key extractors: in some sense, the const qualifier is carried along to the member part, even if that particular member is not defined as const. For instance, if the elements are of type const T *, sorting by T::i is not specified as member<const T,int,&T::i>, but rather as member<T,const int,&T::i>.

For practical demonstrations of use of these key extractors, refer to example 2 and example 6 in the examples section.

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost.MultiIndex Documentation - Tutorial - Techniques

Boost.MultiIndex Tutorial: Techniques

  • Emulating standard containers with multi_index_container

    • Emulation of associative containers

    • Emulation of std::list

  • Metaprogramming and multi_index_container

    • MPL analysis

    • MPL synthesis

Emulating standard containers with multi_index_container

Emulation of associative containers

Academic motivations aside, there is a practical interest in emulating standard associative containers by means of multi_index_container, to take advantage of extended functionalities provided by multi_index_container for lookup, range querying and updating.

In order to emulate a std::set one can follow the substitution rule:

std::set<Key,Compare,Allocator> ->
  multi_index_container<
    Key,
    indexed_by<ordered_unique<identity<Key>,Compare> >,
    Allocator
  >

In the default case where Compare=std::less<Key> and Allocator=std::allocator<Key>, the substitution rule is simplified as

std::set<Key> -> multi_index_container<Key>

The substitution of multi_index_container for std::set keeps the whole set of functionality provided by std::set, so in principle it is a drop-in replacement needing no further adjustments.

std::multiset can be emulated in a similar manner, according to the following rule:

std::multiset<Key,Compare,Allocator> ->
  multi_index_container<
    Key,
    indexed_by<ordered_non_unique<identity<Key>,Compare> >,
    Allocator
  >

When default values are taken into consideration, the rule takes the form

std::multiset<Key> ->
  multi_index_container<
    Key,
    indexed_by<ordered_non_unique<identity<Key> > >
  >

The emulation of std::multisets with multi_index_container results in a slight difference with respect to the interface offered: the member function insert(const value_type&) does not return an iterator as in std::multisets, but rather a std::pair<iterator,bool> in the spirit of std::sets. In this particular case, however, the bool member of the returned pair is always true.

The case of std::maps and std::multimaps does not lend itself to such a direct emulation by means of multi_index_container. The main problem lies in the fact that elements of a multi_index_container are treated as constant, while the std::map and std::multimap handle objects of type std::pair<const Key,T>, thus allowing for free modification of the value part. To overcome this difficulty, create an ad hoc pair class:

template <typename T1,typename T2>
struct mutable_pair
{
  typedef T1 first_type;
  typedef T2 second_type;

  mutable_pair():first(T1()),second(T2()){}
  mutable_pair(const T1& f,const T2& s):first(f),second(s){}
  mutable_pair(const std::pair<T1,T2>& p):first(p.first),second(p.second){}

  T1         first;
  mutable T2 second;
};

and so the substitution rules are:

std::map<Key,T,Compare,Allocator> ->
  multi_index_container<
    Element,
    indexed_by<
      ordered_unique<member<Element,Key,&Element::first>,Compare>
    >,
    typename Allocator::template rebind<Element>::other
  >

std::multimap<Key,T,Compare,Allocator> ->
  multi_index_container<
    Element,
    indexed_by<
      ordered_non_unique<member<Element,Key,&Element::first>,Compare>
    >,
    typename Allocator::template rebind<Element>::other
  >

(with Element=mutable_pair<Key,T>)

If default values are considered, the rules take the form:

std::map<Key,T> ->
  multi_index_container<
    Element,
    indexed_by<ordered_unique<member<Element,Key,&Element::first> > >
  >

std::multimap<Key,T> ->
  multi_index_container<
    Element,
    indexed_by<ordered_non_unique<member<Element,Key,&Element::first> > >
  >

(with Element=mutable_pair<Key,T>)

Unlike as with standard sets, the interface of these multi_index_container-emulated maps does not exactly conform to that of std::maps and std::multimaps. The most obvious difference is the lack of operator [], either in read or write mode; this, however, can be emulated with appropriate use of find and insert.

These emulations of standard associative containers with multi_index_container are comparable to the original constructs in terms of space and time efficiency. See the performance section for further details.

Emulation of std::list

Unlike the case of associative containers, emulating std::list in Boost.MultiIndex does not add any significant functionality, so the following is presented merely for completeness sake.

Much as with standard maps, the main difficulty to overcome when emulating std::list derives from the constant nature of elements of a multi_index_container. Again, some sort of adaptation class is needed, for instance like the following:

template <typename T>
struct mutable_value
{
  mutable_value(const T& t):t(t){}
  operator T&()const{return t;}

private:
  mutable T t;
};

which allows to use the substitution rule:

std::list<T,Allocator> ->
  multi_index_container<
    Element,
    indexed_by<sequenced<> >,
    typename Allocator::template rebind<Element>::other
  >

(with Element=mutable_value<T>)

or, if the default value Allocator=std::allocator<T> is used:

std::list<T> ->
  multi_index_container<mutable_value<T>,indexed_by<sequenced<> > >

Metaprogramming and multi_index_container

Boost.MultiIndex provides a number of facilities intended to allow the analysis and synthesis of multi_index_container instantiations by MPL metaprograms.

MPL analysis

Given a multi_index_container instantiation, the following nested types are provided for compile-time inspection of the various types occurring in the definition of the multi_index_container:

  • index_specifier_type_list,

  • index_type_list,

  • iterator_type_list,

  • const_iterator_type_list.

Each of these types is an MPL sequence with as many elements as indices comprise the multi_index_container: for instance, the n-th element of iterator_type_list is the same as nth_index_iterator<n>::type.

A subtle but important distinction exists between index_specifier_type_list and index_type_list: the former typelist holds the index specifiers with which the multi_index_container instantiation was defined, while the latter gives access to the actual implementation classes corresponding to each specifier. An example will help to clarify this distinction. Given the instantiation:

typedef multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    sequenced<>
  >
> indexed_t;

indexed_t::index_specifier_type_list is a type list with elements

ordered_unique<identity<int> >
sequenced<>

while indexed_t::index_type_list holds the types

multi_index_container::nth_type<0>::type
multi_index_container::nth_type<1>::type

so the typelists are radically different. Check the reference for the exact MPL sequence concepts modeled by these type lists.

MPL synthesis

Although typically indices are specified by means of the indexed_by construct, actually any MPL sequence of index specifiers can be provided instead:

typedef mpl::vector<ordered_unique<identity<int> >,sequenced<> > index_list_t;

typedef multi_index_container<
  int,
  index_list_t
> indexed_t;

This possibility enables the synthesis of instantiations of multi_index_container through MPL metaprograms, as the following example shows:

// original multi_index_container instantiation
typedef multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >
  >
>                                indexed_t1;

// we take its index list and add an index
typedef boost::mpl::push_front<
  indexed_t1::index_specifier_type_list,
  sequenced<>
>::type                          index_list_t;

// augmented multi_index_container
typedef multi_index_container<
  int,
  index_list_t
>                                indexed_t2;

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Boost Property Map Library

Name

PropertyMap:

Synopsis

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class associative_ptr_container

This section describes all the common operations for all associative pointer containers (in addition to reversible_ptr_container).

Hierarchy:

  • reversible_ptr_container
    • associative_ptr_container
      • ptr_set_adapter
      • ptr_multiset_adapter
      • ptr_map_adapter
      • ptr_multi_map_adapter
        • ptr_set
        • ptr_multi_set
        • ptr_map
        • ptr_multimap

See also

  • iterator_range

Navigate

  • home

  • reference

Synopsis

namespace boost
{
    template
    <
        class Key,
        class CloneAllocator = heap_clone_allocator
    >
    class associative_ptr_container
    {
    public: // typedefs
        typedef ...   key_type;
        typedef ...   key_compare;
        typedef ...   value_compare;

    public: // observers
        key_compare    key_comp() const;
        value_compare  value_comp() const;

    public: // modifiers
        template< typename InputIterator >
        void       insert( InputIterator first, InputIterator last );
        template< class InputRange >
        void       insert( const InputRange& r );
        void       erase( iterator position );
        size_type  erase( const key_type& x );
        template< class Range >
        void       erase( const Range& r );
        void       erase( iterator first, iterator last );

    public: // algorithms
        iterator                        find( const key_type& x );
        const_iterator                  find( const key_type& x ) const;
        size_type                       count( const key_type& x ) const;
        iterator                        lower_bound( const key_type& x );
        const_iterator                  lower_bound( const key_type& x ) const;
        iterator                        upper_bound( const key_type& x );
        const_iterator                  upper_bound( const key_type& x ) const;
        iterator_range<iterator>        equal_range( const key_type& x );
        iterator_range<const_iterator>  equal_range( const key_type& x ) const;

    }; //  class 'associative_ptr_container'

} // namespace 'boost'

Semantics

Semantics: typedefs

  • typedef ... key_type;

    • if dealing with a map, then simply the key type

    • if dealing with a set, then the indirected key type, that is, given ptr_set<T>, key_type* will be T*.

  • typedef ... key_compare;

    • comparison object type that determines the order of elements in the container

  • typedef ... value_compare;

    • comparison object type that determines the order of elements in the container

    • if dealing with a map, then this comparison simply forwards to the key_compare comparison operation

Semantics: observers

  • key_compare key_comp() const;
  • value_compare value_comp() const;

    • returns copies of objects used to determine the order of elements

Semantics: modifiers

  • template< typename InputIterator > void insert( InputIterator first, InputIterator last );

    • Requirements: [first,last) is a valid range

    • Effects: Inserts a cloned range

    • Exception safety: Basic guarantee

  • template< class InputRange > void insert( const InputRange& r );

    • Effects: insert( boost::begin(r), boost::end(r) );

  • void erase( iterator position );

    • Requirements: position is a valid iterator from the container

    • Effects: Removes the element defined by position.

    • Throws: Nothing

  • size_type erase( const key_type& x );

    • Effects: Removes all the elements in the container with a key equivalent to x and returns the number of erased elements.

    • Throws: Nothing

  • void erase( iterator first, iterator last );

    • Requirements: [first,last) is a valid range

    • Effects: Removes the range of elements defined by [first,last).

    • Throws: Nothing

  • template< class Range > void erase( const Range& r );

    • Effects: erase( boost::begin(r), boost::end(r) );

Semantics: algorithms

  • iterator       find( const Key& x );
  • const_iterator find( const Key& x ) const;

    • Effects: Searches for the key and returns end() on failure.

    • Complexity: Logarithmic

  • size_type count( const Key& x ) const;
    • Effects: Counts the elements with a key equivalent to x
    • Complexity: Logarithmic
  • iterator       lower_bound( const Key& x );
  • const_iterator lower_bound( const Key& x ) const;

    • Effects: Returns an iterator pointing to the first element with a key not less than x

    • Complexity: Logarithmic

  • iterator       upper_bound( const Key& x );
  • const_iterator upper_bound( const Key& x ) const;

    • Effects: Returns an iterator pointing to the first element with a key greater than x

    • Complexity: Logarithmic

  • iterator_range<iterator>       equal_range( const Key& x );
  • iterator_range<const_iterator> equal_range( const Key& x ) const;
    • Effects: return boost::make_iterator_range( lower_bound( x ), upper_bound( x ) );
    • Complexity: Logarithmic

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

associative_ptr_container:

Synopsis

namespace boost
{
    template
    <
        class Key,
        class CloneAllocator = heap_clone_allocator
    >
    class associative_ptr_container
    {
    public: // typedefs
        typedef ...   key_type;
        typedef ...   key_compare;
        typedef ...   value_compare;

    public    public: // typedefs
        typedef ...   key_type;
        typedef ...   key_compare;
        typedef ...   value_compare;

    public: // late< class InputRange >
        void       insert( const InputRange& r );
        void       erase( iterator position );
        size_type  erase( const key_type& x );
        tem    public: // observers
        key_compare    key_comp() cons    public: // modifiers
        template< r                        find( const key_type& x );
        const_iterator                  find( const key_type& x ) const;
        size_type                       count( const key_type& x ) const;
        iterator                        lower_bound( const key_type& x );
        const_iterator                  lower_bound( const key_type& x ) const;
        iterator                        upper_bound( const key_type& x );
        const_iterator                  upper_bound( const key_type& x ) const;
        iterator_range<iterator>        equal_range( const key_type& x );
        iterator_range<const_iterator>  equal_range( const key_type& x ) const;

    }; //  class 'associative_ptr_container'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Conventions

There are a few design decisions that will affect how the classes are used. Besides these, the classes are much like normal standard containers and provides almost the same interface. The new conventions are:

  • Null pointers are not allowed by default

  • All default iterators apply an extra layer of indirection

  • All comparison operations are done on the pointed to objects and not at the pointer level

  • The containers are neither Copy Constructible nor Assignable

  • Stored elements are required to be Clonable for a subset of the operations

  • Whenever objects are inserted into a container, they are cloned before insertion

  • Whenever pointers are inserted into a container, ownership is transferred to the container

  • Ownership can be transferred from a container on a per pointer basis

  • Ownership can be transferred from a container to another container on a per iterator range basis

  • A container can be cheaply returned from functions either by making a clone or by giving up ownership of the container

  • Iterators are invalidated as in the corresponding standard container

Null pointers are not allowed by default

If the user tries to insert the null pointer, the operation will throw a bad_pointer exception (see Example 1).

Use nullable to allow null pointers.

Please notice that all preconditions of the form

x != 0;

are not active when a container is instantiated with nullable<T> as in

boost::ptr_vector< boost::nullable<animal> > vec;
vec.push_back( 0 ); // ok
All default iterators apply an extra layer of indirection

This is done to make the containers easier and safer to use. It promotes a kind of pointer-less programming and the user of a class needs not worry about pointers except when allocating them (see Example 2). Iterators that provide access to the naked pointers are also provided since they might be useful in rare cases. For example, whenever begin() returns an iterator, ptr_begin() will return an iterator that allows one to iterate over the stored pointers.

All comparison operations are done on the pointed to objects and not at the pointer level

For example, in ptr_set<T> the ordering is by default done by boost::ptr_less<T> which compares the indirected pointers. Similarly, operator==() for container<Foo> compares all objects with operator==(const Foo&, const Foo&).

The containers are neither Copy Constructible nor Assignable

This is because cloning a lot of pointers can be a very expensive operation; instead functions are provided to transfer ownership. If a deep-copy is needed anyway, every container has a clone() member function (see Example 3).

Stored elements are required to be Clonable for a subset of the operations

This is because most polymorphic objects cannot be copied directly, but they can often be so by a use of a member function (see Example 4). Often it does not even make sense to clone an object in which case a large subset of the operations are still workable.

Whenever objects are inserted into a container, they are cloned before insertion

This is necessary because all pointer containers take ownerships of stored objects (see Example 5).

Whenever pointers are inserted into a container, ownership is transferred to the container

All containers take ownership of the stored pointers and therefore a container needs to have its own copies (see Example 5).

Ownership can be transferred from a container on a per pointer basis

This can of course also be convenient. Whenever it happens, an SmartContainer::auto_type object is used to provide an exception-safe transfer (see Example 6).

Ownership can be transferred from a container to another container on a per iterator range basis

This makes it possible to exchange data safely between different pointer containers without cloning the objects again (see Example 7).

A container can be cheaply returned from functions either by making a clone or by giving up ownership of the container

Two special member functions, clone() and release(), both return an auto_ptr<SmartContainer> which can be assigned to another pointer container. This effectively reduces the cost of returning a container to one heap-allocation plus a call to swap() (see Example 3).

Iterators are invalidated as in the corresponding standard container

Because the containers in this library wrap standard containers, the rules for invalidation of iterators are the same as the rules of the corresponding standard container.

For example, for both boost::ptr_vector<T> and std::vector<U> insertion and deletion only invalidates the deleted element and elements following it; all elements before the inserted/deleted element remain valid.

Navigate

  • home

  • reference

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Examples

Some examples are given here and in the accompanying test files:

  • 1. Null pointers cannot be stored in the containers

  • 2. Iterators and other operations return indirected values

  • 3. Copy-semantics of pointer containers

  • 4. Making a non-copyable type Clonable

  • 5. Objects are cloned before insertion, inserted pointers are owned by the container

  • 6. Transferring ownership of a single element

  • 7. Transferring ownership of pointers between different pointer containers

  • 8. Selected test files

  • 9. A large example

1. Null pointers cannot be stored in the containers

my_container.push_back( 0 );            // throws bad_ptr
my_container.replace( an_iterator, 0 ); // throws bad_ptr
my_container.insert( an_iterator, 0 );  // throws bad_ptr
std::auto_ptr<T> p( 0 );
my_container.push_back( p );            // throws bad_ptr

2. Iterators and other operations return indirected values

ptr_vector<X> pvec;
std::vector<X*> vec;
*vec.begin()  = new X;   // fine, memory leak
*pvec.begin() = new X;   // compile time error
( *vec.begin() )->foo(); // call X::foo(), a bit clumsy
pvec.begin()->foo();     // no indirection needed
*vec.front()  = X();     // overwrite first element
pvec.front()  = X();     // no indirection needed

3. Copy-semantics of pointer containers

ptr_vector<T> vec1;
...
ptr_vector<T> vec2( vec1.clone() ); // deep copy objects of 'vec1' and use them to construct
                                    // 'vec2', could be very expensive
vec2 = vec1.release();              // give up ownership of pointers in 'vec1' and
                                    // pass the ownership to 'vec2', rather cheap
vec2.release();                     // give up ownership; the objects will be deallocated
                                    //if not assigned to another container
vec1 = vec2;                        // compile time error: 'operator=()' not defined
ptr_vector<T> vec3( vec1 );         // compile time error: copy-constructor not defined

4. Making a non-copyable type Clonable

 // a class that has no normal copy semantics
class X : boost::noncopyable { public: X* clone() const; ... };

// this will be found by the library by argument dependent lookup (ADL)
X* new_clone( const X& x )
{ return x.clone(); }

// we can now use the interface that requires clonability
ptr_vector<X> vec1, vec2;
...
vec2 = vec1.clone();                                 // 'clone()' requires cloning <g>
vec2.insert( vec2.end(), vec1.begin(), vec1.end() ); // inserting always means inserting clones

5. Objects are cloned before insertion, inserted pointers are owned by the container

class X { ... };                     // assume 'X' is Clonable
X x;                                 // and 'X' can be stack-allocated
ptr_list<X> list;
list.push_back( new_clone( x ) );    // insert a clone
list.push_back( new X );             // always give the pointer directly to the container to avoid leaks
list.push_back( &x );                // don't do this!!!
std::auto_ptr<X> p( new X );
list.push_back( p );                 // give up ownership
BOOST_ASSERT( p.get() == 0 );

6. Transferring ownership of a single element

ptr_deque<T>                    deq;
typedef ptr_deque<T>::auto_type auto_type;

// ... fill the container somehow

auto_type ptr  = deq.release_back();             // remove back element from container and give up ownership
auto_type ptr2 = deq.release( deq.begin() + 2 ); // use an iterator to determine the element to release
ptr            = deq.release_front();            // supported for 'ptr_list' and 'ptr_deque'

deq.push_back( ptr.release() );                  // give ownership back to the container

7. Transferring ownership of pointers between different pointer containers

ptr_list<X> list; ptr_vector<X> vec;
...
//
// note: no cloning happens in these examples
//
list.transfer( list.begin(), vec.begin(), vec );           // make the first element of 'vec' the
                                                           // first element of 'list'
vec.transfer( vec.end(), list.begin(), list.end(), list ); // put all the lists element into the vector

Also transfer objects from ptr_container<Derived> to ptr_container<Base> without any problems.

8. Selected test files

incomplete_type_test.cpp: | Shows how to implement the Composite pattern.
simple_test.cpp: | Shows how the usage of pointer container compares with a container of smart pointers
view_example.cpp: | Shows how to use a pointer container as a view into other container
tree_test.cpp: | Shows how to make a tree-structure
array_test.cpp: | Shows how to make an n-ary tree

9. A large example

This examples shows many of the most common features at work. The example provide lots of comments.

//
// Boost.Pointer Container
//
//  Copyright Thorsten Ottosen 2003-2005. Use, modification and
//  distribution is subject to the Boost Software License, Version
//  1.0. (See accompanying file http://www.boost.org/LICENSE_1_0.txt or copy at
//  http://www.boost.org/http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see http://www.boost.org/libs/ptr_container/
//

//
// This example is intended to get you started.
// Notice how the smart container
//
// 1. takes ownership of objects
// 2. transfers ownership
// 3. applies indirection to iterators
// 4. clones objects from other smart containers
//

//
// First select which container to use.
//
#include <boost/ptr_container/ptr_deque.hpp>

//
// Need these later in the example
//
#include <boost/assert.hpp>
#include <string>
#include <exception>

//
// Then we define a small polymorphic class
// hierarchy.
//

class animal : boost::noncopyable
{
    virtual std::string do_speak() const = 0;
    std::string name_;

protected:
    //
    // Animals cannot be copied...
    //
    animal( const animal& r ) : name_( r.name_ )           { }
    void operator=( const animal& );

private:
    //
    // ...but due to advances in genetics, we can clone them!
    //

    virtual animal* do_clone() const = 0;

public:
    animal( const std::string& name ) : name_(name)        { }
    virtual ~animal() throw()                              { }

    std::string speak() const
    {
        return do_speak();
    }

    std::string name() const
    {
        return name_;
    }

    animal* clone() const
    {
        return do_clone();
    }
};

//
// An animal is still not Clonable. We need this last hook.
//
// Notice that we pass the animal by const reference
// and return by pointer.
//

animal* new_clone( const animal& a )
{
    return a.clone();
}

//
// We do not need to define 'delete_clone()' since
// since the default is to call the default 'operator delete()'.
//

const std::string muuuh = "Muuuh!";
const std::string oiink = "Oiiink";

class cow : public animal
{
    virtual std::string do_speak() const
    {
        return muuuh;
    }

    virtual animal* do_clone() const
    {
        return new cow( *this );
    }

public:
    cow( const std::string& name ) : animal(name)          { }
};

class pig : public animal
{
    virtual std::string do_speak() const
    {
        return oiink;
    }

    virtual animal* do_clone() const
    {
        return new pig( *this );
    }

public:
    pig( const std::string& name ) : animal(name)          { }
};

//
// Then we, of course, need a place to put all
// those animals.
//

class farm
{
    //
    // This is where the smart containers are handy
    //
    typedef boost::ptr_deque<animal> barn_type;
    barn_type                        barn;

    //
    // An error type
    //
    struct farm_trouble : public std::exception           { };

public:
    //
    // We would like to make it possible to
    // iterate over the animals in the farm
    //
    typedef barn_type::iterator  animal_iterator;

    //
    // We also need to count the farm's size...
    //
    typedef barn_type::size_type size_type;

    //
    // And we also want to transfer an animal
    // safely around. The easiest way to think
    // about '::auto_type' is to imagine a simplified
    // 'std::auto_ptr<T>' ... this means you can expect
    //
    //   T* operator->()
    //   T* release()
    //   deleting destructor
    //
    // but not more.
    //
    typedef barn_type::auto_type  animal_transport;

    //
    // Create an empty farm.
    //
    farm()                                                 { }

    //
    // We need a constructor that can make a new
    // farm by cloning a range of animals.
    //
    farm( animal_iterator begin, animal_iterator end )
     :
        //
        // Objects are always cloned before insertion
        // unless we explicitly add a pointer or
        // use 'release()'. Therefore we actually
        // clone all animals in the range
        //
        barn( begin, end )                               { }

    //
    // ... so we need some other function too
    //

    animal_iterator begin()
    {
        return barn.begin();
    }

    animal_iterator end()
    {
        return barn.end();
    }

    //
    // Here it is quite ok to have an 'animal*' argument.
    // The smart container will handle all ownership
    // issues.
    //
    void buy_animal( animal* a )
    {
        barn.push_back( a );
    }

    //
    // The farm can also be in economical trouble and
    // therefore be in the need to sell animals.
    //
    animal_transport sell_animal( animal_iterator to_sell )
    {
        if( to_sell == end() )
            throw farm_trouble();

        //
        // Here we remove the animal from the barn,
        // but the animal is not deleted yet...it's
        // up to the buyer to decide what
        // to do with it.
        //
        return barn.release( to_sell );
    }

    //
    // How big a farm do we have?
    //
    size_type size() const
    {
        return barn.size();
    }

    //
    // If things are bad, we might choose to sell all animals :-(
    //
    std::auto_ptr<barn_type> sell_farm()
    {
        return barn.release();
    }

    //
    // However, if things are good, we might buy somebody
    // else's farm :-)
    //

    void buy_farm( std::auto_ptr<barn_type> other )
    {
        //
        // This line inserts all the animals from 'other'
        // and is guaranteed either to succeed or to have no
        // effect
        //
        barn.transfer( barn.end(), // insert new animals at the end
                         *other );     // we want to transfer all animals,
                                       // so we use the whole container as argument
        //
        // You might think you would have to do
        //
        // other.release();
        //
        // but '*other' is empty and can go out of scope as it wants
        //
        BOOST_ASSERT( other->empty() );
    }

}; // class 'farm'.

int main()
{
    //
    // First we make a farm
    //
    farm animal_farm;
    BOOST_ASSERT( animal_farm.size() == 0u );

    animal_farm.buy_animal( new pig("Betty") );
    animal_farm.buy_animal( new pig("Benny") );
    animal_farm.buy_animal( new pig("Jeltzin") );
    animal_farm.buy_animal( new cow("Hanz") );
    animal_farm.buy_animal( new cow("Mary") );
    animal_farm.buy_animal( new cow("Frederik") );
    BOOST_ASSERT( animal_farm.size() == 6u );

    //
    // Then we make another farm...it will actually contain
    // a clone of the other farm.
    //
    farm new_farm( animal_farm.begin(), animal_farm.end() );
    BOOST_ASSERT( new_farm.size() == 6u );

    //
    // Is it really clones in the new farm?
    //
    BOOST_ASSERT( new_farm.begin()->name() == "Betty" );

    //
    // Then we search for an animal, Mary (the Crown Princess of Denmark),
    // because we would like to buy her ...
    //
    typedef farm::animal_iterator iterator;
    iterator to_sell;
    for( iterator i   = animal_farm.begin(),
                  end = animal_farm.end();
         i != end; ++i )
    {
        if( i->name() == "Mary" )
        {
            to_sell = i;
            break;
        }
    }

    farm::animal_transport mary = animal_farm.sell_animal( to_sell );

    if( mary->speak() == muuuh )
        //
        // Great, Mary is a cow, and she may live longer
        //
        new_farm.buy_animal( mary.release() );
    else
        //
        // Then the animal would be destroyed (!)
        // when we go out of scope.
        //
        ;

    //
    // Now,observe some changes to the two farms...
    //
    BOOST_ASSERT( animal_farm.size() == 5u );
    BOOST_ASSERT( new_farm.size()    == 7u );

    //
    // The new farm has however underestimated how much
    // it cost to feed Mary and its owner is forced to sell the farm...
    //
    animal_farm.buy_farm( new_farm.sell_farm() );

    BOOST_ASSERT( new_farm.size()    == 0u );
    BOOST_ASSERT( animal_farm.size() == 12u );
}

Navigate

  • home

  • reference

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

FAQ

  • Since a pointer container is not Copy Constructible and Assignable, I cannot put them into standard containers; what do I do?

  • Calling assign() is very costly and I do not really need to store cloned objects; I merely need to overwrite the existing ones; what do I do?

  • Which mutating algorithms are safe to use with pointers?

  • Why does ptr_map<T>::insert()/replace() take two arguments (the key and the pointer) instead of one std::pair? And why is the key passed by non-const reference?

  • When instantiating a pointer container with a type T, is T then allowed to be incomplete at that point?

  • Why do iterator-range inserts give the strong exception-safety guarantee?

  • What is the polymorphic class problem?

  • Are the pointer containers faster and do they have a better memory footprint than a container of smart pointers?

  • When the stored pointers cannot be 0, how do I allow this "empty" behavior anyway?

Since a pointer container is not Copy Constructible and Assignable, I cannot put them into standard containers; what do I do?

Since they are Clonable, put them in a pointer container.

Calling assign() is very costly and I do not really need to store cloned objects; I merely need to overwrite the existing ones; what do I do?

Call std::copy( first, last, c.begin() );.

Which mutating algorithms are safe to use with pointers?

Any mutating algorithm that moves elements around by swapping them. An important example is std::sort(); examples of unsafe algorithms are std::unique() and std::remove().

Why does ptr_map<T>::insert()/replace() take two arguments (the key and the pointer) instead of one std::pair? And why is the key passed by non-const reference?

This is the only way the function can be implemented in an exception-safe manner; since the copy-constructor of the key might throw, and since function arguments are not guaranteed to be evaluated from left to right, ensure that evaluating the first argument does not throw. Passing the key as a reference achieves just that.

When instantiating a pointer container with a type T, is T then allowed to be incomplete at that point?

No. This is a distinct property of shared_ptr which implies some overhead.

However, one can leave T incomplete in the header file:

// foo.hpp
class Foo { ... };
new_clone( const Foo& ) { ... }
delete_clone( const Foo* )     { ... }

// x.hpp
class Foo; // Foo is incomplete here
class X { ptr_deque<Foo> container; ... }

// x.cpp
#include <x.hpp>
#include <foo.hpp> // now Foo is not incomplete anymore
...
Why do iterator-range inserts give the strong exception-safety guarantee?

It is because it is actually affordable to do so; the overhead is one heap-allocation which is relatively small compared to cloning N objects.

What is the polymorphic class problem?

The problem refers to the relatively troublesome way C++ supports Object Oriented programming in connection with containers of pointers to polymorphic objects. In a language without garbage collection, use either a container of smart pointers or a container that takes ownership of the pointers. The hard part is to find a safe, fast and elegant solution.

Are the pointer containers faster and do they have a better memory footprint than a container of smart pointers?

The short answer is yes: they are faster and they do use less memory; in fact, they are the only way to obtain the zero-overhead hallmark of C++. Smart pointers usually have one word or more of memory overhead per pointer because a reference count must be maintained. And since the reference count must be maintained, there is also a runtime-overhead. If objects are big, then the memory overhead is often negligible, but for many small objects, it is not. Further reading can be found in these references: [11] and [12].

When the stored pointers cannot be 0, how do I allow this "empty" behavior anyway?

Storing a null-pointer among a list of pointers does not fit well into the Object Oriented paradigm. The most elegant design is to use the Null-Object Pattern where one basically makes a concrete class with dummy implementations of the virtual functions. See [13] for details.

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Usage Guidelines

  • Choosing the right container

  • Recommended practice for Object-Oriented Programming

    • 1. Make base classes abstract and without data

    • 2. Make virtual functions private and provide a non-virtual public forwarding function

    • 3. Derive your base class from boost::noncopyable

    • 4. Avoid null-pointers in containers (if possible)

Choosing the right container

The recommended usage pattern of the container classes are the same as the for normal standard containers.

ptr_vector, ptr_list and ptr_deque offer the programmer different complexity tradeoffs and should be used accordingly. ptr_vector is the type of sequence that should be used by default. ptr_list should be used when there are frequent insertions and deletions from the middle of the sequence and if the container is fairly large (for example, more than 100 elements). ptr_deque is the data structure of choice when most insertions and deletions take place at the beginning or at the end of the sequence. The special container ptr_array may be used when the size of the container is invariant and known at compile time.

An associative container supports unique keys if it may contain at most one element for each key. Otherwise, it supports equivalent keys. ptr_set and ptr_map support unique keys. ptr_multiset and ptr_multimap support equivalent keys.

Recommended practice for Object-Oriented Programming

Idiomtic Object-Oriented Programming in C++ looks a bit different from the way it is done in other languages. This is partly because C++ has both value and reference semantics, and partly because C++ is more flexible than other languages. Below is a list of recommendations that the user can follow:

1. Make base classes abstract and without data

This has the following advantages:

  1. It reduces coupling as, there is no need to maintain or update state

  2. It helps avoid slicing

  3. It ensures the user overrides the right function

Read the following articles:

  • Kevlin Henney's Six of the best

  • Jack Reeves' Multiple Inheritance Considered Useful

2. Make virtual functions private and provide a non-virtual public forwarding function

In code:

class Polymorphic
{
private:
    virtual int do_foo() = 0;

public:
    int foo()
    {
        return do_foo();
    }
    ...
};

This has the following advantages:

  1. It makes sure all calls to the virtual function always goes through one place in the code

  2. It enables the user to check preconditions and postconditions inside the forwarding function

Read Herb Sutter's article Virtuality.

3. Derive your base class from boost::noncopyable

Having an abstact base class prevents slicing when the base class is involved, but it does not prevent it for classes further down the hierarchy. This is where boost::noncopyable is handy to use:

class Polymorphic : boost::noncopyable
{
  ...
};

4. Avoid null-pointers in containers (if possible)

By default the pointer containers do not allow to store null-pointer in them. This behavior can be changed explicitly with the use of boost::nullable.

The primary reason to avoid null-pointers is that the user has to check for null-pointers every time the container is used. This extra checking is easy to forget, and it is somewhat contradictory to the spirit of OO where special cases are replaced with dynamic dispatch.

Often, however, there is a need to place some special object in the container because there is not enough information to construct a full object. In that case use, the Null Object pattern which simply dictates that virtual functions are implemented from the abstract base-class as empty functions or with dummy return values. This means that the OO-code still does not need to worry about null-pointers.

Read

  • Kevlin Henney's Null Object - Something for Nothing

Finally, when the Null Object does not help. Use container< nullable<T> >.

Navigate

  • home

  • reference

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Library headers

Header | Includes
<boost/ptr_container/clone_allocator.hpp> | classes heap_clone_allocator and view_clone_allocator and functions new_clone() and delete_clone()
<boost/ptr_container/ptr_deque.hpp> | class ptr_deque (and std::deque)
<boost/ptr_container/ptr_list.hpp> | class ptr_list (and std::list)
<boost/ptr_container/ptr_vector.hpp> | class ptr_vector (and std::vector)
<boost/ptr_container/ptr_array.hpp> | class ptr_array (and boost::array)
<boost/ptr_container/ptr_set.hpp> | classes ptr_set and ptr_multiset (and std::set and std::multiset)
<boost/ptr_container/ptr_map.hpp> | classes ptr_map and ptr_multimap (and std::map and std::multimap)
<boost/ptr_container/ptr_sequence_adapter.hpp> | class ptr_sequence_adapter
<boost/ptr_container/ptr_set_adapter.hpp> | classes ptr_set_adapter and ptr_multiset_adapter
<boost/ptr_container/ptr_map_adapter.hpp> | classes ptr_map_adapter and ptr_multimap_adapter
<boost/ptr_container/exception.hpp>
classes bad_ptr_container_operation, bad_index
and bad_pointer
<boost/ptr_container/indirect_fun.hpp> | class indirect_fun
<boost/ptr_container/nullable.hpp> | class nullable

Navigate

  • home

  • reference

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Indirected functions

It is quite common to have two pointers and what to compare the pointed to objects. Also, having defined how to compare the objects to avoid some tedious boiler-plate code this library defines predicates that apply an indirection before comparing.

When the container uses void* internally, use the class void_ptr_indirect_fun; otherwise use the class indirect_fun.

Example:

std::string* bar = new std::string("bar");
std::string* foo = new std::string("foo");
BOOST_ASSERT( indirect_fun< std::less<std::string> >()( bar, foo ) == true );
BOOST_ASSERT( make_indirect_fun( std::less<std::string>() )( foo, bar ) == false );

void*       vptr1  = ptr1;
void*       vptr2  = ptr2;
void_ptr_indirect_fun< std::less<std::string>, std::string> cast_fun;
BOOST_CHECK( cast_fun( vptr1, vptr2 ) == true );

See also

  • result_of

  • pointee

  • ptr_set

  • ptr_multiset

Navigate

  • home

  • reference

Remarks:

The class indirect_fun will work with smart pointers such as boost::shared_ptr<T> because of the type traits pointee<T>::type from the header <boost/pointee.hpp>.

Synopsis

Since the definition of the predicates is somewhat trivial, only the first operation is expanded inline.

namespace boost
{

    template< class Fun >
    struct indirect_fun
    {
        indirect_fun() : fun(Fun())
        { }

        indirect_fun( Fun f ) : fun(f)
        { }

        template< class T >
        typename result_of< Fun( typename pointee<T>::type ) >::type
        operator()( const T& r ) const
        {
            return fun( *r );
        }

        template< class T, class U >
        typename result_of< Fun( typename pointee<T>::type,
                                 typename pointee<U>::type ) >::type
        operator()( const T& r, const U& r2 ) const
        {
            return fun( *r, *r2 );
        }

    private:
        Fun fun;
    };

    template< class Fun >
    inline indirect_fun<Fun> make_indirect_fun( Fun f )
    {
        return indirect_fun<Fun>( f );
    }

    template< class Fun, class Arg1, class Arg2 = Arg1 >
    struct void_ptr_indirect_fun
    {
        void_ptr_indirect_fun() : fun(Fun())
        { }

        void_ptr_indirect_fun( Fun f ) : fun(f)
        { }

        typename result_of< Fun( Arg1 ) >::type
        operator()( const void* r ) const
        {
            return fun( * static_cast<const Arg1*>( r ) );
        }

        typename result_of< Fun( Arg1, Arg2 ) >::type
        operator()( const void* l, const void* r ) const
        {
            return fun( * static_cast<const Arg1*>( l ), * static_cast<const Arg2*>( r ) );
        }

    private:
        Fun fun;
    };

    template< class Fun, class Arg >
    inline void_ptr_indirect_fun<Fun,Arg>
    make_void_ptr_indirect_fun( Fun f )
    {
        return void_ptr_indirect_fun<Fun,Arg>( f );
    }

} // namespace 'boost'

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Class ptr_array

A ptr_array<T,size> is a pointer container that uses an underlying boost::array<void*,size> to store the pointers. The class is useful when there is no requirement of dynamic expansion and when no overhead is tolerable.

Hierarchy:

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class T,
        size_t N,
        CloneAllocator = heap_clone_allocator
    >
    class ptr_array : public implementation-defined
    {
    public: // construct/copy/destroy
        ptr_array();
        ptr_array( std::auto_ptr<ptr_array>& r );

    public: // iterators

    public: // capacity

    public: // element access
        T&        front();
        const T&  front() const;
        T&        back();
        const T&  back() const;

        template< size_t idx >
        T&        at();
        template< size_t idx >
        const T&  at() const;
        T&        at( size_t );
        const T&  at( size_t );

        T&        operator[]( size_t );
        const T&  operator[]( size_t ) const;

    public: // modifiers
        void  swap( ptr_array& r );

        template< size_t idx >
        auto_type replace( T* r );
        template< size_t idx, class U >
        auto_type replace( std::auto_ptr<U> r );
        auto_type replace( size_t idx, T* r );
        template< class U >
        auto_type replace( size_t idx, std::auto_ptr<U> r );

    public: // pointer container requirements
        std::auto_ptr<ptr_array>  clone() const;
        std::auto_ptr<ptr_array>  release();
        template< size_t idx >
        bool                      is_null() const;
        bool                      is_null( size_t idx ) const;

    }; //  class 'ptr_sequence_adapter'

} // namespace 'boost'

Semantics

Semantics: construct/copy/destroy

  • ptr_array();

    • Effects: Constructs array where each element is null

  • ptr_array( std::auto_ptr<ptr_array>& r );

    • Effects: Takes ownership of the supplied pointers

Semantics: element access

  • T&       front();
  • const T& front() const;

    • Requirements: not empty();

    • Effects: return *begin();

    • Throws: bad_ptr_container_operation if empty() == true

  • T&       back();
  • const T& back() const;

    • Requirements: not empty();

    • Effects: return *--end();

    • Throws: bad_ptr_container_operation if empty() == true

  • template< size_t idx > T&       at( size_type n );
  • template< size_t idx > const T& at( size_type n ) const;

    • Requirements: idx < size() (compile-time enforced)

    • Effects: Returns a reference to the n'th element

    • Throws: nothing

  • T&       at( size_type n );
  • const T& at( size_type n ) const;

    • Requirements: n < size()

    • Effects: Returns a reference to the n'th element

    • Throws: bad_index if n >=size()

  • T&       operator[]( size_type n );
  • const T& operator[]( size_type n ) const;

    • Requirements: n < size()

    • Effects: Returns a reference to the n'th element

    • Throws: Nothing

Semantics: modifiers

  • void swap( ptr_array& r );

    • Effects: swaps the two arrays

    • Complexity: Linear

    • Throws: nothing

  • template< size_t idx > auto_type replace( T* r );

    • Requirements:

      • idx < size() (compile-time enforced)

      • r != 0

    • Effects: returns the object indexed by idx and replaces it with r.

    • Throws: bad_pointer if x == 0.

    • Exception safety: Strong guarantee

  • template< size_t idx, class U > auto_type replace( std::auto_ptr<U> r );

    • Effects: return replace<idx>( r.release() );

  • auto_type replace( size_t idx, T* r );

    • Requirements: `` x != 0 and idx < size()``

    • Effects: returns the object indexed by idx and replaces it with x.

    • Throws: bad_index if idx >= size() and bad_pointer if x == 0.

    • Exception safety: Strong guarantee

  • template< class U > auto_type replace( size_t idx, std::auto_ptr<U> r );

    • Effects: return replace( idx, r.release() );

Semantics: pointer container requirements

  • std::auto_ptr<ptr_array>  clone() const;

    • Effects: Returns a deep copy of the container

    • Throws: std::bad_alloc if there is not enough memory to make a clone of the container

    • Complexity: Linear

  • std::auto_ptr<ptr_array>  release();

    • Effects: Releases ownership of the container. This is a useful way of returning a container from a function.

    • Postconditions: empty() == true and all pointers are null

    • Throws: std::bad_alloc if the return value cannot be allocated

    • Exception safety: Strong guarantee

  • template< size_t idx > bool is_null() const;

    • Requirements: idx < size() (compile-time enforced)

    • Effects: returns whether the pointer at index idx is null

    • Exception safety: Nothrow guarantee

  • bool is_null( size_type idx ) const;

    • Requirements: idx < size()

    • Effects: returns whether the pointer at index idx is null

    • Exception safety: Nothrow guarantee

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

ptr_array: This is a pointer container that uses an underlying boost::array<void*,size> to store the pointers. The class is useful when there is no requirement of dynamic expansion and when no overhead is tolerable.

Synopsis

namespace boost
{

    template
    <
        class T,
        size_t N,
        CloneAllocator = heap_clone_allocator
    >
    class ptr_array : public implementation-defined
    {
    public: // construct/copy/destroy
        ptr_array();
        ptr_array( std::auto_ptr<ptr_array>& r );

    public: // iterators

    public: // capacity

    public: // element access
        T&        front();
        const T&  front() const;
        T&        back();
        const T&  back() const;

        template< size_t idx >
        T&        at();
        template< size_t idx >
        const T&  at() const;
        T&        at( size_t );
        const T&  at( size_t );

        T&        operator[]( size_t );
        const T&  operator[]( size_t ) const;

    public: // modifiers
        void  swap( ptr_array& r );

        template< size_t idx >
        auto_type replace( T* r );
        template< size_t idx, class U >
        auto_type replace( std::auto_ptr<U> r );
        auto_type replace( size_t idx, T* r );
        template< class U >
        auto_type replace( size_t idx, std::auto_ptr<U> r );

    public: // pointer container requirements
        std::auto_ptr<ptr_array>  clone() const;
        std::auto_ptr<ptr_array>  release();
        template< size_t idx >
        bool                      is_null() const;
        bool                      is_null( size_t idx ) const;

    }; //  class 'ptr_sequence_adapter'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Overview

Boost.Pointer Container provides containers for holding heap-allocated objects in an exception-safe manner and with minimal overhead. The aim of the library is in particular to make OO programming easier in C++ by establishing a standard set of classes, methods and designs for dealing with OO specific problems

  • Motivation

  • Tutorial

  • Reference

  • Usage guidelines

  • Examples

  • Library headers

  • FAQ

  • Upgrading from Boost v. 1.33.*

  • Acknowledgements

  • References

Motivation

Whenever a programmer wants to have a container of pointers to heap-allocated objects, there is usually only one exception-safe way: to make a container of smart pointers like boost::shared_ptr This approach is suboptimal if:

  1. the stored objects are not shared, but owned exclusively, or
  2. the overhead implied by smart pointers is inappropriate

This library therefore provides standard-like containers that are for storing heap-allocated or cloned objects (or in case of a map, the mapped object must be a heap-allocated or cloned object). For each of the standard containers there is a pointer container equivalent that takes ownership of the objects in an exception safe manner. In this respect the library is intended to solve the so-called polymorphic class problem.

The advantages of pointer containers are:

  1. Exception-safe pointer storage and manipulation.
  2. Notational convenience compared to the use of containers of pointers.
  3. Can be used for types that are neither Assignable nor Copy Constructible.
  4. No memory-overhead as containers of smart pointers can have (see [11] and [12]).
  5. Usually faster than using containers of smart pointers (see [11] and [12]).
  6. The interface is slightly changed towards the domain of pointers instead of relying on the normal value-based interface. For example, it is possible for pop_back() to return the removed element.
  7. Propagates constness s.t. one cannot modify the objects via a const_iterator.
  8. Built-in support for deep-copy semantics via the The Clobable Concept

The disadvantages are:

  1. Less flexible than containers of smart pointers like boost::shared_ptr

The user does not need this library when shared semantics are required.

Upgrading from Boost v. 1.33.*

If the user upgrades from one of these versions of Boost, then there has been one major interface change: map iterators now mimic iterators from std::map. Previously, the user may have written:

for( boost::ptr_map<std::string,T>::iterator i = m.begin(), e = m.end();
     i != e; ++i )
{
  std::cout << "key:" << i.key();
  std::cout << "value:" << *i;
  i->foo(); // call T::foo()
}

This needs to be converted into:

for( boost::ptr_map<std::string,T>::iterator i = m.begin(), e = m.end();
     i != e; ++i )
{
  std::cout << "key:" << i->first;
  std::cout << "value:" << *i->second;
  i->second->foo(); // call T::foo()
}

Apart from the above change, the library now also introduces

  • std::auto_ptr<T> overloads:
    std::auto_ptr<T> p( new T );
container.push_back( p );

  • Derived-to-Base conversion in transfer():

    boost::ptr_vector<Base>  vec;
boost::ptr_list<Derived> list;
...
vec.transfer( vec.begin(), list ); // now ok

Also note that Boost.Assign introduces better support for pointer containers.

Acknowledgements

The following people have been very helpful:

  • Bjørn D. Rasmussen for unintentionally motivating me to start this library

  • Pavel Vozenilek for asking me to make the adapters

  • David Abrahams for the indirect_fun design

  • Pavol Droba for being review manager

  • Ross Boylan for trying out a prototype for real

  • Felipe Magno de Almeida for giving feedback based on using the library in production code even before the library was part of boost

  • Jonathan Turkanis for supplying his move_ptr framework which is used internally

  • Stefan Slapeta and Howard Hinnant for Metrowerks support

  • Russell Hind for help with Borland compatibility

  • Jonathan Wakely for his great help with GCC compatibility and bug fixes

  • Pavel Chikulaev for comments and bug-fixes

  • Andreas Hommel for fixing the nasty Metrowerks bug

References

[1] Matt Austern: "The Standard Librarian: Containers of Pointers" , C/C++ Users Journal Experts Forum.
[2] Bjarne Stroustrup, "The C++ Programming Language", Appendix E: "Standard-Library Exception Safety"
[3] Herb Sutter, "Exceptional C++".
[4] Herb Sutter, "More Exceptional C++".
[5] Kevlin Henney: "From Mechanism to Method: The Safe Stacking of Cats" , C++ Experts Forum, February 2002.
[6] Some of the few earlier attempts of pointer containers I have seen are the rather interesting NTL and the pointainer. As of this writing both libraries are not exceptions-safe and can leak.
[7] INTERNATIONAL STANDARD, Programming languages --- C++, ISO/IEC 14882, 1998. See section 23 in particular.
[8] C++ Standard Library Closed Issues List (Revision 27), Item 218, Algorithms do not use binary predicate objects for default comparisons.
[9] C++ Standard Library Active Issues List (Revision 27), Item 226, User supplied specializations or overloads of namespace std function templates.
[10] Harald Nowak, "A remove_if for vector", C/C++ Users Journal, July 2001.
[11] (1, 2) Boost smart pointer timings
[12] (1, 2) NTL: Array vs std::vector and boost::shared_ptr
[13] Kevlin Henney, Null Object, 2002.

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

ptr_container: Boost.Pointer Container provides containers for holding heap-allocated objects in an exception-safe manner and with minimal overhead.

Synopsis

template
<
class T,
class VoidPtrSeq,
class CloneAllocator = heap_clone_allocator
>
class ptr_sequence_adapter : public
ptr_container_detail::reversible_ptr_container< ptr_container_detail::sequence_config<T,VoidPtrSeq>, CloneAllocator >

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_deque

A ptr_deque<T> is a pointer container that uses an underlying std:deque<void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class T,
        class CloneAllocator = heap_clone_allocator
        class Allocator      = std::allocator<void*>
    >
    class ptr_deque : public ptr_sequence_adapter
                             <
                                T,
                                std::deque<void*,Allocator>,
                                CloneAllocator
                             >
    {

    public: // element access
        T&        operator[]( size_type n );
        const T&  operator[]( size_type n ) const;
        T&        at( size_type n );
        const T&  at( size_type n ) const;

    public: // modifiers
        void      push_front( T* x );
        template< class U >
        void      push_front( std::auto_ptr<U> x );
        auto_type pop_front();

    public: // pointer container requirements
       auto_type replace( size_type idx, T* x );
       template< class U >
       auto_type replace( size_type idx, std::auto_ptr<U> x );
       bool      is_null( size_type idx ) const;

    };

} // namespace 'boost'

Semantics

Semantics: modifiers

  • void push_front( T* x );

    • Requirements: x != 0

    • Effects: Inserts the pointer into container and takes ownership of it

    • Throws: bad_point er if x == 0

    • Exception safety: Strong guarantee

  • template< class U > void push_front( std::auto_ptr<U> x );
    • Effects: push_front( x.release() );
  • auto_type pop_front():

    • Requirements:not empty()

    • Effects: Removes the first element in the container

    • Postconditions: size() is one less

    • Throws: bad_ptr_container_operation if empty() == true

    • Exception safety: Strong guarantee

Semantics: element access

  • T& operator[]( size_type n );
  • const T& operator[]( size_type n ) const;

    • Requirements: n < size()

    • Effects: Returns a reference to the n'th element

    • Throws: Nothing

  • T& at( size_type n );
  • const T& at( size_type n ) const;
    • Requirements: n < size()

    • Effects: Returns a reference to the n'th element

    • Throws: bad_index if n >=size()

Semantics: pointer container requirements

  • auto_type replace( size_type idx, T* x );

    • Requirements: `` x != 0 and idx < size()``

    • Effects: returns the object indexed by idx and replaces it with x.

    • Throws: bad_index if idx >= size() and bad_pointer if x == 0.

    • Exception safety: Strong guarantee

  • template< class U > auto_type replace( size_type idx, std::auto_ptr<U> x );

    • Effects: return replace( idx, x.release() );

  • bool is_null( size_type idx ) const;

    • Requirements: idx < size()

    • Effects: returns whether the pointer at index idx is null

    • Exception safety: Nothrow guarantee

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

ptr_deque: This is a pointer container that uses an underlying std:deque to store the pointers.

Synopsis

namespace boost
{

    template
    <
        class T,
        class CloneAllocator = heap_clone_allocator
        class Allocator      = std::allocator<void*>
    >
    class ptr_deque : public ptr_sequence_adapter
                             <
                                T,
                                std::deque<void*,Allocator>,
                                CloneAllocator
                             >
    {

    public: // element access
        T&        operator[]( size_type n );
        const T&  operator[]( size_type n ) const;
        T&        at( size_type n );
        const T&  at( size_type n ) const;

    public: // modifiers
        void      push_front( T* x );
        template< class U >
        void      push_front( std::auto_ptr<U> x );
        auto_type pop_front();

    public: // pointer container requirements
       auto_type replace( size_type idx, T* x );
       template< class U >
       auto_type replace( size_type idx, std::auto_ptr<U> x );
       bool      is_null( size_type idx ) const;

    };

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_list

A ptr_list<T> is a pointer container that uses an underlying std:list<void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class T,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_list : public ptr_sequence_adapter
                            <
                                T,
                                std::list<void*,Allocator>,
                                CloneAllocator
                            >
    {

    public: // modifiers
        void                push_front( T* x );
        template< class U >
        void                push_front( std::auto_ptr<U> x );
        auto_type           pop_front();

    public: // list operations
        void  reverse();

    }; // class 'ptr_list'

} // namespace 'boost'

Semantics

Semantics: modifiers

  • void push_front( T* x );

    • Requirements: x != 0

    • Effects: Inserts the pointer into container and takes ownership of it

    • Throws: bad_pointer if x == 0

    • Exception safety: Strong guarantee

  • template< class U > void push_front( std::auto_ptr<U> x );

    • Effects: push_front( x.release() );

  • auto_type pop_front():

    • Requirements:not empty()

    • Effects: Removes the first element in the container

    • Postconditions: size() is one less

    • Throws: bad_ptr_container_operation if empty() == true

    • Exception safety: Strong guarantee

Semantics: list operations

  • void reverse();

    • Effects: reverses the underlying sequence

    • Throws: nothing

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Name

ptr_list: This is a pointer container that uses an underlying std:list to store the pointers.

Synopsis

namespace boost
{

    template
    <
        class T,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_list : public ptr_sequence_adapter
                            <
                                T,
                                std::list<void*,Allocator>,
                                CloneAllocator
                            >
    {

    public: // modifiers
        void                push_front( T* x );
        template< class U >
        void                push_front( std::auto_ptr<U> x );
        auto_type           pop_front();

    public: // list operations
        void  reverse();

    }; // class 'ptr_list'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_map

A ptr_map<Key,T> is a pointer container that uses an underlying std::map<Key,void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class Key,
        class T,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator< std::pair<const Key,void*> >
    >
    class ptr_map : public ptr_map_adapter
                           <
                               T,
                               std::map<Key,void*,Compare,Allocator>,
                               CloneAllocator
                           >
    {
        // see references

    }; //  class 'ptr_map'

} // namespace 'boost'

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Class ptr_map_adapter

This class is used to build custom pointer containers with an underlying map-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Hierarchy:

  • reversible_ptr_container

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

Navigate

  • home

  • reference

Synopsis

namespace boost
{
    template
    <
        class T,
        class VoidPtrMap,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_map_adapter
    {
    public: // typedefs
        typedef VoidPtrMap::key_type key_type;
        typedef T*                   mapped_type;
        typedef T&                   mapped_reference;
        typedef const T&             const_mapped_reference;
        typedef ...                  value_type;
        typedef ...                  reference;
        typedef ...                  const_reference;
        typedef ...                  pointer;
        typedef ...                  const_pointer;

    public: // modifiers
        std::pair<iterator,bool>  insert( key_type& k, T* x );
        template< class U >
        std::pair<iterator,bool>  insert( const key_type& k, std::auto_ptr<U> x );

    public; // lookup
        T&       operator[]( const key_type& key );
        T&       at( const key_type& key );
        const T& at( const key_type& key ) const;

    public: // pointer container requirements
        bool      transfer( iterator object, ptr_map_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_map_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_map_adapter& from );
        size_type transfer( ptr_map_adapter& from );

    }; //  class 'ptr_map_adapter'

} // namespace 'boost'

Semantics

Semantics: typedefs

The following types are implementation defined:

typedef ... value_type;
typedef ... reference;
typedef ... const_reference;
typedef ... pointer;
typedef ... const_pointer;

However, the structure of the type mimics std::pair s.t. one can use first and second members. The reference-types are not real references and the pointer-types are not real pointers. However, one may still write

map_type::value_type       a_value      = *m.begin();
a_value.second->foo();
map_type::reference        a_reference  = *m.begin();
a_reference.second->foo();
map_type::const_reference  a_creference = *const_begin(m);
map_type::pointer          a_pointer    = &*m.begin();
a_pointer->second->foo();
map_type::const_pointer    a_cpointer   = &*const_begin(m);

The difference compared to std::map<Key,T*> is that constness is propagated to the pointer (that is, to second) in const_itertor.

Semantics: modifiers

  • std::pair<iterator,bool> insert( key_type& k, value_type x );

    • Requirements: x != 0

    • Effects: Takes ownership of x and insert it if there is no equivalent of it already. The bool part of the return value indicates insertion and the iterator points to the element with key x.

    • Throws: bad_point er if x == 0

    • Exception safety: Strong guarantee

  • template< class U > std::pair<iterator,bool> insert( const key_type& k, std::auto_ptr<U> x );

    • Equivalent to (but without the const_cast): return insert( const_cast<key_type&>(k), x.release() );

Semantics: lookup

  • T& operator[]( const key_type& key );

    • Effects: returns the object with key key if it exists; otherwise a new object is allocated and inserted and its reference returned.

    • Exception-safety: Strong guarantee

  • T&       at( const key_type& key );
  • const T& at( const key_type& jey ) const;

    • Requirement: the key exists

    • Throws: bad_ptr_container_operation if the key does not exist

Semantics: pointer container requirements

  • bool transfer( iterator object, ptr_map_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the object defined by object into the container and remove it from from if no equivalent object exists.

    • Returns: whether the object was transferred

    • Exception safety: Strong guarantee

  • size_type transfer( iterator first, iterator last, ptr__set_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the objects defined by the range [first,last) into the container and remove it from from. An object is only transferred if no equivalent object exists.

    • Returns: the number of transferred objects

    • Exception safety: Basic guarantee

  • template< class Range > void transfer( const Range& r, ptr_map_adapter& from );

    • Effects: return transfer( boost::begin(r), boost::end(r), from );

  • size_type transfer( ptr_set_adapter& from );

    • Effects: return transfer( from.begin(), from.end(), from );.

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Name

ptr_map_adapter :This class is used to build custom pointer containers with an underlying map-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Synopsis

namespace boost
{
    template
    <
        class T,
        class VoidPtrMap,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_map_adapter
    {
    public: // typedefs
        typedef VoidPtrMap::key_type key_type;
        typedef T*                   mapped_type;
        typedef T&                   mapped_reference;
        typedef const T&             const_mapped_reference;
        typedef ...                  value_type;
        typedef ...                  reference;
        typedef ...                  const_reference;
        typedef ...                  pointer;
        typedef ...                  const_pointer;

    public: // modifiers
        std::pair<iterator,bool>  insert( key_type& k, T* x );
        template< class U >
        std::pair<iterator,bool>  insert( const key_type& k, std::auto_ptr<U> x );

    public; // lookup
        T&       operator[]( const key_type& key );
        T&       at( const key_type& key );
        const T& at( const key_type& key ) const;

    public: // pointer container requirements
        bool      transfer( iterator object, ptr_map_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_map_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_map_adapter& from );
        size_type transfer( ptr_map_adapter& from );

    }; //  class 'ptr_map_adapter'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Name

ptr_map: a pointer container that uses an underlying std::map<Key,void*> to store the pointers.

Synopsis

namespace boost
{

    template
    <
        class Key,
        class T,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator< std::pair<const Key,void*> >
    >
    class ptr_map : public ptr_map_adapter
                           <
                               T,
                               std::map<Key,void*,Compare,Allocator>,
                               CloneAllocator
                           >
    {
        // see references

    }; //  class 'ptr_map'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_multimap

A ptr_multimap<Key,T> is a pointer container that uses an underlying std::multimap<Key,void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container

    • associative_ptr_container
      • ptr_set_adapter
      • ptr_multiset_adapter
      • ptr_map_adapter
      • ptr_multi_map_adapter
        • ptr_set
        • ptr_multi_set
        • ptr_map

        • ptr_multimap

Navigate

  • home

  • reference

Synopsis

namespace boost
{
    template
    <
        class Key,
        class T,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator< std::pair<const Key,void*> >
    >
    class ptr_multimap : public ptr_multimap_adapter
                                <
                                    T,
                                    std::multimap<Key,void*,Compare,Allocator>,
                                    CloneAllocator
                                >
    {
        // see references

    }; //  class 'ptr_multimap'

} // namespace 'boost'

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Class ptr_multimap_adapter

This class is used to build custom pointer containers with an underlying multimap-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Hierarchy:

  • reversible_ptr_container
    • associative_ptr_container
      • ptr_set_adapter
      • ptr_multiset_adapter
      • ptr_map_adapter
      • ptr_multi_map_adapter
        • ptr_set
        • ptr_multi_set
        • ptr_map
        • ptr_multimap

Navigate

  • home

  • reference

Synopsis

namespace boost
{
    template
    <
        T,
        class VoidPtrMultiMap,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_multimap_adapter
    {
    public: // typedefs
        typedef VoidPtrMap::key_type key_type;
        typedef T*                   mapped_type;
        typedef T&                   mapped_reference;
        typedef const T&             const_mapped_reference;
        typedef ...                  value_type;
        typedef ...                  reference;
        typedef ...                  const_reference;
        typedef ...                  pointer;
        typedef ...                  const_pointer;

    public: // modifiers
        iterator  insert( key_type& k, T* x );
        template< class U >
        iterator  insert( const key_type&, std::auto_ptr<U> x );

    public: // pointer container requirements
        void      transfer( iterator object, ptr_multimap_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_multimap_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_multimap_adapter& from );
        void      transfer( ptr_multimap_adapter& from );

    }; //  class 'ptr_multimap_adapter'

} // namespace 'boost'

Semantics

. typedefs:

Semantics: typedefs

The following types are implementation defined:

typedef ... value_type;
typedef ... reference;
typedef ... const_reference;
typedef ... pointer;
typedef ... const_pointer;

However, the structure of the type mimics std::pair s.t. one can use first and second members. The reference-types are not real references and the pointer-types are not real pointers. However, one may still write

map_type::value_type       a_value      = *m.begin();
a_value.second->foo();
map_type::reference        a_reference  = *m.begin();
a_reference.second->foo();
map_type::const_reference  a_creference = *const_begin(m);
map_type::pointer          a_pointer    = &*m.begin();
a_pointer->second->foo();
map_type::const_pointer    a_cpointer   = &*const_begin(m);

The difference compared to std::map<Key,T*> is that constness is propagated to the pointer (that is, to second) in const_itertor.

Semantics: modifiers

  • iterator insert( key_type& k, T* x );

    • Requirements: x != 0

    • Effects: Takes ownership of x and returns an iterator pointing to it.

    • Throws: bad_pointer if x == 0

    • Exception safety: Strong guarantee

  • template< class U > iterator insert( const key_type& k, std::auto_ptr<U> x );

    • Equivalent to (but without the const_cast): return insert( const_cast<key_type&>(k), x.release() );

Semantics: pointer container requirements

  • void transfer( iterator object, ptr_multimap_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the object defined by object into the container and remove it from from.

    • Postconditions: size() is one more, from.size() is one less.

    • Exception safety: Strong guarantee

  • void transfer( iterator first, iterator last, ptr_multimap_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the objects defined by the range [first,last) into the container and remove it from from.

    • Postconditions: Let N == std::distance(first,last); then size() is N more, from.size() is N less.

    • Exception safety: Basic guarantee

  • template< class Range > void transfer( const Range& r, ptr_multimap_adapter& from );
    • Effects: transfer( boost::begin(r), boost::end(r), from );
  • void transfer( ptr_multimap_adapter& from );

    • Effects: transfer( from.begin(), from.end(), from );.

    • Postconditions: from.empty();

    • Exception safety: Basic guarantee

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Name

ptr_multimap_adapter : This class is used to build custom pointer containers with an underlying multimap-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Synopsis

namespace boost
{
    template
    <
        T,
        class VoidPtrMultiMap,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_multimap_adapter
    {
    public: // typedefs
        typedef VoidPtrMap::key_type key_type;
        typedef T*                   mapped_type;
        typedef T&                   mapped_reference;
        typedef const T&             const_mapped_reference;
        typedef ...                  value_type;
        typedef ...                  reference;
        typedef ...                  const_reference;
        typedef ...                  pointer;
        typedef ...                  const_pointer;

    public: // modifiers
        iterator  insert( key_type& k, T* x );
        template< class U >
        iterator  insert( const key_type&, std::auto_ptr<U> x );

    public: // pointer container requirements
        void      transfer( iterator object, ptr_multimap_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_multimap_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_multimap_adapter& from );
        void      transfer( ptr_multimap_adapter& from );

    }; //  class 'ptr_multimap_adapter'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Name

ptr_multimap: a pointer container that uses an underlying std::multimap<Key,void*> to store the pointers.

Synopsis

namespace boost
{
    template
    <
        class Key,
        class T,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator< std::pair<const Key,void*> >
    >
    class ptr_multimap : public ptr_multimap_adapter
                                <
                                    T,
                                    std::multimap<Key,void*,Compare,Allocator>,
                                    CloneAllocator
                                >
    {
        // see references

    }; //  class 'ptr_multimap'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_multiset

A ptr_multiset<T> is a pointer container that uses an underlying std::multiset<void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

See also

  • void_ptr_indirect_fun

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class Key,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_multiset : public ptr_multiset_adapter
                                <
                                    Key,
                                    std::multiset<void*,void_ptr_indirect_fun<Compare,Key>,Allocator>,
                                    CloneAllocator
                                >
    {
        // see references

    }; //  class 'ptr_multiset'

} // namespace 'boost'

Remarks:

  • Using nullable<T> as Key is meaningless and not allowed

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Class ptr_multiset_adapter

This class is used to build custom pointer containers with an underlying multiset-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Hierarchy:

  • reversible_ptr_container

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

Navigate

  • home

  • reference

Synopsis

namespace boost
{
    template
    <
        class Key,
        class VoidPtrMultiSet,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_multiset_adapter
    {

    public: // modifiers
        iterator  insert( Key* x );
        template< class Key2 >
        iterator  insert( std::auto_ptr<Key2> x );

    public: // pointer container requirements
        void      transfer( iterator object, ptr_multiset_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_multiset_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_multiset_adapter& from );
        void      transfer( ptr_multiset_adapter& from );

    }; //  class 'ptr_multiset_adapter'

} // namespace 'boost'

Semantics

Semantics: modifiers

  • iterator insert( key_type* x );

    • Requirements: x != 0

    • Effects: Takes ownership of x. The returned iterator points to the element with key x.

    • Throws: bad_pointer if x == 0

    • Exception safety: Strong guarantee

  • template< class Key2 > iterator insert( std::auto_ptr<Key2> x );

    • Effects: return insert( x.release() );

Semantics: pointer container requirements

  • void transfer( iterator object, ptr_multiset_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the object defined by object into the container and remove it from from.

    • Postconditions: size() is one more, from.size() is one less.

    • Exception safety: Strong guarantee

  • void transfer( iterator first, iterator last, ptr_multiset_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the objects defined by the range [first,last) into the container and remove it from from.

    • Postconditions: Let N == std::distance(first,last); then size() is N more, from.size() is N less.

    • Exception safety: Basic guarantee

  • template< class Range > void transfer( const Range& r, ptr_multiset_adapter& from );

    • Effects: transfer( boost::begin(r), boost::end(r), from );

  • void transfer( ptr_multiset_adapter& from );

    • Effects: transfer( from.begin(), from.end(), from );.

    • Postconditions: from.empty();

    • Exception safety: Basic guarantee

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Name

ptr_multiset_adapter: This class is used to build custom pointer containers with an underlying multiset-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Synopsis

namespace boost
{
    template
    <
        class Key,
        class VoidPtrMultiSet,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_multiset_adapter
    {

    public: // modifiers
        iterator  insert( Key* x );
        template< class Key2 >
        iterator  insert( std::auto_ptr<Key2> x );

    public: // pointer container requirements
        void      transfer( iterator object, ptr_multiset_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_multiset_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_multiset_adapter& from );
        void      transfer( ptr_multiset_adapter& from );

    }; //  class 'ptr_multiset_adapter'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Name

ptr_multiset: a pointer container that uses an underlying std::multiset<void*> to store the pointers.

Synopsis

namespace boost
{

    template
    <
        class Key,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_multiset : public ptr_multiset_adapter
                                <
                                    Key,
                                    std::multiset<void*,void_ptr_indirect_fun<Compare,Key>,Allocator>,
                                    CloneAllocator
                                >
    {
        // see references

    }; //  class 'ptr_multiset'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_sequence_adapter

This section describes all the common operations for all the pointer sequences:

  • ptr_vector

  • ptr_list

  • ptr_deque

Use the concrete class ptr_sequence_adapter to create custom pointer containers.

Hierarchy:

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class T,
        class VoidPtrSeq,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_sequence_adapter
    {
    public: // construct/copy/destroy
        template< class InputIterator >
        assign( InputIterator first, InputIterator last );
        template< class InputRange >
        assign( const InputRange& e );

    public: // element access
        T&        front();
        const T&  front() const;
        T&        back();
        const T&  back() const;

    public: // modifiers
        void      push_back( T* x );
        template< class U >
        void      push_back( std::auto_ptr<U> x );
        auto_type pop_back();
        iterator  insert( iterator position, T* x );
        template< class U >
        iterator  insert( iterator position, std::auto_ptr<U> x );
        template< class InputIterator >
        void      insert( iterator position, InputIterator first, InputIterator last );
        template< class InputRange >
        void      insert( iterator position, const InputRange& r );
        iterator  erase( iterator position );
        iterator  erase( iterator first, iterator last );
        template< class Range >
        iterator  erase( const Range& r );

    public: // pointer container requirements
        template< class PtrSequence >
        void transfer( iterator before, typename PtrSequence::iterator object,
                       PtrSequence& from );
        template< class PtrSequence >
        void transfer( iterator before, typename PtrSequence::iterator first, typename PtrSequence::iterator last,
                       PtrSequence& from );
        void template< class PtrSequence, class Range >
        void transfer( iterator before, const Range& r, PtrSequence& from );
        template< class PtrSequence >
        void transfer( iterator before, PtrSequence& from );

    public: // algorithms

        void sort();
        void sort( iterator first, iterator last );
        template< class Compare >
        void sort( Compare comp );
        template< class Compare >
        void sort( iterator begin, iterator end, Compare comp );

        void unique();
        void unique( iterator first, iterator last );
        template< class Compare >
        void unique( Compare comp );
        template< class Compare >
        void unique( iterator begin, iterator end, Compare comp );

        template< class Pred >
        void erase_if( Pred pred );
        template< class Pred >
        void erase_if( iterator begin, iterator end, Pred pred );

        void merge( ptr_sequence_adapter& r );
        template< class Compare >
        void merge( ptr_sequence_adapter& r, Compare comp );
        void merge( iterator first, iterator last, ptr_sequence_adapter& from );
        template< class Compare >
        void merge( iterator first, iterator last, ptr_sequence_adapter& from, Compare comp );

    public: // ptr_list interface

    public: // ptr_vector interface

    public: // ptr_deque interface

    }; //  class 'ptr_sequence_adapter'

} // namespace 'boost'

Semantics

Semantics: construct/copy/destroy

  • template< class InputIterator > void assign( InputIterator first, InputIterator last );

    • Requirements: (first,last] is a valid range

    • Effects: clear(); insert( first, last );

    • Postconditions: size() == std::distance( first, last );

    • Exception safety: strong guarantee

  • template< class InputRange > void assign( const InputRange& r );

    • Effects: assign( boost::begin(r), boost::end(r) );

Semantics: element access

  • T& front();

    • Requirements: not empty();

    • Effects: return *begin();

    • Throws: bad_ptr_container_operation if empty() == true

  • const T& front() const;

    • Requirements: not empty();

    • Effects: return *begin();

    • Throws: bad_ptr_container_operation if empty() == true

  • T& back();

    • Requirements: not empty();

    • Effects: return *--end();

    • Throws: bad_ptr_container_operation if empty() == true

  • const T& back() const;

    • Requirements: not empty();

    • Effects: return *--end();

    • Throws: bad_ptr_container_operation if empty() == true

Semantics: modifiers

  • void push_back( T* x );

    • Requirements: x != 0

    • Effects: Inserts the pointer into container and takes ownership of it

    • Throws: bad_pointer if x == 0

    • Exception safety: Strong guarantee

  • template< class U > void push_back( std::auto_ptr<U> x );

    • Effects: push_back( x.release() );

  • auto_type pop_back();

    • Requirements:not empty()

    • Effects: Removes the last element in the container

    • Postconditions: size() is one less

    • Throws: bad_ptr_container_operation if empty() == true

    • Exception safety: Strong guarantee

  • iterator insert( iterator position, T* x );

    • Requirements: position is a valid iterator from the container and x != 0

    • Effects: Inserts x before position and returns an iterator pointing to it

    • Throws: bad_pointer if x == 0

    • Exception safety: Strong guarantee

  • template< class U > iterator insert( iterator position, std::auto_ptr<U> x );

    • Effects: return insert( position, x.release() );

  • template< class InputIterator > void insert( iterator position, InputIterator first, InputIterator last );

    • Requirements: position is a valid iterator from the container

    • Effects: Inserts a cloned range before position

    • Exception safety: Strong guarantee

  • template< class InputRange > void insert( iterator position, const InputRange& r );
    • Effects: i nsert( position, boost::begin(r), boost::end(r) );
  • iterator erase( iterator position );

    • Requirements: position is a valid iterator from the container

    • Effects: Removes the element defined by position and returns an iterator to the following element

    • Throws: Nothing

  • iterator erase( iterator first, iterator last );

    • Requirements: [first,last) is a valid range

    • Effects: Removes the range of element defined by [first,last) and returns an iterator to the following element

    • Throws: Nothing

  • template< class Range > void erase( const Range& r );

    • Effects: erase( boost::begin(r), boost::end(r) );

Semantics: pointer container requirements

Use transfer() to move elements between two containers of the same type. Furthermore, move elements from a container of type T to a container of type U as long as T::value_type is convertible to U::value_type. An example would be transferring from boost::ptr_vector<Derived> to boost::ptr_deque<Base>.

(Remark: When moving elements between two different containers, make sure the allocators are compatible. The special latitude of being able to transfer between two different containers is only available for Sequences and not for Associative Containers.)

  • template< class PtrSequence > void transfer( iterator before, typename PtrSequence::iterator object, PtrSequence& from );

    • Effects: Inserts the object defined by object into the container and remove it from from. Insertion takes place before before.

    • Postconditions: If from.empty(), nothing happens. Otherwise size() is one more, from.size() is one less.

    • Exception safety: Strong guarantee

  • template< class PtrSequence > void transfer( iterator before, typename PtrSequence::iterator first, typename PtrSequence::iterator last, PtrSequence& from );

    • Requirements: from.size() >= std::distance(first,last)

    • Effects: Inserts the objects defined by the range [first,last) into the container and remove it from from. Insertion takes place before before.

    • Postconditions: If from.empty(), nothing happens. Otherwise, let N == std::distance(first,last); then size() is N more, from.size() is N less.

    • Exception safety: Strong guarantee

    • Complexity: Linear or better

  • void template< class PtrSequence, class Range > void transfer( iterator before, const Range& r, PtrSequence& from );

    • Effects: transfer(before, boost::begin(r), boost::end(r), from);

  • template< class PtrSequence> void transfer( iterator before, PtrSequence& from );

    • Effects: transfer(before, from, from);

Semantics: algorithms

The general requirement for these algorithms is that the container does not contain any nulls.

  • void sort();

  • void sort( iterator first, iterator last );

  • template< class Compare > void sort( Compare comp );

  • template< class Compare > void sort( iterator begin, iterator end, Compare comp );

    • Requirements: (versions without Compare) bool operator<( const T&, const T& ) is defined

    • Requirements: (Compare versions) Compare must take const T& arguments

    • Effects: sorts the entire container or the specified range

    • Exception safety: nothrow guarantee (the behavior is undefined if the comparison operator throws)

    • Remarks: The versions of sort() that take two iterators are not available for ptr_list

  • void unique();

  • void unique( iterator first, iterator last );

  • template< class Compare > void unique( Compare comp );

  • template< class Compare > void unique( iterator begin, iterator end, Compare comp );

    • Requirements: (versions without Compare) bool operator==( const T&, const T& ) is defined

    • Requirements: (Compare versions) Compare must take const T& arguments

    • Effects: removes adjacent and equal objects from the entire container or the specified range

    • Exception safety: nothrow guarantee (the behavior is undefined if the comparison operator throws)

  • template< class Pred > void erase_if( Pred pred );

  • template< class Pred > void erase_if( iterator begin, iterator end, Pred pred );

    • Requirements: Pred must take an const T& argument

    • Effects: removes all elements t for which pred(t) returns true from the entire container or the specified range

    • Exception safety: nothrow guarantee (the behavior is undefined if the comparison operator throws)

  • void merge( ptr_sequence_adapter& r );

  • template< class Compare > void merge( ptr_sequence_adapter& r, Compare comp );

  • void merge( iterator first, iterator last, ptr_sequence_adapter& from );

  • template< class Compare > void merge( iterator first, iterator last, ptr_sequence_adapter& from, Compare comp);

    • Requirements: (Compare versions) Compare must take const T& arguments

    • Requirements: both sequences are sorted wrt. the same predicate

    • Effects: transfers the entire container or the specified sequence to the container while ensuring the new sequence is also sorted

    • Postconditions: (Container versions) r.empty()

    • Exception safety: nothrow guarantee (the behavior is undefined if the comparison operator throws)

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

ptr_sequence_adapter: This class creates custom pointer containers.

Synopsis

namespace boost
{

    template
    <
        class T,
        class VoidPtrSeq,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_sequence_adapter
    {
    public: // construct/copy/destroy
        template< class InputIterator >
        assign( InputIterator first, InputIterator last );
        template< class InputRange >
        assign( const InputRange& e );

    public: // element access
        T&        front();
        const T&  front() const;
        T&        back();
        const T&  back() const;

    public: // modifiers
        void      push_back( T* x );
        template< class U >
        void      push_back( std::auto_ptr<U> x );
        auto_type pop_back();
        iterator  insert( iterator position, T* x );
        template< class U >
        iterator  insert( iterator position, std::auto_ptr<U> x );
        template< class InputIterator >
        void      insert( iterator position, InputIterator first, InputIterator last );
        template< class InputRange >
        void      insert( iterator position, const InputRange& r );
        iterator  erase( iterator position );
        iterator  erase( iterator first, iterator last );
        template< class Range >
        iterator  erase( const Range& r );

    public: // pointer container requirements
        template< class PtrSequence >
        void transfer( iterator before, typename PtrSequence::iterator object,
                       PtrSequence& from );
        template< class PtrSequence >
        void transfer( iterator before, typename PtrSequence::iterator first, typename PtrSequence::iterator last,
                       PtrSequence& from );
        void template< class PtrSequence, class Range >
        void transfer( iterator before, const Range& r, PtrSequence& from );
        template< class PtrSequence >
        void transfer( iterator before, PtrSequence& from );

    public: // algorithms

        void sort();
        void sort( iterator first, iterator last );
        template< class Compare >
        void sort( Compare comp );
        template< class Compare >
        void sort( iterator begin, iterator end, Compare comp );

        void unique();
        void unique( iterator first, iterator last );
        template< class Compare >
        void unique( Compare comp );
        template< class Compare >
        void unique( iterator begin, iterator end, Compare comp );

        template< class Pred >
        void erase_if( Pred pred );
        template< class Pred >
        void erase_if( iterator begin, iterator end, Pred pred );

        void merge( ptr_sequence_adapter& r );
        template< class Compare >
        void merge( ptr_sequence_adapter& r, Compare comp );
        void merge( iterator first, iterator last, ptr_sequence_adapter& from );
        template< class Compare >
        void merge( iterator first, iterator last, ptr_sequence_adapter& from, Compare comp );

    public: // ptr_list interface

    public: // ptr_vector interface

    public: // ptr_deque interface

    }; //  class 'ptr_sequence_adapter'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_set

A ptr_set<T> is a pointer container that uses an underlying std::set<void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

See also

  • void_ptr_indirect_fun

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class Key,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_set : public  ptr_set_adapter
                            <
                                Key,
                                std::set<void*,
                                void_ptr_indirect_fun<Compare,Key>,Allocator>,
                                CloneAllocator
                            >
    {
        // see references

    }; //  class 'ptr_set'

} // namespace 'boost'

Remarks:

  • Using nullable<T> as Key is meaningless and not allowed

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Class ptr_set_adapter

This class is used to build custom pointer containers with an underlying set-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Hierarchy:

  • reversible_ptr_container
    • associative_ptr_container
      • ptr_set_adapter
      • ptr_multiset_adapter
      • ptr_map_adapter
      • ptr_multi_map_adapter
        • ptr_set
        • ptr_multi_set
        • ptr_map
        • ptr_multimap

Navigate

  • home

  • reference

Synopsis

namespace boost
{
    template
    <
        class Key,
        class VoidPtrSet,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_set_adapter
    {

    public: // modifiers
        std::pair<iterator,bool>  insert( Key* x );
        template< class Key2 >
        std::pair<iterator,bool>  insert( std::auto_ptr<Key2> x );

    public: // pointer container requirements
        bool      transfer( iterator object, ptr_set_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_set_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_set_adapter& from );
        size_type transfer( ptr_set_adapter& from );

    }; //  class 'ptr_set_adapter'

} // namespace 'boost'

Semantics

Semantics: modifiers

  • std::pair<iterator,bool> insert( key_type* x );

    • Requirements: x != 0

    • Effects: Takes ownership of x and insert it if there is no equivalent of it already. The bool part of the return value indicates insertion and the iterator points to the element with key x.

    • Throws: bad_pointer if x == 0

    • Exception safety: Strong guarantee

  • template< class Key2 > std::pair<iterator,bool>  insert( std::auto_ptr<Key2> x );

    • Effects: return insert( x.release() );

Semantics: pointer container requirements

  • bool transfer( iterator object, ptr_set_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the object defined by object into the container and remove it from from if no equivalent object exists.

    • Returns: whether the object was transferred

    • Exception safety: Strong guarantee

  • void transfer( iterator first, iterator last, ptr__set_adapter& from );

    • Requirements: not from.empty()

    • Effects: Inserts the objects defined by the range [first,last) into the container and remove it from from. An object is only transferred if no equivalent object exists.

    • Returns: the number of transferred objects

    • Exception safety: Basic guarantee

  • template< class Range > void transfer( const Range& r, ptr_set_adapter& from );

    • Effects: Returns transfer( boost::begin(r), boost::end(r), from );

  • size_type transfer( ptr_set_adapter& from );

    • Effects: Returns transfer( from.begin(), from.end(), from );

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

ptr_set_adapter:This class is used to build custom pointer containers with an underlying set-like container. The interface of the class is an extension of the interface from associative_ptr_container.

Synopsis

namespace boost
{
    template
    <
        class Key,
        class VoidPtrSet,
        class CloneAllocator = heap_clone_allocator
    >
    class ptr_set_adapter
    {

    public: // modifiers
        std::pair<iterator,bool>  insert( Key* x );
        template< class Key2 >
        std::pair<iterator,bool>  insert( std::auto_ptr<Key2> x );

    public: // pointer container requirements
        bool      transfer( iterator object, ptr_set_adapter& from );
        size_type transfer( iterator first, iterator last, ptr_set_adapter& from );
        template< class Range >
        size_type transfer( const Range& r, ptr_set_adapter& from );
        size_type transfer( ptr_set_adapter& from );

    }; //  class 'ptr_set_adapter'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Name

ptr_set: a pointer container that uses an underlying std::set<void*> to store the pointers./font>

Synopsis

namespace boost
{

    template
    <
        class Key,
        class Compare        = std::less<Key>,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_set : public  ptr_set_adapter
                            <
                                Key,
                                std::set<void*,
                                void_ptr_indirect_fun<Compare,Key>,Allocator>,
                                CloneAllocator
                            >
    {
        // see references

    }; //  class 'ptr_set'

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Class ptr_vector

A ptr_vector<T> is a pointer container that uses an underlying std::vector<void*> to store the pointers.

Hierarchy:

  • reversible_ptr_container
    • ptr_sequence_adapter
      • ptr_vector
      • ptr_list
      • ptr_deque
      • ptr_array

Navigate

  • home

  • reference

Synopsis

namespace boost
{

    template
    <
        class T,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_vector : public ptr_sequence_adapter
                              <
                                  T,
                                  std::vector<void*,Allocator>,
                                  CloneAllocator
                              >
    {
    public: // construction
        ptr_vector( size_type to_reserve );

    public: // capacity
        size_type  capacity() const;
        void       reserve( size_type n );

    public: // element access
        T&        operator[]( size_type n );
        const T&  operator[]( size_type n ) const;
        T&        at( size_type n );
        const T&  at( size_type n ) const;

    public: // pointer container requirements
       auto_type replace( size_type idx, T* x );
       template< class U >
       auto_type replace( size_type idx, std::auto_ptr<U> x );
       bool      is_null( size_type idx ) const;
    };

} // namespace 'boost'

Semantics

Semantics: construction

  • ptr_vector( size_type to_reserve );

    Constructs an empty vector with a buffer of size least to_reserve.

Semantics: capacity

  • size_type capacity() const;

    • Effects: Returns the size of the allocated buffer

    • Throws: Nothing

  • void reserve( size_type n );

    • Requirements: n <= max_size()

    • Effects: Expands the allocated buffer

    • Postcondition: capacity() >= n

    • Throws: std::length_error() if n > max_size()

Semantics: element access

  • T& operator[]( size_type n );
  • const T& operator[]( size_type n ) const;

    • Requirements: n < size()

    • Effects: Returns a reference to the n'th element

    • Throws: Nothing

  • T& at( size_type n );
  • const T& at( size_type n ) const;

    • Requirements: n < size()

    • Effects: Returns a reference to the n'th element

    • Throws: bad_index if n >= size()

Semantics: pointer container requirements

  • auto_type replace( size_type idx, T* x );

    • Requirements: `` x != 0 and idx < size()``

    • Effects: returns the object indexed by idx and replaces it with x.

    • Throws: bad_index if idx >= size() and bad_pointer if x == 0.

    • Exception safety: Strong guarantee

  • template< class U > auto_type replace( size_type idx, std::auto_ptr<U> x );

    • Effects: return replace( idx, x.release() );

  • bool is_null( size_type idx ) const;

    • Requirements: idx < size()

    • Effects: returns whether the pointer at index idx is null

    • Exception safety: Nothrow guarantee

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Name

ptr_vector: This is a pointer container that uses an underlying std::vector<void*> to store the pointers.

Synopsis

namespace boost
{

    template
    <
        class T,
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_vector : public ptr_sequence_adapter
                              <
                                  T,
                                  std::vector<void*,Allocator>,
                                  CloneAllocator
                              >
    {
    public: // construction
        ptr_vector( size_type to_reserve );

    public: // capacity
        size_type  capacity() const;
        void       reserve( size_type n );

    public: // element access
        T&        operator[]( size_type n );
        const T&  operator[]( size_type n ) const;
        T&        at( size_type n );
        const T&  at( size_type n ) const;

    public: // pointer container requirements
       auto_type replace( size_type idx, T* x );
       template< class U >
       auto_type replace( size_type idx, std::auto_ptr<U> x );
       bool      is_null( size_type idx ) const;
    };

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Reference

The documentation is divided into an explanation for each container. When containers have the same interface, that common interface is explained only once, but links are always provided to more relevant information. Understand the Clonable concept and the Clone Allocator concept.

  • Conventions

  • The Clonable concept

  • The Clone Allocator concept

  • Class hierarchy:

    • reversible_ptr_container

      • ptr_sequence_adapter

        • ptr_vector

        • ptr_list

        • ptr_deque

        • ptr_array

      • associative_ptr_container

        • ptr_set_adapter

        • ptr_multiset_adapter

        • ptr_map_adapter

        • ptr_multi_map_adapter

          • ptr_set

          • ptr_multi_set

          • ptr_map

          • ptr_multimap

  • Serialization

  • Indirected functions

  • Class nullable

  • Exception classes

  • Disabling the use of exceptions

The Clonable concept

Refinement of

  • Heap Allocable

  • Heap Deallocable

The Clonable concept is introduced to formalize the requirements for copying heap-allocated objects. A type T might be Clonable even though it is not Assignable or Copy Constructible. Notice that many operations on the containers does not even require the stored type to be Clonable.

Notation

Type | Object (const or non-const) | Pointer | Describes
T | a | ptr | A Clonable type

Valid expressions

Expression | Type | Semantics | Postcondition
new_clone(a); | T* | Allocate a new object that can be considered equivalent to the a object | typeid(*new_clone(a)) == typeid(a)
delete_clone(ptr); | void | Deallocate an object previously allocated with allocate_clone(). Must not throw |

Default implementation

In the <boost/ptr_container/clone_allocator.hpp> header a default implementation of the two functions is given:

namespace boost
{
    template< class T >
    inline T* new_clone( const T& t )
    {
        return new T( t );
    }

    template< class T >
    void delete_clone( const T* t )
    {
        checked_delete( r );
    }
}

Notice that this implementation makes normal Copy Constructible classes are automatically Clonable unless operator new() or operator delete() are hidden.

The two functions represent a layer of indirection which is necessary to support classes that are not Copy Constructible by default. Notice that the implementation relies on argument-dependent lookup (ADL) to find the right version of new_clone() and delete_clone(). This means that one does not need to overload or specialize the function is the boost namespace, but it can be placed together with the rest of the interface of the class. If you are implementing a class inline in headers, remember to forward declare the functions.

Warning: We are considering to remove the default implementation above. Therefore always make sure to overload the functions for types and do not rely on the defaults in any way.

The Clone Allocator concept

The Clone Allocator concept is introduced to formalize the way pointer containers controls memory of the stored objects (and not the pointers to the stored objects). The clone allocator allows users to apply custom allocators/deallocators for the cloned objects.

More information can be found below:

  • Clone Allocator requirements

  • Class heap_clone_allocator

  • Class view_clone_allocator

Clone Allocator requirements

Notation

Type | Object (const or non-const) | Describes
T | a | A type
T* | ptr | A pointer to T

Valid expressions

Expression | Type | Semantics | Postcondition
CloneAllocator::allocate_clone(a); | T* | Allocate a new object that can be considered equivalent to the a object | typeid(*CloneAllocator::allocate_clone(a)) == typeid(a)
CloneAllocator::deallocate_clone(ptr); | void | Deallocate an object previously allocated with CloneAllocator::allocate_clone() or a compatible allocator. Must not throw. |

The library comes with two predefined clone allocators.

Class heap_clone_allocator

This is the default clone allocator used by all pointer containers. For most purposes you will never have to change this default.

Definition

namespace boost
{
    struct heap_clone_allocator
    {
        template< class U >
        static U* allocate_clone( const U& r )
        {
            return new_clone( r );
        }

        template< class U >
        static void deallocate_clone( const U* r ) const
        {
            delete_clone( r );
        }
    };
}

Notice that the above definition allows you to support custom allocation schemes by relying on new_clone() and delete_clone().

Class view_clone_allocator

This class provides a way to remove ownership properties of the pointer containers. Use the pointer containers as a view into an existing container.

Definition

namespace boost
{
    struct view_clone_allocator
    {
        template< class U >
        static U* allocate_clone( const U& r )
        {
            return const_cast<U*>(&r);
        }

        template< class U >
        static void deallocate_clone( const U* )
        {
            // empty
        }
    };
}

Class hierarchy

The library consists of the following types of classes:

  1. Pointer container adapters

  2. Pointer containers

The pointer container adapters are used to make a pointer container starting from the own "normal" container. For example, if the user has a map class that extends std::map in some way; the adapter class then allows the user to map class as a basis for a new pointer container.

The library provides an adapter for each type of standard container highlighted as links below:

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

The pointer containers of this library are all built using the adapters. There is a pointer container for each type of "normal" standard container highlighted as links below.

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

Serialization

As of version 1.34.0 of Boost, the library support serialization as defined by Boost.Serialization.

Of course, for serialization to work it is required that the stored type itself is serializable. For maps, both the key type and the mapped type must be serializable.

When dealing with serialization (and serialization of polymophic objects in particular), pay special attention to these parts of Boost.Serialization:

  1. Output/saving requires a const-reference:

    //
// serialization helper: we can't save a non-const object
//
template< class T >
inline T const& as_const( T const& r )
{
    return r;
}
...
Container cont;

std::ofstream ofs("filename");
boost::archive::text_oarchive oa(ofs);
oa << as_const(cont);

    See Compile time trap when saving a non-const value for details.

  2. Derived classes need to call base_object() function:

    struct Derived : Base
{
    template< class Archive >
    void serialize( Archive& ar, const unsigned int version )
    {
        ar & boost::serialization::base_object<Base>( *this );
        ...
    }
};

    For details, see Derived Classes.

  3. Use BOOST_CLASS_EXPORT to register the derived classes in the class hierarchy:

    BOOST_CLASS_EXPORT( Derived )

    See Export Key and Object Tracking for details.

Remember these three issues and it will a lot of trouble.

Class nullable

The purpose of the class is simply to tell the containers that null values should be allowed. Its definition is trivial:

namespace boost
{
    template< class T >
    struct nullable
    {
        typedef T type;
    };
}

Please notice that nullable has no effect on the containers interface (except for is_null() functions). For example, it does not make sense to do

boost::ptr_vector< boost::nullable<T> > vec;
vec.push_back( 0 );                      // ok
vec.push_back( new boost::nullable<T> ); // no no!
boost::nullable<T>& ref = vec[0];        // also no no!

Exception classes

There are three exceptions that are thrown by this library. The exception hierarchy looks as follows:

namespace boost
{
    class bad_ptr_container_operation : public std::exception
    {
    public:
        bad_ptr_container_operation( const char* what );
    };

    class bad_index : public bad_ptr_container_operation
    {
    public:
        bad_index( const char* what );
    };

    class bad_pointer : public bad_ptr_container_operation
    {
    public:
        bad_pointer();
        bad_pointer( const char* what );
    };
}

Disabling the use of exceptions

As of version 1.34.0 of Boost, the library allows the user to disable exceptions completely. This means the library is more fit for domains where exceptions are not used. Furthermore, it also speeds up a operations a little. Instead of throwing an exception, the library simply calls BOOST_ASSERT.

To disable exceptions, simply define this macro before including any header:

#define BOOST_PTR_CONTAINER_NO_EXCEPTIONS 1
#include <boost/ptr_container/ptr_vector.hpp>

It is, however, recommended to define the macro on the command-line, so that all headers are compiled the same way. Otherwise the user might end up breaking the One Definition Rule.

If BOOST_NO_EXCEPTIONS is defined, then BOOST_PTR_CONTAINER_NO_EXCEPTIONS is also defined.

Navigate

  • home

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Boost Pointer Container Library

Pointer Container Library

Class reversible_ptr_container

This class is not a real class that can be found in the library. Its purpose is to present the general interface of all the pointer containers.

Hierarchy:

  • reversible_ptr_container

    • ptr_sequence_adapter

      • ptr_vector

      • ptr_list

      • ptr_deque

      • ptr_array

    • associative_ptr_container

      • ptr_set_adapter

      • ptr_multiset_adapter

      • ptr_map_adapter

      • ptr_multi_map_adapter

        • ptr_set

        • ptr_multi_set

        • ptr_map

        • ptr_multimap

Navigate

  • home

  • reference

S ynopsis

namespace boost
{
    template
    <
        class T,
        class CloneAllocator,
        class VoidPtrContainer
    >
    class reversible_ptr_container
    {
    public: // typedefs
        typedef  T*                                           value_type;
        typedef  T&                                           reference;
        typedef  const T&                                     const_reference;
        typedef  implementation defined                       iterator;
        typedef  implementation defined                       const_iterator;
        typedef  typename VoidPtrContainer::differnce_type    difference_type;
        typedef  typename VoidPtrContainer::size_type         size_type;
        typedef  typename VoidPtrContainer::allocator_type    allocator_type;
        typedef  implementation defined                       reverse_iterator;
        typedef  implementation defined                       const_reverse_iterator;
        typedef  implementation defined                       auto_type;

    public: // construct/copy/destroy
        reversible_ptr_container();
        reversible_ptr_container( auto_ptr<reversible_ptr_container> r );
        template< class InputIterator >
        reversible_ptr_container( InputIterator first, InputIterator last );
        ~reversible_ptr_container();
        void operator=( std::auto_ptr<reversible_ptr_container> r )
        allocator_type get_allocator() const;

    public: // iterators
        iterator                begin();
        const_iterator          begin() const;
        iterator                end();
        const_iterator          end() const;
        reverse_iterator        rbegin();
        const_reverse_iterator  rbegin() const;
        reverse_iterator        rend();
        const_reverse_iterator  rend() const;

    public: // capacity
        size_type  size() const;
        size_type  max_size() const;
        bool       empty() const;

    public: // modifiers
        void      swap( reversible_ptr_container& r );
        void      clear():

    public: // pointer container requirements
        auto_type                                replace( iterator position, T* x );
        template< class U >
        auto_type                                replace( iterator position, std::auto_ptr<U> x );
        std::auto_ptr<reversible_ptr_container>  clone() const;
        std::auto_ptr<reversible_ptr_container>  release();
        auto_type                                release( iterator position );

    public: // serialization
        template< class Archive >
        void save( Archive& ar, const unsigned version ) const;
        template< class Archive >
        void load( Archive& ar, const unsigned version );

    }; //  class 'reversible_ptr_container'

    // comparison
    template < class T, class CA, class VPC >
    bool operator==( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template < class T, class CA, class VPC  >
    bool operator<( const reversible_ptr_container<T,CA,VPC>& x,
                    const reversible_ptr_container<T,CA,VPC>& y);

    template < class T, class CA, class VPC  >
    bool operator!=( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template < class T,  class CA, class VPC >
    bool operator>( const reversible_ptr_container<T,CA,VPC>& x,
                    const reversible_ptr_container<T,CA,VPC>& y);

    template < class T,  class CA, class VPC >
    bool operator>=( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template < class T,  class CA, class VPC >
    bool operator<=( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template< class T,  class CA, class VPC  >
    void swap( reversible_ptr_container<T,CA,VPC>& x,
               reversible_ptr_container<T,CA,VPC>& y );

    // clonability
    template< class T,  class CA, class VPC >
    reversible_ptr_container<T,CA,VPC>*
    new_clone( const reversible_ptr_container<T,CA,VPC>& r );

    // null predicate
    template< class Iterator >
    bool is_null( Iterator i );

} // namespace 'boost'

Semantics

Semantics: typedefs

Notice how these two types differ:

  • typedef T* value_type;

    • Notice this has pointer type

  • typedef T& reference;

    • Notice this is not a pointer type

This is done to be able to add pointers directly to the container, but to hide the pointers externally.

Also notice that

  • typedef ... iterator

allows one to iterate over T& objects, not T*. Note that:

iterator i = ...;
i.base();

returns an iterator that allows one to iterate over void* elements (this is very rarely needed. The user is advised not use the functionality unless the one knows what is being done).

  • typedef ... auto_type

This declaration hides a pointer pointer type. Rely on the following operations

T* operator->() const;
T& operator*() const;
T* release();
~auto_type();
operator implementation-defined bool();

The destructor will delete the stored object. It might help to think it is just an std::auto_ptr<T>.

Semantics: construct/copy/destroy

  • reversible_ptr_container();

    • Effects: Constructs an empty container

    • Postconditions: size() == 0

  • explicit reversible_ptr_container( std::auto_ptr< reversible_ptr_container > r );

    • Effects: Constructs a container by taking ownership of the supplied pointers

  • template< class InputIterator > reversible_ptr_container( InputIterator first, InputIterator last );

    • Requirements: (first,last] is a valid range

    • Effects: Constructs a container with a cloned range of (first,last]

    • Postconditions: size() == std::distance( first, last )
  • ~reversible_ptr_container();

    • Effects: Deletes the stored objects

    • Throws: Nothing

  • void operator=( std::auto_ptr<reversible_ptr_container> r );

    • Effects: Deletes the stored objects and then takes ownership of the supplied pointers

    • Throws: Nothing

  • allocator_type get_allocator() const;

    • Effects: Returns a copy of the allocator of the container object

Semantics: iterators

See also: iterator invalidation

  • iterator begin();
  • const_iterator begin() const;

    • Effects: Returns a mutable/non-mutable iterator with value_type T

    • Throws: Nothing

  • iterator end();
  • const_iterator end() const;

    • Effects: Returns a mutable/non-mutable iterator with value_type T

    • Throws: Nothing

  • reverse_iterator rbegin();
  • const_reverse_iterator rbegin() const;

    • Effects: Returns a mutable/non-mutable reverse iterator with value_type T

    • Throws: Nothing

  • reverse_iterator rend();
  • const_reverse_iterator rend() const;

    • Effects: Returns a mutable/non-mutable reverse iterator with value_type T

    • Throws: Nothing

Semantics: capacity

  • size_type size() const;

    • Effects: Returns the number of stored elements

    • Throws: Nothing

  • size_type max_size() const;

    • Effects: Returns the maximum number of stored elements

    • Throws: Nothing

  • bool empty() const;

    • Effects: Returns whether the container is empty or not

    • Throws: Nothing

Semantics: modifiers

  • void swap( reversible_ptr_container& r );

    • Effects: Swaps the content of the two containers

    • Throws: Nothing

  • void clear();

    • Effects: Destroys all object of the container

    • Postconditions: empty() == true

    • Throws: Nothing

Semantics: pointer container requirements

  • auto_type replace( iterator position, T* x );

    • Requirements: not empty() and x != 0

    • Effects: returns the object pointed to by position and replaces it with x.

    • Throws: bad_ptr_container_operation if the container is empty and bad_pointer if x == 0.

    • Exception safety: Strong guarantee

  • template< class U > auto_type replace( iterator position, std::auto_ptr<U> x );

    • Effects: return replace( position, x.release() );

  • std::auto_ptr< reversible_ptr_container > clone() const;

    • Effects: Returns a deep copy of the container

    • Throws: std::bad_alloc if there is not enough memory to make a clone of the container

    • Complexity: Linear

  • std::auto_ptr< reversible_ptr_container > release();

    • Effects: Releases ownership of the container. This is a useful way of returning a container from a function.

    • Postconditions: empty() == true

    • Throws: std::bad_alloc if the return value cannot be allocated

    • Exception safety: Strong guarantee

  • auto_type release( iterator position );

    • Requirements: not empty();

    • Effects: Releases ownership of the pointer referred to by position

    • Postconditions: size() is one less

    • Throws: bad_ptr_container_operation if the container is empty

    • Exception safety: Strong guarantee

Semantics: comparison

These functions compare the underlying range of objects. So

operation( const ptr_container& l, const ptr_container& r );

has the effect one would expect of normal standard containers. Hence objects are compared and not the pointers to objects.

Semantics: clonability

  • template< class T, class CloneAllocator > reversible_ptr_container<T,CA,VPC>* new_clone( const reversible_ptr_container<T,CA,VPC>& r );

    • Effects: return r.clone().release();

    • Remarks: This function is only defined for concrete pointer containers, but not for pointer container adapters.

Semantics: null predicate

  • template< class Iterator > bool is_null( Iterator i );

    • Requirements: i is a valid dereferencable iterator

    • Returns: *i.base() == 0;

Semantics: serialization

All containers can be serialized by means of Boost.Serialization. For an overview, see Serialization of Pointer Containers.

  • template< class Archive > void save( Archive& ar, const unsigned version ) const;

    • Effects: Saves the container to the archive.

    • Remarks: This function is called automatically be stream operators in Boost.Serialization

  • template< class Archive > void load( Archive& ar, const unsigned version );

    • Effects: Clears the container and then loads a new container from the archive.

    • Remarks: This function is called automatically be stream operators in Boost.Serialization

    • Exception safety: Basic guarantee

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see License.html).


Boost pointer container library

Name

reversible_ptr_container: This class presents the general interface of all pointer containers.

Synopsis

namespace boost
{
    template
    <
        class T,
        class CloneAllocator,
        class VoidPtrContainer
    >
    class reversible_ptr_container
    {
    public: // typedefs
        typedef  T*                                           value_type;
        typedef  T&                                           reference;
        typedef  const T&                                     const_reference;
        typedef  implementation defined                       iterator;
        typedef  implementation defined                       const_iterator;
        typedef  typename VoidPtrContainer::differnce_type    difference_type;
        typedef  typename VoidPtrContainer::size_type         size_type;
        typedef  typename VoidPtrContainer::allocator_type    allocator_type;
        typedef  implementation defined                       reverse_iterator;
        typedef  implementation defined                       const_reverse_iterator;
        typedef  implementation defined                       auto_type;

    public: // construct/copy/destroy
        reversible_ptr_container();
        reversible_ptr_container( auto_ptr<reversible_ptr_container> r );
        template< class InputIterator >
        reversible_ptr_container( InputIterator first, InputIterator last );
        ~reversible_ptr_container();
        void operator=( std::auto_ptr<reversible_ptr_container> r )
        allocator_type get_allocator() const;

    public: // iterators
        iterator                begin();
        const_iterator          begin() const;
        iterator                end();
        const_iterator          end() const;
        reverse_iterator        rbegin();
        const_reverse_iterator  rbegin() const;
        reverse_iterator        rend();
        const_reverse_iterator  rend() const;

    public: // capacity
        size_type  size() const;
        size_type  max_size() const;
        bool       empty() const;

    public: // modifiers
        void      swap( reversible_ptr_container& r );
        void      clear():

    public: // pointer container requirements
        auto_type                                replace( iterator position, T* x );
        template< class U >
        auto_type                                replace( iterator position, std::auto_ptr<U> x );
        std::auto_ptr<reversible_ptr_container>  clone() const;
        std::auto_ptr<reversible_ptr_container>  release();
        auto_type                                release( iterator position );

    public: // serialization
        template< class Archive >
        void save( Archive& ar, const unsigned version ) const;
        template< class Archive >
        void load( Archive& ar, const unsigned version );

    }; //  class 'reversible_ptr_container'

    // comparison
    template < class T, class CA, class VPC >
    bool operator==( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template < class T, class CA, class VPC  >
    bool operator<( const reversible_ptr_container<T,CA,VPC>& x,
                    const reversible_ptr_container<T,CA,VPC>& y);

    template < class T, class CA, class VPC  >
    bool operator!=( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template < class T,  class CA, class VPC >
    bool operator>( const reversible_ptr_container<T,CA,VPC>& x,
                    const reversible_ptr_container<T,CA,VPC>& y);

    template < class T,  class CA, class VPC >
    bool operator>=( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template < class T,  class CA, class VPC >
    bool operator<=( const reversible_ptr_container<T,CA,VPC>& x,
                     const reversible_ptr_container<T,CA,VPC>& y);

    template< class T,  class CA, class VPC  >
    void swap( reversible_ptr_container<T,CA,VPC>& x,
               reversible_ptr_container<T,CA,VPC>& y );

    // clonability
    template< class T,  class CA, class VPC >
    reversible_ptr_container<T,CA,VPC>*
    new_clone( const reversible_ptr_container<T,CA,VPC>& r );

    // null predicate
    template< class Iterator >
    bool is_null( Iterator i );

} // namespace 'boost'

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost Pointer Container Library

Pointer Container Library

Tutorial

The tutorial shows the most simple usage of the library. It is assumed that the reader is familiar with the use of standard containers. Although the tutorial is divided into sections, it is recommended to read it all from top to bottom.

  • Basic usage

  • Indirected interface

  • Sequence containers

  • Associative containers

  • Null values

  • Clonability

  • New functions

  • std::auto_ptr<U> overloads

  • Algorithms

Basic usage

The most important aspect of a pointer container is that it manages memory. This means that in most cases there is no need to worry about deleting memory.

Let us assume, an OO-hierarchy of animals

class animal : boost::noncopyable
{
public:
    virtual      ~animal()   {}
    virtual void eat()       = 0;
    virtual int  age() const = 0;
    // ...
};

class mammal : public animal
{
    // ...
};

class bird : public animal
{
    // ...
};

Then the managing of the animals is straight-forward. Imagine a Zoo:

class zoo
{
    boost::ptr_vector<animal> the_animals;
public:

    void add_animal( animal* a )
    {
        the_animals.push_back( a );
    }
};

Notice how just pass the class name to the container; there is no * to indicate it is a pointer. With this declaration, now say:

zoo the_zoo;
the_zoo.add_animal( new mammal("joe") );
the_zoo.add_animal( new bird("dodo") );

Thus, heap-allocate all elements of the container and never rely on copy-semantics.

Indirected interface

As particular feature of the pointer containers is that the query interface is indirected. For example,

boost::ptr_vector<animal> vec;
vec.push_back( new animal ); // you add it as pointer ...
vec[0].eat();                // but get a reference back

This indirection also happens to iterators, so

typedef std::vector<animal*> std_vec;
std_vec vec;
...
std_vec::iterator i = vec.begin();
(*i)->eat(); // '*' needed

now becomes

typedef boost::ptr_vector<animal>  ptr_vec;
ptr_vec vec;
ptr_vec::iterator i = vec.begin();
i->eat(); // no indirection needed

Sequence containers

The sequence containers used when there is no need to keep an ordering on the elements. Basically expect all operations of the normal standard containers to be available. So, for example, with a ptr_deque and ptr_list object the user can say:

boost::ptr_deque<animal> deq;
deq.push_front( new animal );
deq.pop_front();

because std::deque and std::list has push_front() and pop_front members.

If the standard sequence support random access, so does the pointer container; for example:

for( boost::ptr_deque<animal>::size_type i = 0u;
     i != deq.size(); ++i )
     deq[i].eat();

The ptr_vector also allows the user to specify the size of the buffer to allocate; for example

boost::ptr_vector<animal> animals( 10u );

will reserve room for 10 animals.

Associative containers

To keep an ordering on the animals, use a ptr_set:

boost::ptr_set<animal> set;
set.insert( new monkey("bobo") );
set.insert( new whale("anna") );
...

This requires that operator<() is defined for animals. One way to do this could be

inline bool operator<( const animal& l, const animal& r )
{
    return l.name() < r.name();
}

to keep the animals sorted by name.

To keep all the animals in zoo ordered with respect to their name, but it so happens that many animals have the same name. Then use a ptr_multimap:

typedef boost::ptr_multimap<std::string,animal> zoo_type;
zoo_type zoo;
std::string bobo = "bobo",
            anna = "anna";
zoo.insert( bobo, new monkey(bobo) );
zoo.insert( bobo, new elephant(bobo) );
zoo.insert( anna, new whale(anna) );
zoo.insert( anna, new emu(anna) );

Note that must create the key as an lvalue (due to exception-safety issues); the following would not have compiled

zoo.insert( "bobo", // this is bad, but you get compile error
            new monkey("bobo") );

If a multimap is not needed, use operator[]() to avoid the clumsiness:

boost::ptr_map<std::string,animal> animals;
animals["bobo"].set_name("bobo");

This requires a default constructor for animals and a function to do the initialization, in this case set_name().

A better alternative is to use Boost.Assign. In particular, consider

  • ptr_push_back(), ptr_push_front(),
ptr_insert() and
ptr_map_insert()

  • ptr_list_of()

For example, the above insertion may now be written

boost::ptr_multimap<std::string,animal> animals;

using namespace boost::assign;
ptr_map_insert<monkey>( animals )( "bobo", "bobo" );
ptr_map_insert<elephant>( animals )( "bobo", "bobo" );
ptr_map_insert<whale>( animals )( "anna", "anna" );
ptr_map_insert<emu>( animals )( "anna", "anna" );

Null values

By default, if the user inserts null into a container, an exception is thrown. To allow nulls, say so explicitly when declaring the container variable

boost::ptr_vector< boost::nullable<animal> > animals_type;
animals_type animals;
...
animals.insert( animals.end(), new dodo("fido") );
animals.insert( animals.begin(), 0 ) // ok

Once a null is inserted into the container, always check if the value is null before accessing the object

for( animals_type::iterator i = animals.begin();
     i != animals.end(); ++i )
{
    if( !boost::is_null(i) ) // always check for validity
        i->eat();
}

If the container supports random access, check this as

for( animals_type::size_type i = 0u;
     i != animals.size(); ++i )
{
    if( !animals.is_null(i) )
         animals[i].eat();
}

Note that it is meaningless to insert null into ptr_set and ptr_multiset.

Clonability

In OO programming it is typical to prohibit copying of objects; the objects may sometimes be allowed to be clonable; for example,:

animal* animal::clone() const
{
    return do_clone(); // implemented by private virtual function
}

If the OO hierarchy thus allows cloning, tell the pointer containers how cloning is to be done. This is simply done by defining a free-standing function, new_clone(), in the same namespace as the object hierarchy:

inline animal* new_clone( const animal& a )
{
    return a.clone();
}

That is all, now a lot of functions in a pointer container can exploit the clonability of the animal objects. For example

typedef boost::ptr_list<animal> zoo_type;
zoo_type zoo, another_zoo;
...
another_zoo.assign( zoo.begin(), zoo.end() );

will fill another zoo with clones of the first zoo. Similarly, insert() can now insert clones into the pointer container

another_zoo.insert( another_zoo.begin(), zoo.begin(), zoo.end() );

The whole container can now also be cloned

zoo_type yet_another_zoo = zoo.clone();

New functions

A few new functions make sense when working with pointers, . For example, to remove an animal from the zoo

zoo_type::auto_type the_animal = zoo.release( zoo.begin() );
the_animal->eat();
animal* the_animal_ptr = the_animal.release(); // now this is not deleted
zoo.release(2); // for random access containers

Think of auto_type as a non-copyable form of std::auto_ptr. Notice that when an object is released, the pointer is removed from the container and the containers size shrinks. For containers that store nulls, exploit that auto_type is convertible to bool:

if( ptr_vector< nullable<T> >::auto_type r = vec.pop_back() )
{
  ...
}

Release the entire container to return it from a function

std::auto_ptr< boost::ptr_deque<animal> > get_zoo()
{
    boost::ptr_deque<animal>  result;
    ...
    return result.release(); // give up ownership
}
...
boost::ptr_deque<animal> animals = get_zoo();

To move an animal object from one zoo to another. In other words, to move the animal and the responsibility of it to another zoo

another_zoo.transfer( another_zoo.end(), // insert before end
                      zoo.begin(),       // insert this animal ...
                      zoo );             // from this container

This kind of "move-semantics" is different from normal value-based containers. Think of transfer() as the same as splice() on std::list.

To replace an element,do:

zoo_type::auto_type old_animal = zoo.replace( zoo.begin(), new monkey("bibi") );
zoo.replace( 2, old_animal.release() ); // for random access containers

A map is slightly different to iterator over than standard maps. Now say,

typedef boost::ptr_map<std::string, boost::nullable<animal> > animal_map;
animal_map map;
...
for( animal_map::const_iterator i = map.begin(), e = map.end(); i != e; ++i )
{
    std::cout << "\n key: " << i->first;
    std::cout << "\n age: ";

    if( boost::is_null(i) )
        std::cout << "unknown";
    else
        std::cout << i->second->age();
 }

Except for the check for null, this looks like it would with a normal map. But if age() had not been a const member function, it would not have compiled.

Maps can also be indexed with bounds-checking

try
{
    animal& bobo = map.at("bobo");
}
catch( boost::bad_ptr_container_operation& e )
{
    // "bobo" not found
}

std::auto_ptr<U> overloads

Every time there is a function that takes a T* parameter, there is also a function taking an std::auto_ptr<U> parameter. This is of course done to make the library integrate with std::auto_ptr. For example

std::ptr_vector<Base> vec;
vec.push_back( new Base );

is complemented by

std::auto_ptr<Derived> p( new Derived );
vec.push_back( p );

Notice that the template argument for std::auto_ptr does not need to follow the template argument for ptr_vector as long as Derived* can be implicitly converted to Base*.

Algorithms

Unfortunately it is not possible to use pointer containers with mutating algorithms from the standard library. However, the most useful ones are instead provided as member functions:

boost::ptr_vector<animal> zoo;
...
zoo.sort();                               // assume 'bool operator<( const animal&, const animal& )'
zoo.sort( std::less<animal>() );          // the same, notice no '*' is present
zoo.sort( zoo.begin(), zoo.begin() + 5 ); // sort selected range

Notice that predicates are automatically wrapped in an indirect_fun object.

Remove equal and adjacent elements using unique():

zoo.unique();                             // assume 'bool operator==( const animal&, const animal& )'
zoo.unique( zoo.begin(), zoo.begin() + 5, my_comparison_predicate() );

To remove certain elements, use erase_if:

zoo.erase_if( my_predicate() );

Finally, to merge together two sorted containers:

boost::ptr_vector<animal> another_zoo = ...;
another_zoo.sort();                      // sorted wrt. to same order as 'zoo'
zoo.merge( another_zoo );
BOOST_ASSERT( another_zoo.empty() );

See also

  • Usage guidelines

  • Cast utilities

Navigate

  • home

  • examples

©Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).


Class any

boost::any — A class whose instances can hold instances of any type that satisfies ValueType requirements.

Synopsis

class any {
public:
  // construct/copy/destruct
  any();
  any(const any &);
  template<typename ValueType> any(const ValueType &);
  any & operator=(const any &);
  template<typename ValueType> any & operator=(const ValueType &);
  ~any();

  // modifiers
  any & swap(any &);

  // queries
  bool empty() const;
  const std::type_info & type() const;
};

Description

any construct/copy/destruct

  1. any();
    Postconditions: this->empty()
  2. any(const any & other);
    Effects: Copy constructor that copies content of other into new instance, so that any content is equivalent in both type and value to the content of other, or empty if other is empty.
    Throws: May fail with a std::bad_alloc exception or any exceptions arising from the copy constructor of the contained type.
  3. template<typename ValueType> any(const ValueType & value);
    Effects: Makes a copy of value, so that the initial content of the new instance is equivalent in both type and value to value.
    Throws: std::bad_alloc or any exceptions arising from the copy constructor of the contained type.
  4. any & operator=(const any & rhs);
    Effects: Copies content of rhs into current instance, discarding previous content, so that the new content is equivalent in both type and value to the content of rhs, or empty if rhs.empty().
    Throws: std::bad_alloc or any exceptions arising from the copy constructor of the contained type. Assignment satisfies the strong guarantee of exception safety.
  5. template<typename ValueType> any & operator=(const ValueType & rhs);
    Effects: Makes a copy of rhs, discarding previous content, so that the new content of is equivalent in both type and value to rhs.
    Throws: std::bad_alloc or any exceptions arising from the copy constructor of the contained type. Assignment satisfies the strong guarantee of exception safety.
  6. ~any();
    Effects: Releases any and all resources used in management of instance.
    Throws: Nothing.

any modifiers

  1. any & swap(any & rhs);
    Effects: Exchange of the contents of *this and rhs.
    Returns: *this
    Throws: Nothing.

any queries

  1. bool empty() const;
    Returns: true if instance is empty, otherwise false.
    Throws: Will not throw.
  2. const std::type_info & type() const;
    Returns: the typeid of the contained value if instance is non-empty, otherwise typeid(void).
    Notes: Useful for querying against types known either at compile time or only at runtime.
Copyright © 2001 Kevlin Henney


Function any_cast

boost::any_cast — Custom keyword cast for extracting a value of a given type from an any .

Synopsis

template<typename T> T any_cast(any & operand);
template<typename T> T any_cast(const any & operand);
template<typename ValueType> const ValueType * any_cast(const any * operand);
template<typename ValueType> ValueType * any_cast(any * operand);

Description

Returns: | If passed a pointer, it returns a similarly qualified pointer to the value content if successful, otherwise null is returned. If T is ValueType, it returns a copy of the held value, otherwise, if T is a reference to (possibly const qualified) ValueType, it returns a reference to the held value.
Throws: | Overloads taking an any pointer do not throw; overloads taking an any value or reference throws bad_any_cast if unsuccessful.
Copyright © 2001 Kevlin Henney


Reference

ValueType requirements
Header <boost/any.hpp>

ValueType requirements

Values are strongly informational objects for which identity is not significant, i.e. the focus is principally on their state content and any behavior organized around that. Another distinguishing feature of values is their granularity: normally fine-grained objects representing simple concepts in the system such as quantities.

As the emphasis of a value lies in its state not its identity, values can be copied and typically assigned one to another, requiring the explicit or implicit definition of a public copy constructor and public assignment operator. Values typically live within other scopes, i.e. within objects or blocks, rather than on the heap. Values are therefore normally passed around and manipulated directly as variables or through references, but not as pointers that emphasize identity and indirection.

The specific requirements on value types to be used in an any are:

  • A ValueType is CopyConstructible [20.1.3].

  • A ValueType is optionally Assignable [23.1]. The strong exception-safety guarantee is required for all forms of assignment.

  • The destructor for a ValueType upholds the no-throw exception-safety guarantee.

Header <boost/any.hpp>

namespace boost {
  class bad_any_cast;
  class any;
  template<typename T> T any_cast(any &);
  template<typename T> T any_cast(const any &);
  template<typename ValueType> const ValueType * any_cast(const any *);
  template<typename ValueType> ValueType * any_cast(any *);
}
Copyright © 2001 Kevlin Henney


Function apply_visitor

boost::apply_visitor — Allows compile-time checked type-safe application of the given visitor to the content of the given variant, ensuring that all types are handled by the visitor.

Synopsis

template<typename Visitor, typename Variant>
  typename Visitor::result_type
  apply_visitor(Visitor & visitor, Variant & operand);
template<typename Visitor, typename Variant>
  typename Visitor::result_type
  apply_visitor(const Visitor & visitor, Variant & operand);
template<typename BinaryVisitor, typename Variant1, typename Variant2>
  typename BinaryVisitor::result_type
  apply_visitor(BinaryVisitor & visitor, Variant1 & operand1,
                Variant2 & operand2);
template<typename BinaryVisitor, typename Variant1, typename Variant2>
  typename BinaryVisitor::result_type
  apply_visitor(const BinaryVisitor & visitor, Variant1 & operand1,
                Variant2 & operand2);
template<typename Visitor>
  apply_visitor_delayed_t<Visitor> apply_visitor(Visitor & visitor);

Description

The behavior of apply_visitor is dependent on the number of arguments on which it operates (i.e., other than the visitor). The function behaves as follows:

  • Overloads accepting one operand invoke the unary function call operator of the given visitor on the content of the given variant operand.

  • Overloads accepting two operands invoke the binary function call operator of the given visitor on the content of the given variant operands.

  • The overload accepting only a visitor returns a generic function object that accepts either one or two arguments and invokes apply_visitor using these arguments and visitor, thus behaving as specified above. (This behavior is particularly useful, for example, when one needs to operate on each element of a sequence of variant objects using a standard library algorithm.)

Returns: | The overloads accepting operands return the result of applying the given visitor to the content of the given operands. The overload accepting only a visitor return a function object, thus delaying application of the visitor to any operands.
Requires: | The given visitor must fulfill the StaticVisitor concept requirements with respect to each of the bounded types of the given variant.
Throws: | The overloads accepting operands throw only if the given visitor throws when applied. The overload accepting only a visitor will not throw. (Note, however, that the returned function object may throw when invoked.)
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template apply_visitor_delayed_t

boost::apply_visitor_delayed_t — Adapts a visitor for use as a function object.

Synopsis

template<typename Visitor>
class apply_visitor_delayed_t {
public:
  // types
  typedef typename Visitor::result_type result_type;

  // construct/copy/destruct
  explicit apply_visitor_delayed_t(Visitor &);

  // function object interface
  template<typename Variant> result_type operator()(Variant &);
  template<typename Variant1, typename Variant2>
    result_type operator()(Variant1 &, Variant2 &);
};

Description

Adapts the function given at construction for use as a function object. This is useful, for example, to operate on each element of a sequence of variant objects using a standard library algorithm such as std::for_each.

See the "visitor-only" form of apply_visitor for a simple way to create apply_visitor_delayed_t objects.

apply_visitor_delayed_t construct/copy/destruct

  1. explicit apply_visitor_delayed_t(Visitor & visitor);
    Effects: Constructs the function object with the given visitor.

apply_visitor_delayed_t function object interface

  1. template<typename Variant> result_type operator()(Variant & operand);
template<typename Variant1, typename Variant2>
  result_type operator()(Variant1 & operand1, Variant2 & operand2);

    Invokes apply_visitor on the stored visitor using the given operands.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Concept Assignable

Assignable

Description

Assignable types must have copy constructors, operator= for assignment, and the swap() function defined.

Refinement of

  • CopyConstructible

Notation

X

A type playing the role of assignable-type in the Assignable concept.

x, y

Objects of type X

Valid expressions

Name | Expression | Type | Semantics
Assignment | x = y | X & | Require operator=
Swap | swap(x, y) | void | Require swap() function

Models

  • int

See also

  • CopyConstructible

Copyright © 2001, 2002 Indiana University
Copyright © 2000, 2001 University of Notre Dame du Lac
Copyright © 2000 Jeremy Siek, Lie-Quan Lee, Andrew Lumsdaine
Copyright © 1996-1999 Silicon Graphics Computer Systems, Inc.
Copyright © 1994 Hewlett-Packard Company


Class bad_any_cast

boost::bad_any_cast — The exception thrown in the event of a failed any_cast of an any value.

Synopsis

class bad_any_cast : public std::bad_cast {
public:
  virtual const char * what() const;
};

Description

virtual const char * what() const;
Copyright © 2001 Kevlin Henney


Class bad_get

boost::bad_get — The exception thrown in the event of a failed application of boost::get on the given operand value.

Synopsis

class bad_get : public std::exception {
public:
  virtual const	char * what() const;
};

Description

virtual const	char * what() const;
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class bad_visit

boost::bad_visit — The exception thrown in the event of a visitor unable to handle the visited value.

Synopsis

class bad_visit : public std::exception {
public:
  virtual const char * what() const;
};

Description

virtual const char * what() const;
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Macro BOOST_VARIANT_ENUM_PARAMS

BOOST_VARIANT_ENUM_PARAMS — Enumerate parameters for use with variant.

Synopsis

BOOST_VARIANT_ENUM_PARAMS(param)

Description

Expands to a comma-separated sequence of length BOOST_VARIANT_LIMIT_TYPES , where each element in the sequence consists of the concatenation of param with its zero-based index into the sequence. That is, param ## 0, param ## 1, ..., param ## BOOST_VARIANT_LIMIT_TYPES - 1.

Rationale: For the user this macro greatly simplifies the process of declaring variant types in function templates or explicit partial specializations of class templates, as shown in the tutorial.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Macro BOOST_VARIANT_ENUM_SHIFTED_PARAMS

BOOST_VARIANT_ENUM_SHIFTED_PARAMS — Enumerate all but the first parameter for use with variant.

Synopsis

BOOST_VARIANT_ENUM_SHIFTED_PARAMS(param)

Description

Expands to a comma-separated sequence of length BOOST_VARIANT_LIMIT_TYPES - 1 , where each element in the sequence consists of the concatenation of param with its one-based index into the sequence. That is, param ## 1, ..., param ## BOOST_VARIANT_LIMIT_TYPES - 1.

Note: This macro results in the same expansion as BOOST_VARIANT_ENUM_PARAMS -- but without the first term.

Copyright © 2002, 2003 Eric Friedman,Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Macro BOOST_VARIANT_LIMIT_TYPES

BOOST_VARIANT_LIMIT_TYPES — Expands to the length of the template parameter list for variant.

Synopsis

BOOST_VARIANT_LIMIT_TYPES

Description

Note: Conforming implementations of variant must allow at least ten template arguments. That is, BOOST_VARIANT_LIMIT_TYPES must be greater or equal to 10.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Macro BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT

BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT — Indicates make_recursive_variant operates in an implementation-defined manner.

Synopsis

BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT

Description

Defined only if make_recursive_variant does not operate as documented on the target compiler, but rather in an implementation-defined manner.
Implementation Note: If BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT is defined for the target compiler, the current implementation uses
the MPL lambda mechanism to approximate the desired behavior. (In most cases, however, such compilers do not have full lambda support.)
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Macro BOOST_VARIANT_NO_REFERENCE_SUPPORT

BOOST_VARIANT_NO_REFERENCE_SUPPORT - Indicates variant does not support references as bounded types.

Synopsis

BOOST_VARIANT_NO_REFERENCE_SUPPORT

Description

Defined only if variant does not support references as bounded types.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Macro BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT

BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT — Indicates absence of support for specifying the bounded types of a variant by the elements of a type sequence.

Synopsis

BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT

Description

Defined only if make_variant_over and make_recursive_variant_over are not supported for some reason on the target compiler.
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Concept CopyConstructible

CopyConstructible

Description

Copy constructible types must be able to be constructed from another member of the type.

Notation

X

A type playing the role of copy-constructible-type in the CopyConstructible concept.

x, y

Objects of type X

Valid expressions

Name | Expression | Type | Semantics
Copy construction | X(x) | X | Require copy constructor.

Models

  • int

Copyright © 2001, 2002 Indiana University
Copyright © 2000, 2001 University of Notre Dame du Lac
Copyright © 2000 Jeremy Siek, Lie-Quan Lee, Andrew Lumsdaine
Copyright © 1996-1999 Silicon Graphics Computer Systems, Inc.
Copyright © 1994 Hewlett-Packard Company


Concept DefaultConstructible

DefaultConstructible

Description

DefaultConstructible objects only need to have a default constructor.

Notation

X

A type playing the role of default-constructible-type in the DefaultConstructible concept.

Valid expressions

Name | Expression | Type | Semantics
Construction | X() | X | Construct an instance of the type with default parameters.

Models

  • int

  • std::vector<double>

Copyright © 2001, 2002 Indiana University
Copyright © 2000, 2001 University of Notre Dame du Lac
Copyright © 2000 Jeremy Siek, Lie-Quan Lee, Andrew Lumsdaine
Copyright © 1996-1999 Silicon Graphics Computer Systems, Inc.
Copyright © 1994 Hewlett-Packard Company


Design Overview

"Never-Empty" Guarantee

The Guarantee
The Implementation Problem
The "Ideal" Solution: False Hopes
An Initial Solution: Double Storage
Current Approach: Temporary Heap Backup
Enabling Optimizations
Future Direction: Policy-based Implementation

The Guarantee

All instances v of type variant<T1,T2,...,TN> guarantee that v has constructed content of one of the types Ti, even if an operation on v has previously failed.

This implies that variant may be viewed precisely as a union of exactly its bounded types. This "never-empty" property insulates the user from the possibility of undefined variant content and the significant additional complexity-of-use attendant with such a possibility.

The Implementation Problem

While the never-empty guarantee might at first seem "obvious," it is in fact not even straightforward how to implement it in general (i.e., without unreasonably restrictive additional requirements on bounded types).

The central difficulty emerges in the details of variant assignment. Given two instances v1 and v2 of some concrete variant type, there are two distinct, fundamental cases to consider for the assignment v1 = v2.

First consider the case that v1 and v2 each contains a value of the same type. Call this type T. In this situation, assignment is perfectly straightforward: use T::operator=.

However, consider the case that v1 and v2 contain values of distinct types. Call these types T and U. At this point, since variant manages its content on the stack, the left-hand side of the assignment (i.e., v1) must destroy its content so as to permit in-place copy-construction of the content of the right-hand side (i.e., v2). In the end, whereas v1 began with content of type T, it ends with content of type U, namely a copy of the content of v2.

The crux of the problem, then, is this: in the event that copy-construction of the content of v2 fails, how can v1 maintain its "never-empty" guarantee? By the time copy-construction from v2 is attempted, v1 has already destroyed its content.

The "Ideal" Solution: False Hopes

The following schemes hope to solve the problem:

  1. Provide some "backup" storage, appropriately aligned, capable of holding values of the contained type of the left-hand side.

  2. Copy the memory (e.g., using memcpy) of the storage of the left-hand side to the backup storage.

  3. Attempt a copy of the right-hand side content to the (now-replicated) left-hand side storage.

  4. In the event of an exception from the copy, restore the backup (i.e., copy the memory from the backup storage back into the left-hand side storage).

  5. Otherwise, in the event of success, now copy the memory of the left-hand side storage to another "temporary" aligned storage.

  6. Now restore the backup (i.e., again copying the memory) to the left-hand side storage; with the "old" content now restored, invoke the destructor of the contained type on the storage of the left-hand side.

  7. Finally, copy the memory of the temporary storage to the (now-empty) storage of the left-hand side.

While complicated, it appears such a scheme could provide the desired safety in a relatively efficient manner. In fact, several early iterations of the library implemented this very approach.

Unfortunately, as Dave Abraham's first noted, the scheme results in undefined behavior:

"That's a lot of code to read through, but if it's doing what I think it's doing, it's undefined behavior.

"Is the trick to move the bits for an existing object into a buffer so we can tentatively construct a new object in that memory, and later move the old bits back temporarily to destroy the old object?

"The standard does not give license to do that: only one object may have a given address at a time. See 3.8, and particularly paragraph 4."

Additionally, as close examination quickly reveals, the scheme has the potential to create irreconcilable race-conditions in concurrent environments.

Ultimately, even if the above scheme could be made to work on certain platforms with particular compilers, it is still necessary to find a portable solution.

An Initial Solution: Double Storage

Upon learning of the infeasibility of the above scheme, Anthony Williams proposed in [Wil02] a scheme that served as the basis for a portable solution in some pre-release implementations of variant.

The essential idea to this scheme, which shall be referred to as the "double storage" scheme, is to provide enough space within a variant to hold two separate values of any of the bounded types.

With the secondary storage, a copy the right-hand side can be attempted without first destroying the content of the left-hand side; accordingly, the content of the left-hand side remains available in the event of an exception.

Thus, with this scheme, the variant implementation needs only to keep track of which storage contains the content -- and dispatch any visitation requests, queries, etc. accordingly.

The most obvious flaw to this approach is the space overhead incurred. Though some optimizations could be applied in special cases to eliminate the need for double storage -- for certain bounded types or in some cases entirely (see "Enabling Optimizations” for more details) -- many users on the Boost mailing list strongly objected to the use of double storage. In particular, it was noted that the overhead of double storage would be at play at all times -- even if assignment to variant never occurred. For this reason and others, a new approach was developed.

Current Approach: Temporary Heap Backup

Despite the many objections to the double storage solution, it was realized that no replacement would be without drawbacks. Thus, a compromise was desired.

To this end, Dave Abrahams suggested to include the following in the behavior specification for variant assignment: "variant assignment from one type to another may incur dynamic allocation." That is, while variant would continue to store its content in situ after construction and after assignment involving identical contained types, variant would store its content on the heap after assignment involving distinct contained types.

The algorithm for assignment would proceed as follows:

  1. Copy-construct the content of the right-hand side to the heap; call the pointer to this data p.

  2. Destroy the content of the left-hand side.

  3. Copy p to the left-hand side storage.

Since all operations on pointers are nothrow, this scheme would allow variant to meet its never-empty guarantee.

The most obvious concern with this approach is that while it certainly eliminates the space overhead of double storage, it introduces the overhead of dynamic-allocation to variant assignment -- not just in terms of the initial allocation but also as a result of the continued storage of the content on the heap. While the former problem is unavoidable, the latter problem may be avoided with the following "temporary heap backup" technique:

  1. Copy-construct the content of the left-hand side to the heap; call the pointer to this data backup.

  2. Destroy the content of the left-hand side.

  3. Copy-construct the content of the right-hand side in the (now-empty) storage of the left-hand side.

  4. In the event of failure, copy backup to the left-hand side storage.

  5. In the event of success, deallocate the data pointed to by backup.

With this technique: 1) only a single storage is used; 2) allocation is on the heap in the long-term only if the assignment fails; and 3) after any successful assignment, storage within the variant is guaranteed. For the purposes of the initial release of the library, these characteristics were deemed a satisfactory compromise solution.

There remain notable shortcomings, however. In particular, there may be some users for which heap allocation must be avoided at all costs; for other users, any allocation may need to occur via a user-supplied allocator. These issues will be addressed in the future (see “Future Direction: Policy-based Implementation”). For now, though, the library treats storage of its content as an implementation detail. Nonetheless, as described in the next section, there are certain things the user can do to ensure the greatest efficiency for variant instances (see “Enabling Optimizations” for details).

Enabling Optimizations

As described in “The Implementation Problem”, the central difficulty in implementing the never-empty guarantee is the possibility of failed copy-construction during variant assignment. Yet types with nothrow copy constructors clearly never face this possibility. Similarly, if one of the bounded types of the variant is nothrow default-constructible, then such a type could be used as a safe "fallback" type in the event of failed copy construction.

Accordingly, variant is designed to enable the following optimizations once the following criteria on its bounded types are met:

  • For each bounded type T that is nothrow copy-constructible (as indicated by boost::has_nothrow_copy), the library guarantees variant will use only single storage and in-place construction for T.

  • If any bounded type is nothrow default-constructible (as indicated by boost::has_nothrow_constructor), the library guarantees variant will use only single storage and in-place construction for every bounded type in the variant. Note, however, that in the event of assignment failure, an unspecified nothrow default-constructible bounded type will be default-constructed in the left-hand side operand so as to preserve the never-empty guarantee.

Caveat: On most platforms, the Type Traits templates has_nothrow_copy and has_nothrow_constructor by default return false for all class and struct types. It is necessary therefore to provide specializations of these templates as appropriate for user-defined types, as demonstrated in the following:

// ...in your code (at file scope)...

namespace boost {

  template <>
  struct has_nothrow_copy< myUDT >
    : mpl::true_
  {
  };

}

Implementation Note: So as to make the behavior of variant more predictable in the aftermath of an exception, the current implementation prefers to default-construct boost::blank if specified as a bounded type instead of other nothrow default-constructible bounded types. (If this is deemed to be a useful feature, it will become part of the specification for variant; otherwise, it may become obsolete. Please provide feedback to the Boost mailing list.)

Future Direction: Policy-based Implementation

As the previous sections have demonstrated, much effort has been expended in an attempt to provide a balance between performance, data size, and heap usage. Further, significant optimizations may be enabled in variant on the basis of certain traits of its bounded types.

However, there will be some users for whom the chosen compromise is unsatisfactory (e.g.: heap allocation must be avoided at all costs; if heap allocation is used, custom allocators must be used; etc.). For this reason, a future version of the library will support a policy-based implementation of variant. While this will not eliminate the problems described in the previous sections, it will allow the decisions regarding tradeoffs to be decided by the user rather than the library designers.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Concept EqualityComparable

EqualityComparable

Description

Equality Comparable types must have == and != operators.

Notation

X

A type playing the role of comparable-type in the EqualityComparable concept.

x, y

Objects of type X

Valid expressions

Name | Expression | Type
Equality test | x == y | Convertible to bool
Inequality test | x != y | Convertible to bool

Models

  • int

  • std::vector<int>

Copyright © 2001, 2002 Indiana University
Copyright © 2000, 2001 University of Notre Dame du Lac
Copyright © 2000 Jeremy Siek, Lie-Quan Lee, Andrew Lumsdaine
Copyright © 1996-1999 Silicon Graphics Computer Systems, Inc.
Copyright © 1994 Hewlett-Packard Company


Function get

boost::get — Retrieves a value of a specified type from a given variant.

Synopsis

template<typename U, typename T1, typename T2, ..., typename TN>
  U * get(variant<T1, T2, ..., TN> * operand);
template<typename U, typename T1, typename T2, ..., typename TN>
  const U * get(const variant<T1, T2, ..., TN> * operand);
template<typename U, typename T1, typename T2, ..., typename TN>
  U & get(variant<T1, T2, ..., TN> & operand);
template<typename U, typename T1, typename T2, ..., typename TN>
  const U & get(const variant<T1, T2, ..., TN> & operand);

Description

The get function allows run-time checked, type-safe retrieval of the content of the given variant . The function succeeds only if the content is of the specified type U, with failure indicated as described below.

Warning: After either operand or its content is destroyed (e.g., when the given variant is assigned a value of different type), the returned reference is invalidated. Thus, significant care and caution must be extended when handling the returned reference.

Notes: | As part of its guarantee of type-safety, get enforces const-correctness. Thus, the specified type U must be const-qualified whenever operand or its content is likewise const-qualified. The converse, however, is not required: that is, the specified type U may be const-qualified even when operand and its content are not.
Returns: | If passed a pointer, get returns a pointer to the value content if it is of the specified type U; otherwise, a null pointer is returned. If passed a reference, get returns a reference to the value content if it is of the specified type U; otherwise, an exception is thrown (see below).
Throws: | Overloads taking a variant pointer will not throw; the overloads taking a variant reference throw bad_get if the content is not of the specified type U.
Rationale: | While visitation via apply_visitor is generally preferred due to its greater safety, get may be more convenient in some cases due to its straightforward usage.
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Function template swap

boost::swap

Synopsis

template<typename T1, typename T2, ..., typename TN>
  void swap(variant<T1, T2, ..., TN> & lhs, variant<T1, T2, ..., TN> & rhs);

Description

Effects: | Swaps lhs with rhs by application of variant::swap .
Throws: | May fail with any exception arising from variant::swap .
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Function template operator<<

boost::operator<< — Provides streaming output for variant types.

Synopsis

template<typename ElemType, typename Traits, typename T1, typename T2, ...,
         typename TN>
  std::basic_ostream<ElemType,Traits> &
  operator<<(std::basic_ostream<ElemType,Traits> & out,
             const variant<T1, T2, ..., TN> & rhs);

Description

Requires: | Every bounded type of the variant must fulfill the requirements of the OutputStreamable concept.
Effects: | Calls out << x, where x is the content of rhs.
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template is_recursive_wrapper

boost::is_recursive_wrapper — Determines whether the specified type is a specialization of recursive_wrapper.

Synopsis

template<typename T>
class is_recursive_wrapper {
public:
  // types
  typedef unspecified type;

  // static constants
  static const bool value = unspecified;
};

Description

Value is true if T is a specialization of recursive_wrapper.

Note: is_recursive_wrapper is a model of MPL's IntegralConstant concept.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Concept LessThanComparable

LessThanComparable

Description

LessThanComparable types must have <, >, <=, and >= operators.

Notation

X

A type playing the role of comparable-type in the LessThanComparable concept.

x, y

Objects of type X

Valid expressions

Name | Expression | Type | Semantics
Less than | x < y | Convertible to bool | Determine if one value is less than another.
Less than or equal | x <= y | Convertible to bool | Determine if one value is less than or equal to another.
Greater than | x > y | Convertible to bool | Determine if one value is greater than another.
Greater than or equal to | x >= y | Convertible to bool | Determine if one value is greater than or equal to another.

Models

  • int

Copyright © 2001, 2002 Indiana University
Copyright © 2000, 2001 University of Notre Dame du Lac
Copyright © 2000 Jeremy Siek, Lie-Quan Lee, Andrew Lumsdaine
Copyright © 1996-1999 Silicon Graphics Computer Systems, Inc.
Copyright © 1994 Hewlett-Packard Company


Class template make_recursive_variant

boost::make_recursive_variant — Simplifies declaration of recursive variant types.

Synopsis

template<typename T1, typename T2 = unspecified, ...,
         typename TN = unspecified>
class make_recursive_variant {
public:
  // types
  typedef boost::variant< unspecified > type;
};

Description

type has behavior equivalent in every respect to some variant< U1, U2, ..., UN >, where each type Ui>i is the result of the corresponding type Ti undergone a transformation function. The following pseudo-code specifies the behavior of this transformation (call it substitute):

  • If Ti is boost::recursive_variant_ then: variant< U1, U2, ..., UN >;

  • Else if Ti is of the form X * then: substitute(X) *;

  • Else if Ti is of the form X & then: substitute(X) &;

  • Else if Ti is of the form R (*)( X1, X2, ..., XN ) then: substitute(R) (*)( substitute(X1), substitute(X2), ..., substitute(XN) );

  • Else if Ti is of the form F < X1, X2, ..., XN > then: F< substitute(X1), substitute(X2), ..., substitute(XN) >;

  • Else: T i

Note: cv-qualifiers are preserved and the actual process is generally a bit more complicated. However, the above does convey the essential idea as well as describe the extent of the substititions.

Use of make_recursive_variant is demonstrated in the section called “Recursive types with make_recursive_variant”.

Portability: Due to standard conformance issues in several compilers, make_recursive_variant is not universally supported. On these compilers the library indicates its lack of support via the definition of the preprocessor symbol BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT class="emphasis">.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template make_recursive_variant_over

boost::make_recursive_variant_over — Exposes a recursive variant whose bounded types are the elements of the given type sequence.

Synopsis

template<typename Sequence>
class make_recursive_variant_over {
public:
  // types
  typedef variant< unspecified > type;
};

Description

type has behavior equivalent in every respect to make_recursive_variant< Sequence[0], Sequence[1], ... >::type (where Sequence[i] denotes the i-th element of Sequence), except that no upper limit is imposed on the number of types.

Notes:

  • Sequence must meet the requirements of MPL's Sequence concept.

  • Due to standard conformance problems in several compilers, make_recursive_variant_over may not be supported on your compiler. See BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT for more information.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template make_variant_over

boost::make_variant_over — Exposes a variant whose bounded types are the elements of the given type sequence.

Synopsis

template<typename Sequence>
class make_variant_over {
public:
  // types
  typedef variant< unspecified > type;
};

Description

type has behavior equivalent in every respect to variant< Sequence[0], Sequence[1], ... > (where Sequence[i] denotes the i-th element of Sequence), except that no upper limit is imposed on the number of types.

Notes:

  • Sequence must meet the requirements of MPL's Sequence concept.

  • Due to standard conformance problems in several compilers, make_variant_over may not be supported on your compiler. See BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT for more information.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template recursive_wrapper

boost::recursive_wrapper — Solves circular dependencies, enabling recursive types.

Synopsis

template<typename T>
class recursive_wrapper {
public:
  // types
  typedef T type;

  // construct/copy/destruct
  recursive_wrapper();
  recursive_wrapper(const recursive_wrapper &);
  recursive_wrapper(const T &);
  ~recursive_wrapper();

  // modifiers
  void swap(recursive_wrapper &);
  recursive_wrapper & operator=(const recursive_wrapper &);
  recursive_wrapper & operator=(const T &);

  // queries
  T & get();
  const T & get() const;
  T * get_pointer();
  const T * get_pointer() const;
};

Description

The recursive_wrapper class template has an interface similar to a simple value container, but its content is allocated dynamically. This allows recursive_wrapper to hold types T whose member data leads to a circular dependency (e.g., a data member of T has a data member of type T).

The application of recursive_wrapper is easiest understood in context. See “Recursive types with recursive_wrapper” for a demonstration of a common use of the class template.

Notes:

  • Any type specified as the template argument to recursive_wrapper must be capable of construction via operator new. Thus, for instance, references are not supported.

recursive_wrapper construct/copy/destruct

  1. recursive_wrapper();

    Initializes *this by default construction of T.

    Requires:

    T must fulfill the requirements of the DefaultConstructible [20.1.4] concept.

    Throws:

    May fail with any exceptions arising from the default constructor of T or, in the event of insufficient memory, with std::bad_alloc.

  2. recursive_wrapper(const recursive_wrapper & other);

    Copies the content of other into *this.

    Throws:

    May fail with any exceptions arising from the copy constructor of T or, in the event of insufficient memory, with std::bad_alloc.

  3. recursive_wrapper(const T & operand);

    Copies operand into *this.

    Throws:

    May fail with any exceptions arising from the copy constructor of T or, in the event of insufficient memory, with std::bad_alloc.

  4. ~recursive_wrapper();

    Deletes the content of *this.

    Throws: Will not throw.

recursive_wrapper modifiers

  1. void swap(recursive_wrapper & other);

    Exchanges contents of *this and other.

    Throws:

    Will not throw.

  2. recursive_wrapper & operator=(const recursive_wrapper & rhs);

    Assigns the content of r hs to the content of *this.

    Requires:

    T must fulfill the requirements of the Assignable concept.

    Throws:

    May fail with any exceptions arising from the assignment operator of T.

  3. recursive_wrapper & operator=(const T & rhs);

    Assigns rhs into the content of *this.

    Requires: T must fulfill the requirements of the Assignable concept.
    Throws: May fail with any exceptions arising from the assignment operator of T.

recursive_wrapper queries

  1. T & get();
const T & get() const;

    Returns a reference to the content of *this.

    Throws: Will not throw.

  2. T * get_pointer();
const T * get_pointer() const;

    Returns a pointer to the content of *this.

    Throws:

    Will not throw.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Reference

Concepts
Header <boost/variant.hpp>
Header <boost/variant/variant_fwd.hpp>
Header <boost/variant/variant.hpp>
Header <boost/variant/recursive_variant.hpp>
Header <boost/variant/recursive_wrapper.hpp>
Header <boost/variant/apply_visitor.hpp>
Header <boost/variant/get.hpp>
Header <boost/variant/bad_visit.hpp>
Header <boost/variant/static_visitor.hpp>
Header <boost/variant/visitor_ptr.hpp>

Concepts

BoundedType
StaticVisitor
OutputStreamable

BoundedType

The requirements on a bounded type are as follows:

  • CopyConstructible [20.1.3].

  • Destructor upholds the no-throw exception-safety guarantee.

  • Complete at the point of variant template instantiation. (See boost::recursive_wrapper <T> for a type wrapper that accepts incomplete types to enable recursive variant types.)

Every type specified as a template argument to variant must at minimum fulfill the above requirements. In addition, certain features of variant are available only if its bounded types meet the requirements of these following additional concepts:

  • Assignable : variant is itself Assignable if and only if every one of its bounded types meets the requirements of the concept. (Note that top-level const-qualified types and reference types do not meet these requirements.)

  • DefaultConstructible [20.1.4]: variant is itself DefaultConstructible if and only if its first bounded type (i.e., T1) meets the requirements of the concept.

  • EqualityComparable : variant is itself EqualityComparable if and only if every one of its bounded types meets the requirements of the concept.

  • LessThanComparable : variant is itself LessThanComparable if and only if every one of its bounded types meets the requirements of the concept.

  • OutputStreamable : variant is itself OutputStreamable if and only if every one of its bounded types meets the requirements of the concept.

StaticVisitor

Examples

The requirements on a static visitor of a type T are as follows:

  • Must allow invocation as a function by overloading operator(), unambiguously accepting any value of type T.

  • Must expose inner type result_type. (See boost::visitor_ptr for a solution to using functions as visitors.)

  • If result_type is not void, then each operation of the function object must return a value implicitly convertible to result_type.

Examples

The following class satisfies the requirements of a static visitor of several types (i.e., explicitly: int and std::string; or, e.g., implicitly: short and const char *; etc.):

class my_visitor
    : public boost::static_visitor<int>
{
public:

    int operator()(int i)
    {
        return i * 2;
    }

    int operator()(const std::string& s)
    {
        return s.length();
    }

};

Another example is the following class, whose function-call operator is a member template, allowing it to operate on values of many types. Thus, the following class is a visitor of any type that supports streaming output (e.g., int, double, std::string, etc.):

class printer
    : public boost::static_visitor<>
{
    template <typename T>
    void operator()(const T& t)
    {
        std::cout << t << std::endl;
    }
};

OutputStreamable

The requirements on an output streamable type T are as follows:

  • For any object t of type T, std::cout << t must be a valid expression.

Header <boost/variant.hpp>

This header exists simply as a convenience to the user, including all of the headers in the boost/variant directory.

Header <boost/variant/variant_fwd.hpp>

Provides forward declarations of the boost::variant , boost::make_variant_over , boost::make_recursive_variant , and boost::make_recursive_variant_over class templates and the boost::recursive_variant_ tag type. Also defines several preprocessor symbols, as described below.

BOOST_VARIANT_LIMIT_TYPES
BOOST_VARIANT_ENUM_PARAMS(param)
BOOST_VARIANT_ENUM_SHIFTED_PARAMS(param)
BOOST_VARIANT_NO_REFERENCE_SUPPORT
BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT
BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT

Header <boost/variant/variant.hpp>

namespace boost {
  template<typename T1, typename T2 = unspecified, ...,
           typename TN = unspecified>
    class variant;

  template<typename Sequence> class make_variant_over;
  template<typename T1, typename T2, ..., typename TN>
    void swap(variant<T1, T2, ..., TN> &, variant<T1, T2, ..., TN> &);
  template<typename ElemType, typename Traits, typename T1, typename T2, ...,
           typename TN>
    std::basic_ostream<ElemType,Traits> &
    operator<<(std::basic_ostream<ElemType,Traits> &,
               const variant<T1, T2, ..., TN> &);
}

Header <boost/variant/recursive_variant.hpp>

namespace boost {
  typedef unspecified recursive_variant_;

  template<typename T1, typename T2 = unspecified, ...,
           typename TN = unspecified>
    class make_recursive_variant;
  template<typename Sequence> class make_recursive_variant_over;
}

Header <boost/variant/recursive_wrapper.hpp>

namespace boost {
  template<typename T> class recursive_wrapper;
  template<typename T> class is_recursive_wrapper;
  template<typename T> class unwrap_recursive_wrapper;
}

Header <boost/variant/apply_visitor.hpp>

namespace boost {
  template<typename Visitor> class apply_visitor_delayed_t;
  template<typename Visitor, typename Variant>
    typename Visitor::result_type apply_visitor(Visitor &, Variant &);
  template<typename Visitor, typename Variant>
    typename Visitor::result_type apply_visitor(const Visitor &, Variant &);
  template<typename BinaryVisitor, typename Variant1, typename Variant2>
    typename BinaryVisitor::result_type
    apply_visitor(BinaryVisitor &, Variant1 &, Variant2 &);
  template<typename BinaryVisitor, typename Variant1, typename Variant2>
    typename BinaryVisitor::result_type
    apply_visitor(const BinaryVisitor &, Variant1 &, Variant2 &);
  template<typename Visitor>
    apply_visitor_delayed_t<Visitor> apply_visitor(Visitor &);
}

Header <boost/variant/get.hpp>

namespace boost {
  class bad_get;
  template<typename U, typename T1, typename T2, ..., typename TN>
    U * get(variant<T1, T2, ..., TN> *);
  template<typename U, typename T1, typename T2, ..., typename TN>
    const U * get(const variant<T1, T2, ..., TN> *);
  template<typename U, typename T1, typename T2, ..., typename TN>
    U & get(variant<T1, T2, ..., TN> &);
  template<typename U, typename T1, typename T2, ..., typename TN>
    const U & get(const variant<T1, T2, ..., TN> &);
}

Header <boost/variant/bad_visit.hpp>

namespace boost {
  class bad_visit;
}

Header <boost/variant/static_visitor.hpp>

namespace boost {
  template<typename ResultType> class static_visitor;
}

Header <boost/variant/visitor_ptr.hpp>

namespace boost {
  template<typename T, typename R> class visitor_ptr_t;
  template<typename R, typename T> visitor_ptr_t<T,R> visitor_ptr(R (*)(T));
}
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


References

[Abr00] David Abrahams. "Exception-Safety in Generic Components." M. Jazayeri, R. Loos, D. Musser (eds.): Generic Programming '98, Proc. of a Dagstuhl Seminar, Lecture Notes on Computer Science, Vol. 1766, pp. 69-79. Springer-Verlag Berlin Heidelberg. 2000.

[Abr01] David Abrahams. "Error and Exception Handling." Boost technical article. 2001-2003.

[Ale01a] Andrei Alexandrescu. "An Implementation of Discriminated Unions in C++." OOPSLA 2001, Second Workshop on C++ Template Programming. Tampa Bay, 14 October 2001.

[Ale01b] Andrei Alexandrescu. Modern C++ Design. Addison-Wesley, C++ In-Depth series. 2001.

[Ale02] Andrei Alexandrescu. "Generic<Programming>: Discriminated Unions" series: Part 1, Part 2, Part 3. C/C++ Users Journal. 2002.

[Boo02] Various Boost members. "Proposal --- A type-safe union." Boost public discussion. 2002.

[C++98] International Standard, Programming Languages – C++. ISO/IEC:14882. 1998.

[GoF95] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. 1995.

[Gre02] Douglas Gregor. "BOOST_USER: variant." Boost Wiki paper. 2002.

[Gur02] Aleksey Gurtovoy. Boost Metaprogramming Library. 2002.

[Hen01] Kevlin Henney. Boost Any Library. 2001.

[MK02] Paul Mensonides and Vesa Karvonen. Boost Preprocessor Library. 2002.

Type Traits Steve Cleary, Beman Dawes, Aleksey Gurtovoy, Howard Hinnant, Jesse Jones, Mat Marcus, John Maddock, Jeremy Siek. Boost Type Traits Library. 2001.

[Sut00] Herb Sutter. Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions. Addison-Wesley, C++ In-Depth series. 2000.

[Wil02] Anthony Williams. Double-Storage Proposal. 2002.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template static_visitor

boost::static_visitor — Convenient base type for static visitors.

Synopsis

template<typename ResultType>
class static_visitor {
public:
  // types
  typedef ResultType result_type;  // Exposes result_type member as required by StaticVisitor concept.
};

Description

Denotes the intent of the deriving class as meeting the requirements of a static visitor of some type. Also exposes the inner type result_type as required by the StaticVisitor concept.

Notes: static_visitor is intended for use as a base type only and is therefore non-instantiable.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Tutorial

Basic Usage
Advanced Topics

Basic Usage

A discriminated union container on some set of types is defined by instantiating the boost::variant class template with the desired types. These types are called bounded types and are subject to the requirements of the BoundedType concept. Any number of bounded types may be specified, up to some implementation-defined limit (see BOOST_VARIANT_LIMIT_TYPES).

For example, the following declares a discriminated union container on int and std::string:

boost::variant< int, std::string > v;

By default, a variant default-constructs its first bounded type, so v initially contains int(0). If this is not desired, or if the first bounded type is not default-constructible, a variant can be constructed directly from any value convertible to one of its bounded types. Similarly, a variant can be assigned any value convertible to one of its bounded types, as demonstrated in the following:

v = "hello";

Now v contains a std::string equal to "hello". To demonstrate this, stream v

std::cout << v << std::endl;

To do more with the content of a variant than streaming, the user needs some way to access the contained value. There are two ways to accomplish this: apply_visitor , which is safest and very powerful, and get<T>, which is sometimes more convenient to use.

For instance, to concatenate to the string contained in v. With value retrieval by get , this may be accomplished quite simply, as seen in the following:

std::string& str = boost::get<std::string>(v);
str += " world! ";

As desired, the std::string contained by v now is equal to "hello world! ". Again, to demonstrate this stream v to standard output:

std::cout << v << std::endl;

While use of get is perfectly acceptable in this trivial example, get generally suffers from several significant shortcomings. For instance, to write a function accepting a variant<int, std::string>,not knowing whether the passed variant contained an inn or a std::string. If insisted upon continued use of get, the user needs to query the variant for its contained type. The following function, which "doubles" the content of the given variant, demonstrates this approach:

void  times_two( boost::variant< int, std::string > & operand )
{
    if ( int* pi = boost::get<int>( &v ) )
        *pi *= 2;
    else if ( std::string* pstr = boost::get<std::string>( &v ) )
        *pstr += *pstr;}}

However, such code is quite brittle, and without careful attention will likely lead to the introduction of subtle logical errors detectable only at runtime. For instance, to extend times_two to operate on a variant with additional bounded types. Specifically, let's add std::complex<double> to the set. Clearly, there is a need to at least change the function declaration:

void times_two( boost::variant< int, std::string, std::complex<double> > & operand )
{
    // as above...?
}

Of course, additional changes are required, for currently if the passed variant in fact contained a std::complex value, times_two would silently return -- without any of the desired side-effects and without any error. In this case, the fix is obvious. But in more complicated programs, it could take considerable time to identify and locate the error in the first place.

Thus, real-world use of variant typically demands an access mechanism more robust than get. For this reason, variant supports compile-time checked visitation via apply_visitor . Visitation requires that the programmer explicitly handle (or ignore) each bounded type. Failure to do so results in a compile-time error.

Visitation of a variant requires a visitor object. The following demonstrates one such implementation of a visitor implementating behavior identical to times_two:

class times_two_visitor
    : public boost::static_visitor<>
{
public:

    void operator()(int & i) const
    {
        i *= 2;
    }

    void operator()(std::string & str) const
    {
        str += str;
    }

};

With the implementation of the above visitor, then apply it to v, as seen in the following:

boost::apply_visitor( times_two_visitor(), v );

As expected, the content of v is now a std::string equal to "hello world! hello world! ". (Skip the verification this time.)

In addition to enhanced robustness, visitation provides another important advantage over get: the ability to write generic visitors. For instance, the following visitor will "double" the content of any variant (provided its bounded types each support operator+=):

class times_two_generic
    : public boost::static_visitor<>
{
public:

    template <typename T>
    void operator()( T & operand ) const
    {
        operand += operand;
    }

};

Again, apply_visitor sets the wheels in motion:

boost::apply_visitor( times_two_generic(), v );

While the initial setup costs of visitation may exceed that required for get, the benefits quickly become significant. Before concluding this section, explore one last benefit of visitation with apply_visitor: delayed visitation. Namely, a special form of apply_visitor is available that does not immediately apply the given visitor to any variant but rather returns a function object that operates on any variant given to it. This behavior is particularly useful when operating on sequences of variant type, as the following demonstrates:

std::vector< boost::variant<int, std::string> > vec;
vec.push_back( 21 );
vec.push_back( "hello " );

times_two_generic visitor;
std::for_each(
      vec.begin(), vec.end()
   , boost::apply_visitor(visitor)
   );

Advanced Topics

Preprocessor macros
Using a type sequence to specify bounded types
Recursive variant types
Binary visitation

This section discusses several features of the library often required for advanced uses of variant. Unlike in the above section, each feature presented below is largely independent of the others. Accordingly, this section is not necessarily intended to be read linearly or in its entirety.

Preprocessor macros

While the variant class template's variadic parameter list greatly simplifies use for specific instantiations of the template, it significantly complicates use for generic instantiations. For instance, while it is immediately clear how one might write a function accepting a specific variant instantiation, say variant<int, std::string>, it is less clear how one might write a function accepting any given variant.

Due to the lack of support for true variadic template parameter lists in the C++98 standard, the preprocessor is needed. While the Preprocessor library provides a general and powerful solution, the need to repeat BOOST_VARIANT_LIMIT_TYPES unnecessarily clutters otherwise simple code. Therefore, for common use-cases, this library provides its own macro BOOST_VARIANT_ENUM_PARAMS .

This macro simplifies for the user the process of declaring variant types in function templates or explicit partial specializations of class templates, as shown in the following:

// general cases
template <typename T> void some_func(const T &);
template <typename T> class some_class;

// function template overload
template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
void some_func(const boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> &);

// explicit partial specialization
template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
class some_class< boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >;

Using a type sequence to specify bounded types

While convenient for typical uses, the variant class template's variadic template parameter list is limiting in two significant dimensions. First, due to the lack of support for true variadic template parameter lists in C++, the number of parameters must be limited to some implementation-defined maximum (namely, BOOST_VARIANT_LIMIT_TYPES ). Second, the nature of parameter lists in general makes compile-time manipulation of the lists excessively difficult.

To solve these problems, make_variant_over< Sequence > exposes a variant whose bounded types are the elements of Sequence (where Sequence is any type fulfilling the requirements of MPL's Sequence concept). For instance,

typedef mpl::vector< std::string > types_initial;
typedef mpl::push_front< types_initial, int >::type types;

boost::make_variant_over< types >::type v1;

behaves equivalently to

boost::variant< int, std::string > v2;

Portability: Unfortunately, due to standard conformance issues in several compilers, make_variant_over is not universally available. On these compilers the library indicates its lack of support for the syntax via the definition of the preprocessor symbol BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT .

Recursive variant types

Recursive types with recursive_wrapper
Recursive types with make_recursive_variant

Recursive types facilitate the construction of complex semantics from simple syntax. For instance, nearly every programmer is familiar with the canonical definition of a linked list implementation, whose simple definition allows sequences of unlimited length:

template <typename T>
struct list_node
{
    T data;
    list_node * next;
};

The nature of variant as a generic class template unfortunately precludes the straightforward construction of recursive variant types. Consider the following attempt to construct a structure for simple mathematical expressions:

struct add;
struct sub;
template <typename OpTag> struct binary_op;

typedef boost::variant<
      int
    , binary_op<add>
    , binary_op<sub>
    > expression;

template <typename OpTag>
struct binary_op
{
    expression left;  // variant instantiated here...
    expression right;

    binary_op( const expression & lhs, const expression & rhs )
        : left(lhs), right(rhs)
    {
    }

}; // ...but binary_op not complete until here!

While well-intentioned, the above approach will not compile because binary_op is still incomplete when the variant type expression is instantiated. Further, the approach suffers from a more significant logical flaw: even if C++ syntax were different such that the above example could be made to "work," expression would need to be of infinite size, which is clearly impossible.

To overcome these difficulties, variant includes special support for the boost::recursive_wrapper class template, which breaks the circular dependency at the heart of these problems. Further, boost::make_recursive_variant provides a more convenient syntax for declaring recursive variant types. Tutorials for use of these facilities is described in “Recursive types with recursive_wrapper” and “Recursive types with make_recursive_variant”.

Recursive types with recursive_wrapper

The following example demonstrates how recursive_wrapper could be used to solve the problem presented in “Recursive variant types”:

typedef boost::variant<
      int
    , boost::recursive_wrapper< binary_op<add> >
    , boost::recursive_wrapper< binary_op<sub> >
    > expression;

Because variant provides special support for recursive_wrapper, clients may treat the resultant variant as though the wrapper were not present. This is seen in the implementation of the following visitor, which calculates the value of an expression without any reference to recursive_wrapper:

class calculator : public boost::static_visitor<int>
{
public:

    int operator()(int value) const
    {
        return value;
    }

    int operator()(const binary_op<add> & binary) const
    {
        return boost::apply_visitor( calculator(), binary.left )
             + boost::apply_visitor( calculator(), binary.right );
    }

    int operator()(const binary_op<sub> & binary) const
    {
        return boost::apply_visitor( calculator(), binary.left )
             - boost::apply_visitor( calculator(), binary.right );
    }

};

Finally, to demonstrate expression in action:

void f()
{
    // result = ((7-3)+8) = 12
    expression result(
        binary_op<add>(
            binary_op<sub>(7,3)
          , 8
          )
      );

    assert( boost::apply_visitor(calculator(),result) == 12 );
}

Recursive types with make_recursive_variant

For some applications of recursive variant types, a user may be able to sacrifice the full flexibility of using recursive_wrapper with variant for the following convenient syntax:

typedef boost::make_recursive_variant<
      int
    , std::vector< boost::recursive_variant_ >
    >::type int_tree_t;

Use of the resultant variant type is as expected:

std::vector< int_tree_t > subresult;
subresult.push_back(3);
subresult.push_back(5);

std::vector< int_tree_t > result;
result.push_back(1);
result.push_back(subresult);
result.push_back(7);

int_tree_t var(result);

To be clear, one might represent the resultant content of var as ( 1 ( 3 5 ) 7 ).

Finally, note that a type sequence can be used to specify the bounded types of a recursive variant via the use of boost::make_recursive_variant_over, whose semantics are the same as make_variant_over (which is described in “Using a type sequence to specify bounded types”).

Portability: Unfortunately, due to standard conformance issues in several compilers, make_recursive_variant is not universally supported. On these compilers the library indicates its lack of support via the definition of the preprocessor symbol BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT . Thus, unless working with highly-conformant compilers, maximum portability will be achieved by instead using recursive_wrapper, as described in “Recursive types with recursive_wrapper”.

Binary visitation

As the tutorial above demonstrates, visitation is a powerful mechanism for manipulating variant content. Binary visitation further extends the power and flexibility of visitation by allowing simultaneous visitation of the content of two different variant objects.

Notably this feature requires that binary visitors are incompatible with the visitor objects discussed in the tutorial above, as they must operate on two arguments. The following demonstrates the implementation of a binary visitor:

class are_strict_equals
    : public boost::static_visitor<bool>
{
public:

    template <typename T, typename U>
    bool operator()( const T &, const U & ) const
    {
        return false; // cannot compare different types
    }

    template <typename T>
    bool operator()( const T & lhs, const T & rhs ) const
    {
        return lhs == rhs;
    }

};

As expected, the visitor is applied to two variant arguments by means of apply_visitor:
boost::variant< int, std::string > v1( "hello" );

boost::variant< double, std::string > v2( "hello" );
assert( boost::apply_visitor(are_strict_equals(), v1, v2) );

boost::variant< int, const char * > v3( "hello" );
assert( !boost::apply_visitor(are_strict_equals(), v1, v3) );

Finally, we must note that the function object returned from the "delayed" form of apply_visitor also supports binary visitation, as the following demonstrates:

typedef boost::variant<double, std::string> my_variant;

std::vector< my_variant > seq1;
seq1.push_back("pi is close to ");
seq1.push_back(3.14);

std::list< my_variant > seq2;
seq2.push_back("pi is close to ");
seq2.push_back(3.14);

are_strict_equals visitor;
assert( std::equal(
      v1.begin(), v1.end(), v2.begin()
    , boost::apply_visitor( visitor )
    ) );

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template unwrap_recursive_wrapper

boost::unwrap_recursive_wrapper — Unwraps the specified argument if given a specialization of recursive_wrapper.

Synopsis

template<typename T>
class unwrap_recursive_wrapper {
public:
  // types
  typedef unspecified type;
};

Description

type is equivalent to T::type if T is a specialization of recursive_wrapper . Otherwise, type is equivalent to T.

Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template variant

boost::variant — Safe, generic, stack-based discriminated union container.

Synopsis

template<typename T1, typename T2 = unspecified, ...,
         typename TN = unspecified>
class variant {
public:
  // types
  typedef unspecified types;

  // construct/copy/destruct
  variant();
  variant(const variant &);
  template<typename T> variant(T &);
  template<typename T> variant(const T &);
  template<typename U1, typename U2, ..., typename UN>
    variant(variant<U1, U2, ..., UN> &);
  template<typename U1, typename U2, ..., typename UN>
    variant(const variant<U1, U2, ..., UN> &);
  ~variant();

  // modifiers
  void swap(variant &);
  variant & operator=(const variant &);
  template<typename T> variant & operator=(const T &);

  // queries
  int which() const;
  bool empty() const;
  const std::type_info & type() const;

  // relational
  bool operator==(const variant &) const;
  template<typename U> void operator==(const U &) const;
  bool operator<(const variant &) const;
  template<typename U> void operator<(const U &) const;
};

Description

The variant class template (inspired by Andrei Alexandrescu's class of the same name [Ale01A]) is an efficient, recursive-capable, bounded discriminated union value type capable of containing any value type (either POD or non-POD). It supports construction from any type convertible to one of its bounded types or from a source variant whose bounded types are each convertible to one of the destination variant's bounded types. As well, through apply_visitor , variant supports compile-time checked, type-safe visitation; and through get, variant supports run-time checked, type-safe value retrieval.

Notes:

  • The bounded types of the variant are exposed via the nested typedef types, which is an MPL-compatible Sequence containing the set of types that must be handled by any visitor to the variant.

  • All members of variant satisfy at least the basic guarantee of exception-safety. That is, all operations on a variant remain defined even after previous operations have failed.

  • Each type specified as a template argument to variant must meet the requirements of the BoundedType concept.

  • Each type specified as a template argument to variant must be distinct after removal of qualifiers. Thus, for instance, both variant<int, int> and variant<int, const int> have undefined behavior.

  • Conforming implementations of variant must allow at least ten types as template arguments. The exact number of allowed arguments is exposed by the preprocessor macro BOOST_VARIANT_LIMIT_TYPES . (See make_variant_over for a means to specify the bounded types of a variant by the elements of an MPL or compatible Sequence, thus overcoming this limitation.)

variant construct/copy/destruct

  1. variant();
    Requires: The first bounded type of the variant (i.e., T1) must fulfill the requirements of the DefaultConstructible [20.1.4] concept.
    Postconditions: Content of *this is the default value of the first bounded type (i.e, T1).
    Throws: May fail with any exceptions arising from the default constructor of T1.
  2. variant(const variant & other);
    Postconditions: Content of *this is a copy of the content of other.
    Throws: May fail with any exceptions arising from the copy constructor of other's contained type.
  3. template<typename T> variant(T & operand);
    Requires: T must be unambiguously convertible to one of the bounded types (i.e., T1, T2, etc.).
    Postconditions: Content of *this is the best conversion of operand to one of the bounded types, as determined by standard overload resolution rules.
    Throws: May fail with any exceptions arising from the conversion of operand to one of the bounded types.
  4. template<typename T> variant(const T & operand);
    Notes: Same semantics as previous constructor, but allows construction from temporaries.
  5. template<typename U1, typename U2, ..., typename UN>
  variant(variant<U1, U2, ..., UN> & operand);
    Requires: Every one of U1, U2, ..., UN must have an unambiguous conversion to one of the bounded types (i.e., T1, T2, ..., TN).
    Postconditions: If variant<U1, U2, ..., UN> is itself one of the bounded types, then content of *this is a copy of operand. Otherwise, content of *this is the best conversion of the content of operand to one of the bounded types, as determined by standard overload resolution rules.
    Throws: If variant<U1, U2, ..., UN> is itself one of the bounded types, then may fail with any exceptions arising from the copy constructor of variant<U1, U2, ..., UN>. Otherwise, may fail with any exceptions arising from the conversion of the content of operand to one of the bounded types.
  6. template<typename U1, typename U2, ..., typename UN>
  variant(const variant<U1, U2, ..., UN> & operand);
    Notes: Same semantics as previous constructor, but allows construction from temporaries.
  7. ~variant();
    Effects: Destroys the content of *this.
    Throws: Will not throw.

variant modifiers

  1. void swap(variant & other);
    Requires: Every bounded type must fulfill the requirements of the Assignable concept.
    Effects: Interchanges the content of *this and other.
    Throws: If the contained type of other is the same as the contained type of *this, then may fail with any exceptions arising from the swap of the contents of *this and other. Otherwise, may fail with any exceptions arising from either of the copy constructors of the contained types. Also, in the event of insufficient memory, may fail with std::bad_alloc (why?).
  2. variant & operator=(const variant & rhs);
    Requires: Every bounded type must fulfill the requirements of the Assignable concept.
    Effects: If the contained type of rhs is the same as the contained type of *this, then assigns the content of rhs into the content of *this. Otherwise, makes the content of *this a copy of the content of rhs, destroying the previous content of *this.
    Throws: If the contained type of rhs is the same as the contained type of *this, then may fail with any exceptions arising from the assignment of the content of rhs into the content *this. Otherwise, may fail with any exceptions arising from the copy constructor of the contained type of rhs. Also, in the event of insufficient memory, may fail with std::bad_alloc (why?).
  3. template<typename T> variant & operator=(const T & rhs);
    Requires:
    • T must be unambiguously convertible to one of the bounded types (i.e., T1, T2, etc.).
    • Every bounded type must fulfill the requirements of the Assignable concept.
    Effects: If the contained type of *this is T, then assigns rhs into the content of *this. Otherwise, makes the content of *this the best conversion of rhs to one of the bounded types, as determined by standard overload resolution rules, destroying the previous content of *this.
    Throws: If the contained type of *this is T, then may fail with any exceptions arising from the assignment of rhs into the content *this. Otherwise, may fail with any exceptions arising from the conversion of rhs to one of the bounded types. Also, in the event of insufficient memory, may fail with std::bad_alloc (why?).

variant queries

  1. int which() const;
    Returns: The zero-based index into the set of bounded types of the contained type of *this. (For instance, if called on a variant<int, std::string> object containing a std::string, which() would return 1.)
    Throws: Will not throw.
  2. bool empty() const;
    Returns: false: variant always contains exactly one of its bounded types. (See "Never-Empty" Guarantee” for more information.)
    Rationale: Facilitates generic compatibility with boost::any .
    Throws: Will not throw.
  3. const std::type_info & type() const;
    Returns: typeid(x), where x is the the content of *this.
    Throws: Will not throw.

variant relational

  1. bool operator==(const variant & rhs) const;
template<typename U> void operator==(const U &) const;
    Notes: The overload returning void exists only to prohibit implicit conversion of the operator's right-hand side to variant; thus, its use will (purposefully) result in a compile-time error.
    Requires: Every bounded type of the variant must fulfill the requirements of the EqualityComparable concept.
    Returns: true if which() == rhs.which() and content_this == content_rhs, where content_this is the content of *this and content_rhs is the content of rhs.
    Throws: If which() == rhs.which() then may fail with any exceptions arising from operator==(T,T), where T is the contained type of *this.

  2. bool operator<(const variant & rhs) const;
template<typename U> void operator<(const U &) const;
    Notes:

    The overload returning void exists only to prohibit implicit conversion of the operator's right-hand side to variant; thus, its use will (purposefully) result in a compile-time error.

    Requires: Every bounded type of the variant must fulfill the requirements of the LessThanComparable concept.
    Returns: If which() == rhs.which() then: content_this < content_rhs, where content_this is the content of *this and content_rhs is the content of rhs. Otherwise: which() < rhs.which().
    Throws: If which() == rhs.which() then may fail with any exceptions arising from operator<(T,T), where T is the contained type of *this.
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template variant

Name

variant: This is a safe, generic, stack-based discriminated union container.

Synopsis

template<typename T1, typename T2 = unspecified, ...,
         typename TN = unspecified>
class variant {
public:
  // types
  typedef unspecified types;

  // construct/copy/destruct
  variant();
  variant(const variant &);
  template<typename T> variant(T &);
  template<typename T> variant(const T &);
  template<typename U1, typename U2, ..., typename UN>
    variant(variant<U1, U2, ..., UN> &);
  template<typename U1, typename U2, ..., typename UN>
    variant(const variant<U1, U2, ..., UN> &);
  ~variant();

  // modifiers
  void swap(variant &);
  variant & operator=(const variant &);
  template<typename T> variant & operator=(const T &);

  // queries
  int which() const;
  bool empty() const;
  const std::type_info & type() const;

  // relational
  bool operator==(const variant &) const;
  template<typename U> void operator==(const U &) const;
  bool operator<(const variant &) const;
  template<typename U> void operator<(const U &) const;
};

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Function template visitor_ptr

boost::visitor_ptr — Returns a visitor object that adapts function pointers for use as a static visitor.

Synopsis

template<typename R, typename T> visitor_ptr_t<T,R> visitor_ptr(R (*)(T));

Description

Constructs and returns a visitor_ptr_t adaptor over the given function.

Returns: | Returns a visitor_ptr_t visitor object that, when applied, invokes the given function.
Throws: | Will not throw. (Note: however, that the returned visitor object may throw when applied.)
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Class template visitor_ptr_t

boost::visitor_ptr_t — Adapts a function pointer for use as a static visitor.

Synopsis

template<typename T, typename R>
class visitor_ptr_t : public static_visitor<R> {
public:
  // construct/copy/destruct
  explicit visitor_ptr_t(R (*)(T));

  // static visitor interfaces
  R operator()(unspecified-forwarding-type);
  template<typename U> void operator()(const U&);
};

Description

Adapts the function given at construction for use as a static visitor of type T with result type R.

visitor_ptr_t construct/copy/destruct

  1. explicit visitor_ptr_t(R (*)(T));
    Effects: Constructs the visitor with the given function.

visitor_ptr_t static visitor interfaces

  1. R operator()(unspecified-forwarding-type operand);
template<typename U> void operator()(const U&);
    Effects: If passed a value or reference of type T, it invokes the function given at construction, appropriately forwarding operand.
    Returns: Returns the result of the function invocation.
    Throws: The overload taking a value or reference of type T throws if the invoked function throws. The overload taking all other values always throws bad_visit .
Copyright © 2002, 2003 Eric Friedman, Itay Maman
Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Boost Configuration Reference

Configuring Boost for Your Platform
Using the default boost configuration
 The <boost/config.hpp> header
Using the configure script
User settable options
Advanced configuration usage
Testing the boost configuration
Boost Macro Reference
Macros that describe defects
Macros that describe optional features
Boost Helper Macros
Boost Informational Macros
Macros for libraries with separate source code
Guidelines for Boost Authors
Adding New Defect Macros
Adding New Feature Test Macros
Modifying the Boost Configuration Headers
Rationale

Configuring Boost for Your Platform

Using the default boost configuration

Boost comes configured for most common compilers and platforms; hence can be used "as is".Since the compiler is configured separately from the standard library, the default configuration should work even if the compiler's standard library is replaced with a third-party standard library (like STLport).

Using boost "as is" without trying to reconfigure is the recommended method for using boost. However, run the configure script if required/desired, and the regression tests provided allow to test the current boost configuration with the particular compiler setup.

Boost library users can request support for additional compilers or platforms by visiting Tracker and submitting a support request.

The <boost/config.hpp> header

Boost library implementations access configuration macros via #include <boost/config.hpp>.

While Boost library users are not required to include that file directly, or use those configuration macros, such use is acceptable. The configuration macros are documented as to their purpose, usage, and limitations which makes them usable by both Boost library and user code.

Boost informational or helper macros are designed for use by Boost users as well as for own internal use. Note however, that the feature test and defect test macros were designed for internal use by Boost libraries, not user code, so they can change at any time (though no gratuitous changes are made to them). Boost library problems resulting from changes to the configuration macros are caught by the Boost regression tests, so the Boost libraries are updated to account for those changes. By contrast, Boost library user code can be adversely affected by changes to the macros without warning. The best way to keep abreast of changes to the macros used in user code is to monitor the discussions on the Boost developers list.

Using the configure script

Note: This configure script only sets up the Boost headers for use with a particular compiler. It has no effect on Boost.Build, or how the libraries are built.

To try and improve things, run the boost config script if boost is incorrectly configured for a particular setup and when on a UNIX like platformIf. From a shell command prompt cd into <boost-root>/libs/config/ and type:

sh ./configure

A list of the items being checked as the script works its way through the regression tests is displayed. Note that the configure script only really auto-detects thecompiler if it is called g++, c++ or CC. If some other compiler is used, set one or more of the following environment variables:

Variable | Description
CXX | The name of the compiler, for example "c++".
CXXFLAGS | The compiler flags to use, for example "-O2".
LDFLAGS | The linker flags to use, for example "-L/mypath".
LIBS | Any libraries to link in, for example -lpthread.

For example to run the configure script with HP aCC, use something like:

export CXX="aCC"
export CXXFLAGS="-Aa -DAportable -D__HPACC_THREAD_SAFE_RB_TREE -DRWSTD_MULTI_THREAD -DRW_MULTI_THREAD -D_REENTRANT -D_THREAD_SAFE"
export LDFLAGS="-DAportable"
export LIBS="-lpthread"
sh ./configure

However the configure script is run, when it finishes a new header - user.hpp - located in the <boost-root/libs/config/> directory.
Note : That configure does not install this header into the boost include path by default. This header contains all the options generated by the configure script, plus a header-section that contains the user settable options from the default version of user.hpp (located under <boost-root>/boost/config/). There are two ways this header can be used:

Option 1: Copy the header into <boost-root>/boost/config/ so that it replaces the default user.hpp provided by boost. This option allows only one configure-generated setup; boost developers should avoid this option, as it incurs the danger of accidentally committing a configure-modified user.hpp to the cvs repository (something you will not be thanked for!).

Option 2: Give the header a more memorable name, and place it somewhere convenient; then, define the macro BOOST_USER_CONFIG to point to it. For example create a new sub-directory <boost-root>/boost/config/user/, and copy the header there; for example as "multithread-gcc-config.hpp". Then, when compiling add the command line option: -DBOOST_USER_CONFIG="<boost/config/user/multithread-gcc-config.hpp>", and boost will use the new configuration header. This option allows to generate more than one configuration header, and to keep them separate from the boost source - so that updates to the source do not interfere with the configuration.

User settable options

There are some configuration-options that represent user choices, rather than compiler defects or platform specific options. These are listed in <boost/config/user.hpp> and at the start of a configure-generated user.hpp header. These are defined on the command line, or by editing <boost/config/user.hpp>, they are listed in the following table:

Macro

Description
BOOST_USER_CONFIG | When defined, it should point to the name of the user configuration file to include prior to any boost configuration files. When not defined, defaults to <boost/config/user.hpp>.
BOOST_COMPILER_CONFIG | When defined, it should point to the name of the compiler configuration file to use. Defining this cuts out the compiler selection logic, and eliminates the dependency on the header containing that logic. For example, when using gcc, define BOOST_COMPILER_CONFIG to "boost/config/compiler/gcc.hpp".
BOOST_STDLIB_CONFIG | When defined, it should point to the name of the standard library configuration file to use. Defining this cuts out the standard library selection logic, and eliminates the dependency on the header containing that logic. For example, when using STLport, define BOOST_STDLIB_CONFIG to "boost/config/stdlib/stlport.hpp".
BOOST_PLATFORM_CONFIG | When defined, it should point to the name of the platform configuration file to use. Defining this cuts out the platform selection logic, and eliminates the dependency on the header containing that logic. For example, when compiling on linux, define BOOST_PLATFORM_CONFIG to "boost/config/platform/linux.hpp".
BOOST_NO_COMPILER_CONFIG | When defined, no compiler configuration file is selected or included, define when the compiler is fully conformant with the standard, or where the user header (see BOOST_USER_CONFIG), has had any options necessary added to it, for example by an autoconf generated configure script.
BOOST_NO_STDLIB_CONFIG | When defined, no standard library configuration file is selected or included, define when the standard library is fully conformant with the standard, or where the user header (see BOOST_USER_CONFIG), has had any options necessary added to it, for example by an autoconf generated configure script.
BOOST_NO_PLATFORM_CONFIG | When defined, no platform configuration file is selected or included, define when the platform is fully conformant with the standard (and has no useful extra features), or where the user header (see BOOST_USER_CONFIG), has had any options necessary added to it, for example by an autoconf generated configure script.
BOOST_NO_CONFIG | Equivalent to defining all of BOOST_NO_COMPILER_CONFIG, BOOST_NO_STDLIB_CONFIG and BOOST_NO_PLATFORM_CONFIG.
BOOST_STRICT_CONFIG | The normal behavior for compiler versions that are newer than the last known version, is to assume that they have all the same defects as the last known version. By setting this define, then compiler versions that are newer than the last known version are assumed to be fully conforming with the standard. This is probably most useful for boost developers or testers, and for those who want to use boost to test beta compiler versions.
BOOST_ASSERT_CONFIG | When this flag is set, if the config finds anything unknown, then it will stop with a #error rather than continue. Boost regression testers should set this define, as should anyone who wants to quickly check whether boost is supported on their platform.
BOOST_DISABLE_THREADS | When defined, disables threading support, even if the compiler in its current translation mode supports multiple threads.
BOOST_DISABLE_WIN32 | When defined, disables the use of Win32 specific API's, even when these are available. Also has the effect of setting BOOST_DISABLE_THREADS unless BOOST_HAS_PTHREADS is set. This option may be set automatically by the config system when it detects that the compiler is in "strict mode".
BOOST_DISABLE_ABI_HEADERS | Stops boost headers from including any prefix/suffix headers that normally control things like struct packing and alignment.
BOOST_ABI_PREFIX | A prefix header to include in place of whatever boost.config would normally select, any replacement should set up struct packing and alignment options as required.
BOOST_ABI_SUFFIX | A suffix header to include in place of whatever boost.config would normally select, any replacement should undo the effects of the prefix header.
BOOST_ALL_DYN_LINK

Forces all libraries that have separate source, to be linked as dll's rather than static libraries on Microsoft Windows (this macro is used to turn on __declspec(dllimport) modifiers, so that the compiler knows which symbols to look for in a dll rather than in a static library).

Note that there may be some libraries that can only be statically linked (Boost.Test for example) and others which may only be dynamically linked (Boost.Threads for example), in these cases this macro has no effect.
BOOST_WHATEVER_DYN_LINK

Forces library "whatever" to be linked as a dll rather than a static library on Microsoft Windows: replace the WHATEVER part of the macro name with the name of the library that you want to dynamically link to, for example use BOOST_DATE_TIME_DYN_LINK or BOOST_REGEX_DYN_LINK etc (this macro is used to turn on __declspec(dllimport) modifiers, so that the compiler knows which symbols to look for in a dll rather than in a static library).

Note that there may be some libraries that can only be statically linked (Boost.Test for example) and others which may only be dynamically linked (Boost.Threads for example), in these cases this macro is unsupported.
BOOST_ALL_NO_LIB

Tells the config system not to automatically select which libraries to link against.

Normally if a compiler supports #pragma lib, then the correct library build variant will be automatically selected and linked against, simply by the act of including one of that library's headers. This macro turns that feature off.
BOOST_WHATEVER_NO_LIB

Tells the config system not to automatically select which library to link against for library "whatever", replace WHATEVER in the macro name with the name of the library; for example BOOST_DATE_TIME_NO_LIB or BOOST_REGEX_NO_LIB.

Normally if a compiler supports #pragma lib, then the correct library build variant will be automatically selected and linked against, simply by the act of including one of that library's headers. This macro turns that feature off.
BOOST_LIB_DIAGNOSTIC | Causes the auto-linking code to output diagnostic messages indicating the name of the library that is selected for linking.
BOOST_LIB_TOOLSET | Overrides the name of the toolset part of the name of library being linked to; note if defined this must be defined to a quoted string literal, for example "abc".

Advanced configuration usage

By setting various macros on the compiler command line or by editing <boost/config/user.hpp>, the boost configuration setup can be optimised in a variety of ways.

Boost's configuration is structured so that the user-configuration is included first (defaulting to <boost/config/user.hpp> if BOOST_USER_CONFIG is not defined). This sets up any user-defined policies, and gives the user-configuration a chance to influence what happens next.

Next the compiler, standard library, and platform configuration files are included. These are included via macros (BOOST_COMPILER_CONFIG etc, see user settable macros), and if the corresponding macro is undefined then a separate header that detects which compiler/standard library/platform is in use is included in order to set these. The config can be told to ignore these headers altogether if the corresponding BOOST_NO_XXX macro is set (for example BOOST_NO_COMPILER_CONFIG to disable including any compiler configuration file - see user settable macros).

Finally the boost configuration header, includes <boost/config/suffix.hpp>; this header contains any boiler plate configuration code - for example where one boost macro being set implies that another must be set also.

The following usage examples represent just a few of the possibilities:

Example 1: Creating own frozen configuration.

Lets suppose that the user is building boost with Visual C++ 6, and STLport 4.0. Lets suppose also that there is no intent to update the compiler or standard library any time soon. In order to avoid breaking dependencies when boost is updated, "freeze" the configuration headers, so that the project is rebuilt only if the boost code itself has changed, and not because the boost config has been updated for more recent versions of Visual C++ or STLport. Start by realizing that the configuration files in use are: <boost/config/compiler/visualc.hpp> for the compiler, <boost/config/stdlib/stlport.hpp> for the standard library, and <boost/config/platform/win32.hpp> for the platform. Next we'll create our own private configuration directory: boost/config/mysetup/, and copy the configuration files into there. Finally, open up <boost/config/user.hpp> and edit the following defines:

#define BOOST_COMPILER_CONFIG "boost/config/mysetup/visualc.hpp"
#define BOOST_STDLIB_CONFIG "boost/config/mysetup/stlport.hpp"
#define BOOST_USER_CONFIG "boost/config/mysetup/win32.hpp"

Now when boost is used, its configuration header will go straight to the "frozen" versions, and ignore the default versions, the user is now insulated from any configuration changes when boost is updated. This technique is also useful if some of the boost configuration files are to be modified; for example, when working with a beta compiler release not yet supported by boost.

Example 2: Skipping files that are not needed.

Lets suppose that boost is used with a compiler that is fully conformant with the standard; and there is no interest in the fact that older versions of the compiler may have had bugs, because the current version does not need any configuration macros setting. In a case like this, define BOOST_NO_COMPILER_CONFIG either on the command line, or in <boost/config/user.hpp>, and miss out the compiler configuration header altogether (actually two headers are missed out on, one which works out what the compiler is, and one that configures boost for it). This has two consequences: the first is that less code has to be compiled, and the second that a dependency on two boost headers is removed.

Example 3: Using configure script to freeze the boost configuration.

If working on a unix-like platform then use the configure script to generate a "frozen" configuration based on the current compiler setup - see using the configure script for more details.

Testing the boost configuration

The boost configuration library provides a full set of regression test programs under the <boost-root>/libs/config/test/ sub-directory:

File | Description
config_info.cpp | Prints out a detailed description of the compiler/standard library/platform setup, plus the current boost configuration. The information provided by this program useful in setting up the boost configuration files. In order to report that boost has incorrectly configured for the compiler/library/platform then please include the output from this program when reporting the changes required.
config_test.cpp | A monolithic test program that includes most of the individual test cases. This provides a quick check to see if boost is correctly configured for the compiler/library/platform.
limits_test.cpp | Tests your standard library's std::numeric_limits implementation (or its boost provided replacement if BOOST_NO_LIMITS is defined). This test file fails with most versions of numeric_limits, mainly due to the way that some compilers treat NAN's and infinity.
no_*pass.cpp | Individual compiler defect test files. Each of these should compile, if one does not then the corresponding BOOST_NO_XXX macro needs to be defined - see each test file for specific details.
no_*fail.cpp | Individual compiler defect test files. Each of these should not compile, if one does then the corresponding BOOST_NO_XXX macro is defined when it need not be - see each test file for specific details.
has_*pass.cpp | Individual feature test files. If one of these does not compile then the corresponding BOOST_HAS_XXX macro is defined when it should not be - see each test file for specific details.
has_*fail.cpp | Individual feature test files. If one of these does compile then the corresponding BOOST_HAS_XXX macro can be safely defined - see each test file for specific details.

Although, the configuration regression tests are run as individual test files, there are rather a lot of them, so there are a couple of shortcuts to help out:

If the boost regression test driver is built, then use this to produce a nice html formatted report of the results using the supplied test file.

Alternatively, run the configure script like this:

./configure --enable-test

in which case the script will test the current configuration rather than creating a new one from scratch.

When reporting the results of these tests for a new platform/library/compiler then please include a log of the full compiler output, the output from config_info.cpp, and the pass/fail test results.

Boost Macro Referencee

Macros that describe defects:

The following macros all describe features that are required by the C++ standard, if one of the following macros is defined, then it represents a defect in the compiler's conformance with the standard.

Macro

Section

Description
BOOST_BCB_PARTIAL_SPECIALIZATION_BUG | Compiler | The compiler exhibits certain partial specialization bug - probably Borland C++ Builder specific.
BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL | Compiler | Argument dependent lookup fails if there is a using declaration for the symbol being looked up in the current scope. For example, using boost::get_pointer; prevents ADL from finding overloads of get_pointer in namespaces nested inside boost (but not elsewhere). Probably Borland specific.
BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP | Compiler | Compiler does not implement argument-dependent lookup (also named Koenig lookup); see std::3.4.2 [basic.koenig.lookup]
BOOST_NO_AUTO_PTR | Standard library | If the compiler / library supplies non-standard or broken std::auto_ptr.
BOOST_NO_CTYPE_FUNCTIONS | Platform | The Platform does not provide functions for the character-classifying operations <ctype.h> and <cctype>, only macros.
BOOST_NO_CV_SPECIALIZATIONS | Compiler | If template specializations for cv-qualified types conflict with a specializations for a cv-unqualififed type.
BOOST_NO_CV_VOID_SPECIALIZATIONS | Compiler | If template specialisations for cv-void types conflict with a specialisation for void.
BOOST_NO_CWCHAR | Platform | The Platform does not provide <wchar.h> and <cwchar>.
BOOST_NO_CWCTYPE | Platform | The Platform does not provide <wctype.h> and <cwctype>.
BOOST_NO_DEPENDENT_NESTED_DERIVATIONS Compiler The compiler fails to compile a nested class that has a dependent base class:
template<typename T>
struct foo : {
   template<typename U>
   struct bar : public U {};
};
BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS Compiler Template value parameters cannot have a dependent type, for example:
template<class T, typename T::type value>
class X { ... };
BOOST_NO_EXCEPTION_STD_NAMESPACE | Standard Library | The standard library does not put some or all of the contents of <exception> in namespace std.
BOOST_NO_EXCEPTIONS | Compiler | The compiler does not support exception handling (this setting is typically required by many C++ compilers for embedded platforms). Note that there is no requirement for boost libraries to honor this configuration setting - indeed doing so may be impossible in some cases. Warning:Those libraries that do honor this will typically abort if a critical error occurs.
BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS | Compiler | Can only use deduced template arguments when calling function template instantiations.
BOOST_NO_FUNCTION_TEMPLATE_ORDERING Compiler The compiler does not perform function template ordering or its function template ordering is incorrect.
template<typename T> void f(T); // #1
template<typename T, typename U> void f(T (*)(U)); // #2

void bar(int);

f(&bar); // should choose #2.
BOOST_NO_INCLASS_MEMBER_INITIALIZATION | Compiler | Compiler violates std::9.4.2/4.
BOOST_NO_INTRINSIC_WCHAR_T | Compiler | The C++ implementation does not provide wchar_t, or it is really a synonym for another integral type. Use this symbol to decide whether it is appropriate to explicitly specialize a template on wchar_t if there is already a specialization for other integer types.
BOOST_NO_IS_ABSTRACT | Compiler | The C++ compiler does not support SFINAE with abstract types, this is covered by Core Language DR337, but is not part of the current standard. Fortunately most compilers that support SFINAE also support this DR.
BOOST_NO_LIMITS | Standard library | The C++ implementation does not provide the <limits> header. Never check for this symbol in library code; always include <boost/limits.hpp>, which guarantees to provide std::numeric_limits.
BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS | Standard library | Constants such as numeric_limits<T>::is_signed are not available for use at compile-time.
BOOST_NO_LONG_LONG_NUMERIC_LIMITS | Standard library | There is no specialization for numeric_limits<long long> and numeric_limits<unsigned long long>. <boost/limits.hpp> will then add these specializations as a standard library "fix" only if the compiler supports the long long datatype.
BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS | Compiler | The compiler does not support the specialization of individual member functions of template classes.
BOOST_NO_MEMBER_TEMPLATE_KEYWORD | Compiler | If the compiler supports member templates, but not the template keyword when accessing member template classes.
BOOST_NO_MEMBER_TEMPLATE_FRIENDS | Compiler | Member template friend syntax ("template<class P> friend class frd;") described in the C++ Standard, 14.5.3, not supported.
BOOST_NO_MEMBER_TEMPLATES | Compiler | Member template functions not fully supported.
BOOST_NO_MS_INT64_NUMERIC_LIMITS | Standard library | There is no specialization for numeric_limits<__int64> and numeric_limits<unsigned __int64>. <boost/limits.hpp> will then add these specializations as a standard library "fix", only if the compiler supports the __int64 datatype.
BOOST_NO_OPERATORS_IN_NAMESPACE | Compiler | Compiler requires inherited operator friend functions to be defined at namespace scope, then using'ed to boost. Probably GCC specific. See boost/operators.hpp for example.
BOOST_NO_POINTER_TO_MEMBER_CONST | Compiler | The compiler does not correctly handle pointers to const member functions, preventing use of these in overloaded function templates. See boost/functional.hpp for example.
BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS | Compiler | Pointers to members don't work when used as template parameters.
BOOST_NO_PRIVATE_IN_AGGREGATE | Compiler | The compiler misreads 8.5.1, treating classes as non-aggregate if they contain private or protected member functions.
BOOST_NO_SFINAE | Compiler | The compiler does not support the "Substitution Failure Is Not An Error" meta-programming idiom.
BOOST_NO_STD_ALLOCATOR | Standard library | The C++ standard library does not provide a standards conforming std::allocator.
BOOST_NO_STD_DISTANCE | Standard library | The platform does not have a conforming version of std::distance.
BOOST_NO_STD_ITERATOR | Standard library | The C++ implementation fails to provide the std::iterator class.
BOOST_NO_STD_ITERATOR_TRAITS | Standard library | The compiler does not provide a standard compliant implementation of std::iterator_traits. Note that the compiler may still have a non-standard implementation.
BOOST_NO_STD_LOCALE | Standard library | The standard library lacks std::locale.
BOOST_NO_STD_MESSAGES | Standard library | The standard library lacks a conforming std::messages facet.
BOOST_NO_STD_MIN_MAX | Standard library | The C++ standard library does not provide the min() and max() template functions that should be in <algorithm>.
BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN | Standard library | Defined if the standard library's output iterators are not assignable.
BOOST_NO_STD_USE_FACET | Standard library | The standard library lacks a conforming std::use_facet.
BOOST_NO_STD_WSTREAMBUF | Standard library | The standard library's implementation of std::basic_streambuf<wchar_t> is either missing, incomplete, or buggy.
BOOST_NO_STD_WSTRING | Standard library | The standard library lacks std::wstring.
BOOST_NO_STDC_NAMESPACE | Compiler/Platform | The contents of C++ standard headers for C library functions (the <c...> headers) have not been placed in namespace std. This test is difficult - some libraries "fake" the std C functions by adding using declarations to import them into namespace std, unfortunately they don't necessarily catch all of them.
BOOST_NO_STRINGSTREAM | Standard library | The C++ implementation does not provide the <sstream> header.
BOOST_NO_SWPRINTF | Platform | The platform does not have a conforming version of swprintf.
BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION | Compiler | Class template partial specialization (14.5.4 [temp.class.spec]) not supported.
BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS | Standard library | The standard library does not provide templated iterator constructors for its containers.
BOOST_NO_TEMPLATE_TEMPLATES | Compiler | The compiler does not support template template parameters.
BOOST_NO_UNREACHABLE_RETURN_DETECTION | Compiler | If a return is unreachable, then no return statement should be required, however some compilers insist on it, while other issue a bunch of warnings if it is in fact present.
BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE | Compiler | The compiler will not accept a using declaration that brings a function from a typename used as a base class into a derived class if functions of the same name are present in the derived class.
BOOST_NO_USING_TEMPLATE | Compiler | The compiler will not accept a using declaration that imports a template class or function from another namespace. Originally a Borland specific problem with imports to/from the global namespace, extended to MSVC6 which has a specific issue with importing template classes (but not functions).
BOOST_NO_VOID_RETURNS Compiler The compiler does not allow a void function to return the result of calling another void function.
void f() {}
void g() { return f(); }

Macros that describe optional features:

The following macros describe features that are not required by the C++ standard. The macro is only defined if the feature is present.

Macro | Section | Description
BOOST_HAS_BETHREADS | Platform | The platform supports BeOS style threads.
BOOST_HAS_CLOCK_GETTIME | Platform | The platform has the POSIX API clock_gettime.
BOOST_HAS_DECLSPEC | Compiler | The compiler uses __declspec(dllexport) and __declspec(dllimport) to export/import symbols from dll's.
BOOST_HAS_DIRENT_H | Platform | The platform has the POSIX header <dirent.h>.
BOOST_HAS_EXPM1 | Platform | The platform has the functions expm1 expm1f and expm1l in <math.h>
BOOST_HAS_FTIME | Platform | The platform has the Win32 API GetSystemTimeAsFileTime.
BOOST_HAS_GETTIMEOFDAY | Platform | The platform has the POSIX API gettimeofday.
BOOST_HAS_HASH | Standard library | The C++ implementation provides the (SGI) hash_set and hash_map classes. When defined, BOOST_HASH_SET_HEADER and BOOST_HASH_LIST_HEADER will contain the names of the header needed to access hash_set and hash_map; BOOST_STD_EXTENSION_NAMESPACE will provide the namespace in which the two class templates reside.
BOOST_HAS_LOG1P | Platform | The platform has the functions log1p, log1pf and log1pl in <math.h>.
BOOST_HAS_LONG_LONG | Compiler | The compiler supports the long long data type.
BOOST_HAS_MACRO_USE_FACET | Standard library | The standard library lacks a conforming std::use_facet, but has a macro _USE(loc, Type) that does the job. This is primarily for the Dinkumware std lib.
BOOST_HAS_MS_INT64 | Compiler | The compiler supports the __int64 data type.
BOOST_HAS_NANOSLEEP | Platform | The platform has the POSIX API nanosleep.
BOOST_HAS_NL_TYPES_H | Platform | The platform has an <nl_types.h>.
BOOST_HAS_NRVO | Compiler | Indicated that the compiler supports the named return value optimization (NRVO). Used to select the most efficient implementation for some function. See boost/operators.hpp for example.
BOOST_HAS_PARTIAL_STD_ALLOCATOR | Standard Library | The standard library has a partially conforming std::allocator class, but without any of the member templates.
BOOST_HAS_PTHREAD_DELAY_NP | Platform | The platform has the POSIX API pthread_delay_np.
BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE | Platform | The platform has the POSIX API pthread_mutexattr_settype.
BOOST_HAS_PTHREAD_YIELD | Platform | The platform has the POSIX API pthread_yield.
BOOST_HAS_PTHREADS | Platform | The platform support POSIX style threads.
BOOST_HAS_SCHED_YIELD | Platform | The platform has the POSIX API sched_yield.
BOOST_HAS_SGI_TYPE_TRAITS | Compiler/standard library | The compiler has native support for SGI style type traits.
BOOST_HAS_STDINT_H | Platform | The platform has a <stdint.h>
BOOST_HAS_SLIST | Standard library | The C++ implementation provides the (SGI) slist class. When defined, BOOST_SLIST_HEADER will contain the name of the header needed to access slist and BOOST_STD_EXTENSION_NAMESPACE will provide the namespace in which slist resides.
BOOST_HAS_STLP_USE_FACET | Standard library | The standard library lacks a conforming std::use_facet, but has a workaround class-version that does the job. This is primarily for the STLport std lib.
BOOST_HAS_TR1_ARRAY | Standard library | The library has a TR1 conforming version of <array>
BOOST_HAS_TR1_COMPLEX_OVERLOADS | Standard library | The library has a version of <complex> that supports passing scalars to the complex number algorithms.
BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG | Standard library | The library has a version of <complex> that includes the new inverse trig functions from TR1.
BOOST_HAS_TR1_REFERENCE_WRAPPER | Standard library | The library has TR1 conforming reference wrappers in <functional>.
BOOST_HAS_TR1_RESULT_OF | Standard library | The library has a TR1 conforming result_of template in <functional>.
BOOST_HAS_TR1_MEM_FN | Standard library | The library has a TR1 conforming mem_fn function template in <functional>.
BOOST_HAS_TR1_BIND | Standard library | The library has a TR1 conforming bind function template in <functional>.
BOOST_HAS_TR1_FUNCTION | Standard library | The library has a TR1 conforming function class template in <functional>.
BOOST_HAS_TR1_HASH | Standard library | The library has a TR1 conforming hash function template in <functional>.
BOOST_HAS_TR1_SHARED_PTR | Standard library | The library has a TR1 conforming shared_ptr class template in <memory>.
BOOST_HAS_TR1_RANDOM | Standard library | The library has a TR1 conforming version of <random>.
BOOST_HAS_TR1_REGEX | Standard library | The library has a TR1 conforming version of <regex>.
BOOST_HAS_TR1_TUPLE | Standard library | The library has a TR1 conforming version of <tuple>.
BOOST_HAS_TR1_TYPE_TRAITS | Standard library | The library has a TR1 conforming version of <type_traits>.
BOOST_HAS_TR1_UTILITY | Standard library | The library has the TR1 additions to <utility> (tuple interface to std::pair).
BOOST_HAS_TR1_UNORDERED_MAP | Standard library | The library has a TR1 conforming version of <unordered_map>.
BOOST_HAS_TR1_UNORDERED_SET | Standard library | The library has a TR1 conforming version of <unordered_set>.
BOOST_HAS_TR1 | Standard library | Implies all the other BOOST_HAS_TR1_* macros should be set.
BOOST_HAS_THREADS | Platform/compiler | Defined if the compiler, in its current translation mode, supports multiple threads of execution.
BOOST_HAS_TWO_ARG_USE_FACET | Standard library | The standard library lacks a conforming std::use_facet, but has a two argument version that does the job. This is primarily for the Rogue Wave std lib.
BOOST_HAS_UNISTD_H | Platform | The Platform provides <unistd.h>.
BOOST_HAS_WINTHREADS | Platform | The platform supports MS Windows style threads.
BOOST_MSVC_STD_ITERATOR | Standard library | Microsoft's broken version of std::iterator is being used. This implies that std::iterator takes no more than two template parameters.
BOOST_MSVC6_MEMBER_TEMPLATES | Compiler | Microsoft Visual C++ 6.0 has enough member template idiosyncrasies (being polite) that BOOST_NO_MEMBER_TEMPLATES is defined for this compiler. BOOST_MSVC6_MEMBER_TEMPLATES is defined to allow compiler specific workarounds. This macro gets defined automatically if BOOST_NO_MEMBER_TEMPLATES is not defined - in other words this is treated as a strict subset of the features required by the standard.
BOOST_HAS_STDINT_H | Platform | There are no 1998 C++ Standard headers <stdint.h> or <cstdint>, although the 1999 C Standard does include <stdint.h>. If <stdint.h> is present, <boost/stdint.h> can make good use of it, so a flag is supplied (signalling presence; thus the default is not present, conforming to the current C++ standard).

Boost Helper Macros

The following macros are either simple helpers, or macros that provide workarounds for compiler/standard library defects.

Macro

Description
BOOST_DEDUCED_TYPENAME Some compilers do not support the use of typename for dependent types in deduced contexts. This macro expands to nothing on those compilers, and typename elsewhere. For example, replace:
template <class T> void f(T, typename T::type);

with:

template <class T> void f(T, BOOST_DEDUCED_TYPENAME T::type);
BOOST_HASH_MAP_HEADER | The header to include to get the SGI hash_map class. This macro is only available if BOOST_HAS_HASH is defined.
BOOST_HASH_SET_HEADER | The header to include to get the SGI hash_set class. This macro is only available if BOOST_HAS_HASH is defined.
BOOST_SLIST_HEADER | The header to include to get the SGI slist class. This macro is only available if BOOST_HAS_SLIST is defined.
BOOST_STD_EXTENSION_NAMESPACE | The namespace used for std library extensions (hashtable classes etc).
BOOST_STATIC_CONSTANT(Type, assignment) On compilers that do not allow in-class initialization of static integral constant members, use enums as a workaround if the constants must be available at compile-time. This macro provides a convenient way to declare such constants. For example instead of:
struct foo{
   static const int value = 2;
};

use:

struct foo{
   BOOST_STATIC_CONSTANT(int, value = 2);
};
BOOST_UNREACHABLE_RETURN(result) | Normally evaluates to nothing, but evaluates to return x; if the compiler requires a return, even when it can never be reached.
BOOST_EXPLICIT_TEMPLATE_TYPE(t)
BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v)
BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(t)
BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(t, v)
         Some compilers silently "fold" different function template instantiations if some of the template parameters don't appear in the function parameter list. For instance:
  #include <iostream>
  #include <ostream>
  #include <typeinfo>

  template <int n>
  void f() { std::cout << n << ' '; }

  template <typename T>
  void g() { std::cout << typeid(T).name() << ' '; }

  int main() {
    f<1>();
    f<2>();

    g<int>();
    g<double>();
  }
incorrectly outputs "2 2 double double " on VC++ 6. These macros, to be used in the function parameter list, fix the problem without effects on the calling syntax. For instance, in the case above write:
  template <int n>
  void f(BOOST_EXPLICIT_TEMPLATE_NON_TYPE(int, n)) { ... }

  template <typename T>
  void g(BOOST_EXPLICIT_TEMPLATE_TYPE(T)) { ... }
Beware that they can declare (for affected compilers) a dummy defaulted parameter, so they

a) should be always invoked *at the end* of the parameter list
b) cannot be used if the function template is multiply declared.

Furthermore, in order to add any needed comma separator, an "APPEND_*" version must be used when the macro invocation appears after a normal parameter declaration or after the invocation of another macro of this same group.
BOOST_USE_FACET(Type, loc) When the standard library does not have a comforming std::use_facet there are various workarounds available, but they differ from library to library. This macro provides a consistent way to access a locale's facets. For example, replace:
std::use_facet<Type>(loc);

with:

BOOST_USE_FACET(Type, loc);

Note do not add a std:: prefix to the front of BOOST_USE_FACET.
BOOST_HAS_FACET(Type, loc) When the standard library does not have a comforming std::has_facet there are various workarounds available, but they differ from library to library. This macro provides a consistent way to check a locale's facets. For example, replace:
std::has_facet<Type>(loc);

with:

BOOST_HAS_FACET(Type, loc);

Note do not add a std:: prefix to the front of BOOST_HAS_FACET.
BOOST_NESTED_TEMPLATE Member templates are supported by some compilers even though they can't use the A::template member<U> syntax, as a workaround replace:
typedef typename A::template rebind<U> binder;

with:

typedef typename A::BOOST_NESTED_TEMPLATE rebind<U> binder;
BOOST_STRINGIZE(X) | Converts the parameter X to a string after macro replacement on X has been performed.
BOOST_JOIN(X,Y) | This piece of macro magic joins the two arguments together, even when one of the arguments is itself a macro (see 16.3.1 in C++ standard). This is normally used to create a mangled name in combination with a predefined macro such a __LINE__.

Boost Informational Macros

The following macros describe boost features; these are, generally speaking the only boost macros that should be tested in user code.

Macro | Header | Description
BOOST_VERSION | <boost/version.hpp> | Describes the boost version number in XXYYZZ format such that: (BOOST_VERSION % 100) is the sub-minor version, ((BOOST_VERSION / 100) % 1000) is the minor version, and (BOOST_VERSION / 100000) is the major version.
BOOST_NO_INT64_T | <boost/cstdint.hpp> <boost/stdint.h> | Defined if there are no 64-bit integral types: int64_t, uint64_t etc.
BOOST_NO_INTEGRAL_INT64_T | <boost/cstdint.hpp> <boost/stdint.h> | Defined if int64_t as defined by <boost/cstdint.hpp> is not usable in integral constant expressions.
BOOST_MSVC | <boost/config.hpp> | Defined if the compiler is really Microsoft Visual C++, as opposed to one of the many other compilers that also define _MSC_VER.
BOOST_INTEL | <boost/config.hpp> | Defined if the compiler is an Intel compiler, takes the same value as the compiler version macro.
BOOST_WINDOWS | <boost/config.hpp> | Defined if the Windows platfrom API is available.
BOOST_DINKUMWARE_STDLIB | <boost/config.hpp> | Defined if the dinkumware standard library is in use, takes the same value as the Dinkumware library version macro _CPPLIB_VER if defined, otherwise 1.
BOOST_NO_WREGEX | <boost/regex.hpp> | Defined if the regex library does not support wide character regular expressions.
BOOST_COMPILER | <boost/config.hpp> | Defined as a string describing the name and version number of the compiler in use. Mainly for debugging the configuration.
BOOST_STDLIB | <boost/config.hpp> | Defined as a string describing the name and version number of the standard library in use. Mainly for debugging the configuration.
BOOST_PLATFORM | <boost/config.hpp> | Defined as a string describing the name of the platform. Mainly for debugging the configuration.

Macros for libraries with separate source code

The following macros and helper headers are of use to authors whose libraries include separate source code, and are intended to address two issues: fixing the ABI of the compiled library, and selecting which compiled library to link against based upon the compilers settings.

ABI Fixing

When linking against a pre-compiled library it is vital that the ABI used by the compiler when building the library matches exactly the ABI used by the code using the library. In this case ABI means things like the struct packing arrangement used, the name mangling scheme used, or the size of some types (enum types for example). This is separate from things like threading support, or runtime library variations, which have to be dealt with by build variants. To put this in perspective there is one compiler (Borland's) that has so many compiler options that make subtle changes to the ABI, that at least in theory there are 3200 combinations, and that is without considering runtime library variations. Fortunately these variations can be managed by #pragma's that tell the compiler what ABI to use for the types declared in your library. In order to avoid sprinkling #pragma's all over the boost headers, there are some prefix and suffix headers that do the job. Typical usage is:

my_library.cpp

#ifndef MY_INCLUDE_GUARD
#define MY_INCLUDE_GUARD

// all includes go here:
#include <boost/config.hpp>
#include <whatever>

#include <boost/config/abi_prefix.hpp>  // must be the last #include

namespace boost{
// your code goes here
}

#include <boost/config/abi_suffix.hpp>  // pops abi_prefix.hpp pragmas

#endif // include guard

my_library.cpp

...
// nothing special need be done in the implementation file
...

The user can disable this mechanism by defining BOOST_DISABLE_ABI_HEADERS, or they can define BOOST_ABI_PREFIX and/or BOOST_ABI_SUFFIX to point to their own prefix/suffix headers if they so wish.

Automatic library selection

It is essential that users link to a build of a library which was built against the same runtime library that their application will be built against - if this does not happen then the library will not be binary compatible with their own code - and there is a high likelihood that their application will experience runtime crashes. These kinds of problems can be extremely time consuming and difficult to debug, and often lead to frustrated users and authors alike (simply selecting the right library to link against is not as easy as it seems when their are 6-8 of them to chose from, and some users seem to be unaware that there even are different runtimes available to them).

To solve this issue, some compilers allow source code to contain #pragma's that instruct the linker which library to link against, all the user need do is include the headers they need, place the compiled libraries in their library search path, and the compiler and linker do the rest. Boost.config supports this via the header <boost/config/auto_link.hpp>, before including this header one or more of the following macros need to be defined:

BOOST_LIB_NAME | Required: An identifier containing the basename of the library, for example 'boost_regex'.
BOOST_DYN_LINK | Optional: when set link to dll rather than static library.
BOOST_LIB_DIAGNOSTIC | Optional: when set the header will print out the name of the library selected (useful for debugging).

If the compiler supports this mechanism, then it will be told to link against the appropriately named library, the actual algorithm used to mangle the name of the library is documented inside <boost/config/auto_link.hpp> and has to match that used to create the libraries via bjam 's install rules.

Typical usage is:

my_library.hpp

...
//
// Don't include auto-linking code if the user has disabled it by
// defining BOOST_ALL_NO_LIB, or BOOST_MY_LIBRARY_NO_LIB, or if this
// is one of our own source files (signified by BOOST_MY_LIBRARY_SOURCE):
//
#if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_MY_LIBRARY_NO_LIB) && !defined(BOOST_MY_LIBRARY_SOURCE)
#  define BOOST_LIB_NAME boost_my_library
#  ifdef BOOST_MY_LIBRARY_DYN_LINK
#     define BOOST_DYN_LINK
#  endif
#  include <boost/config/auto_link.hpp>
#endif
...

my_library.cpp

// define BOOST_MY_LIBRARY_SOURCE so that the header knows that the
// library is being built (possibly exporting rather than importing code)
//
#define BOOST_MY_LIBRARY_SOURCE

#include <boost/my_library/my_library.hpp>
...

Guidelines for Boost Authors

The boost/config.hpp header is used to pass configuration information to other boost files, allowing them to cope with platform dependencies such as arithmetic byte ordering, compiler pragmas, or compiler shortcomings. Without such configuration information, many current compilers would not work with the Boost libraries.

Centralizing configuration information in this header reduces the number of files that must be modified when porting libraries to new platforms, or when compilers are updated. Ideally, no other files would have to be modified when porting to a new platform.

Configuration headers are controversial because some view them as condoning broken compilers and encouraging non-standard subsets. Adding settings for additional platforms and maintaining existing settings can also be a problem. In other words, configuration headers are a necessary evil rather than a desirable feature. The boost config.hpp policy is designed to minimize the problems and maximize the benefits of a configuration header.

Note:

  • Boost library implementers are not required to #include <boost/config.hpp>, and are not required in any way to support compilers that do not comply with the C++ Standard (ISO/IEC 14882).

  • If a library implementer wishes to support some non-conforming compiler, or to support some platform specific feature, #include <boost/config.hpp> is the preferred way to obtain configuration information not available from the standard headers such as <climits>, etc.

  • If configuration information can be deduced from standard headers such as <climits>, use those standard headers rather than <boost/config.hpp>.

  • Boost files that use macros defined in <boost/config.hpp> should have sensible, standard conforming, default behavior if the macro is not defined. This means that the starting point for porting <boost/config.hpp> to a new platform is simply to define nothing at all specific to that platform. In the rare case where there is no sensible default behavior, an #error message should describe the problem.

  • If a Boost library implementer wants something added to config.hpp, post a request on the Boost mailing list. There is no guarantee such a request will be honored; the intent is to limit the complexity of config.hpp.

  • The intent is to support only compilers which appear on their way to becoming C++ Standard compliant, and only recent releases of those compilers at that.

  • The intent is not to disable mainstream features now well-supported by the majority of compilers, such as namespaces, exceptions, RTTI, or templates.

Adding New Defect Macros

To add a new defect macro - either to fix a problem with an existing library, or when adding a new library - distill the issue down to a simple test case; often, at this point other (possibly better) workarounds may become apparent. Secondly always post the test case code to the boost mailing list and invite comments; remember that C++ is complex and that sometimes what may appear a defect, may in fact turn out to be a problem with the authors understanding of the standard.

When naming the macro, follow the BOOST_NO_SOMETHING naming convention, so that it is obvious that this is a macro reporting a defect.

Finally, add the test program to the regression tests. Place the test case in a .ipp file with the following comments near the top:

//  MACRO:         BOOST_NO_FOO
//  TITLE:         foo
//  DESCRIPTION:   If the compiler fails to support foo

These comments are processed by the autoconf script, so make sure the format follows the one given. The file should be named "boost_no_foo.ipp", where foo is the defect description - try and keep the file name under the Mac 30 character filename limit though. Provide a function prototype "int test()" that is declared in a namespace with the same name as the macro, but in all lower case, and which returns zero on success:

namespace boost_no_foo{

int test()
{
   // test code goes here:
   //
   return 0;
}

}

Once the test code is in place, build and run the program "generate.cpp" found in the boost-root/libs/config/tools/ directory. This generates two .cpp test files from the new test code, and adds the tests to the regression test Jamfile, and the config_test.cpp test program. Finally add a new entry to config_info.cpp so that the new macro gets printed out when that program is run.

Adding New Feature Test Macros

To add a macro that describes a feature that the standard does not require, follow the convention for adding a new defect macro (above), but call the macro BOOST_HAS_FOO, and name the test file "boost_has_foo.ipp". Try not to add feature test macros unnecessarily, if there is a platform specific macro that can already be used (for example _WIN32, __BEOS__, or __linux) to identify the feature then use that. Try to keep the macro to a feature group, or header name, rather than one specific API (for example BOOST_HAS_NL_TYPES_H rather than BOOST_HAS_CATOPEN). If the macro describes a POSIX feature group, then add boilerplate code to boost/config/suffix.hpp to auto-detect the feature where possible (The POSIX feature cannot be used to test the macro directly, because many of these features can be added by third party libraries, and are not therefore identified inside <unistd.h>).

Modifying the Boost Configuration Headers

The aim of boost's configuration setup is that the configuration headers should be relatively stable - a boost user should not have to recompile their code just because the configuration for some compiler that they are not interested in has changed. Separating the configuration into separate compiler/standard library/platform sections provides for part of this stability, but boost authors require some amount of restraint as well, in particular:

<boost/config.hpp> should never change, do not alter this file.

<boost/config/user.hpp> is included by default, do not add extra code to this file unless required. If extra code is added, please remember to update libs/config/tools/configure.in as well.

<boost/config/suffix.hpp> is always included so be careful about modifying this file as it breaks dependencies for everyone. This file should include only "boilerplate" configuration code, and generally should change only when new macros are added.

<boost/config/select_compiler_config.hpp>, <boost/config/select_platform_config.hpp> and <boost/config/select_stdlib_config.hpp> are included by default and should change only if support for a new compiler/standard library/platform is added.

The compiler/platform/standard library selection code is set up so that unknown platforms are ignored and assumed to be fully standards compliant - this gives unknown platforms a "sporting chance" of working "as is" even without running the configure script.

When adding or modifying the individual mini-configs, assume that future, as yet unreleased versions of compilers, have all the defects of the current version. Although this is perhaps unnecessarily pessimistic, it cuts down on the maintenance of these files, and experience suggests that pessimism is better placed.

Rationale

The problem with many traditional "textbook" implementations of configuration headers (where all the configuration options are in a single "monolithic" header) is that they violate certain fundamental software engineering principles which would have the effect of making boost more fragile, more difficult to maintain and more difficult to use safely. The following article gives a description of the principles.

The problem

Consider a situation in which the user is concurrently developing on multiple platforms. In this case when adding a new platform or changing the platform definitions of an existing platform. Everything, and this does literally mean everything, recompiles. Isn't it quite absurd that adding a new platform, which has absolutely nothing to do with previously existing platforms, means that all code on all existing platforms needs to be recompiled?

Effectively, there is an imposed physical dependency between platforms that have nothing to do with each other. Essentially, the traditional solution employed by configuration headers does not conform to the Open-Closed Principle:

"A module should be open for extension but closed for modification."

Extending a traditional configuration header implies modifying existing code.

Furthermore, consider the complexity and fragility of the platform detection code. What if a simple change breaks the detection on some minor platform? What if someone accidentally or on purpose (as a workaround for some other problem) defines some platform dependent macros that are used by the detection code? A traditional configuration header is one of the most volatile headers of the entire library, and more stable elements of Boost would depend on it. This violates the Stable Dependencies Principle:

"Depend in the direction of stability."

After even a minor change to a traditional configuration header on one minor platform, almost everything on every platform should be tested if we follow sound software engineering practice.

Another important issue is that it is not always possible to submit changes to <boost/config.hpp>. Some boost users are currently working on platforms using tools and libraries that are under strict Non-Disclosure Agreements. In this situation it is impossible to submit changes to a traditional monolithic configuration header, instead some method by which the user can insert their own configuration code must be provided.

The solution

The approach taken by boost's configuration headers is to separate configuration into three orthogonal parts: the compiler, the standard library and the platform. Each compiler/standard library/platform gets its own mini-configuration header, so that changes to one compiler's configuration (for example) does not affect other compilers. In addition there are measures that can be taken both to omit the compiler/standard library/platform detection code (so that adding support to a new platform does not break dependencies), or to freeze the configuration completely; providing almost complete protection against dependency changes.

Copyright Beman Dawes 2001

Copyright Vesa Karvonen 2001

Copyright John Maddock 2001

Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at www.boost.org/License.html).


S60 Boost Library API Reference: C:/Gen_boost/Generation/api_ref/ Directory Reference

Boost C++ Libraries
API Reference Guide
api_ref

api_ref Directory Reference

Directories

directory epoc32

© Nokia 2006-2007

Back to top


S60 Boost Library API Reference: C:/Gen_boost/Generation/api_ref/epoc32/ Directory Reference

Boost C++ Libraries
API Reference Guide
api_ref » epoc32

epoc32 Directory Reference

Directories

directory include

© Nokia 2006-2007

Back to top


S60 Boost Library API Reference: C:/Gen_boost/Generation/api_ref/epoc32/include/ Directory Reference

Boost C++ Libraries
API Reference Guide
api_ref » epoc32 » include

include Directory Reference

Files

file display.inl
Defines the inline constructors for the display data classes.

© Nokia 2006-2007

Back to top


S60 Boost Library API Reference: Directory Hierarchy

Boost C++ Libraries
API Reference Guide

S60 Boost Library API Reference Directories

This directory hierarchy is sorted roughly, but not completely, alphabetically:
  • api_ref
    • epoc32
      • include

© Nokia 2006-2007

Back to top


S60 Boost Library API Reference: display.inl File Reference

Boost C++ Libraries
API Reference Guide
api_ref » epoc32 » include

display.inl File Reference

Detailed Description

Defines the inline constructors for the display data classes.

© Nokia 2006-2007

Back to top


Boost: enable_shared_from_this.hpp documentation

enable_shared_from_this.hpp

Purpose

The header <boost/enable_shared_from_this.hpp> defines the class template enable_shared_from_this. It is used as a base class that allows a shared_ptr to the current object to be obtained from within a member function.

enable_shared_from_this<T> defines two member functions called shared_from_this that return a shared_ptr<T> and shared_ptr<T const>, depending on constness, to this.

Example

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

Synopsis

namespace boost
{

template<class T> class enable_shared_from_this
{
public:

    shared_ptr<T> shared_from_this();
    shared_ptr<T const> shared_from_this() const;
}

}

template<class T> shared_ptr<T> enable_shared_from_this<T>::shared_from_this();
template<class T> shared_ptr<T const> enable_shared_from_this<T>::shared_from_this() const;

Requires: enable_shared_from_this<T> must be an accessible base class of T. *this must be a subobject of an instance t of type T . There must exist at least one shared_ptr instance p that owns t.

Returns: A shared_ptr<T> instance r that shares ownership with p.

Postconditions: r.get() == this

Copyright © 2002, 2003 by Peter Dimov. Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


Concept EqualityComparable

EqualityComparable

Description

Equality Comparable types must have == and != operators.

Notation

X:

A type playing the role of comparable-type in the EqualityComparable concept.

x, y :

Objects of type X

Valid expressions

Name | Expression | Type
Equality test | x == y | Convertible to bool
Inequality test | x != y | Convertible to bool

Models

  • int
  • std::vector<int>

Copyright © 2001, 2002 Indiana University
Copyright © 2000, 2001 University of Notre Dame du Lac
Copyright © 2000 Jeremy Siek, Lie-Quan Lee, Andrew Lumsdaine
Copyright © 1996-1999 Silicon Graphics Computer Systems, Inc.
Copyright © 1994 Hewlett-Packard Company


Exception-specification rationale

Exception specifications [ISO 15.4] are sometimes coded to indicate what exceptions may be thrown, or because the programmer hopes they will improved performance. But consider the following member from a smart pointer:

T& operator*() const throw()  { return *ptr; }
This function calls no other functions; it only manipulates fundamental data types like pointers Therefore, no runtime behavior of the exception-specification can ever be invoked. The function is completely exposed to the compiler; indeed it is declared inline Therefore, a smart compiler can easily deduce that the functions are incapable of throwing exceptions, and make the same optimizations it would have made based on the empty exception-specification. A "dumb" compiler, however, may make all kinds of pessimizations.

For example, some compilers turn off inlining if there is an exception-specification. Some compilers add try/catch blocks. Such pessimizations can be a performance disaster which makes the code unusable in practical applications.

Although initially appealing, an exception-specification tends to have consequences that require very careful thought to understand. The biggest problem with exception-specifications is that programmers use them as though they have the effect the programmer would like, instead of the effect they actually have.

A non-inline function is the one place a "throws nothing" exception-specification may have some benefit with some compilers.

© Copyright Beman Dawes 2003. Distributed under the Boost Software License, Version 1.0. (See accompanying file License.html or copy at http://www.boost.org/LICENSE_1_0.txt)


Frequently Asked Questions

1. Why can't I compare boost::function objects with operator== or operator!=?
2. I see void pointers; is this [mess] type safe?
3. Why are there workarounds for void returns? C++ allows them!
4. Why (function) cloning?
5. How much overhead does a call through boost::function incur?
1.

Why can't I compare boost::function objects with operator== or operator!=?

Comparison between boost::function objects cannot be implemented "well", and therefore will not be implemented. The typical semantics requested for f == g given boost::function objects f and g are:

  • If f and g store function objects of the same type, use that type's operator== to compare them.
  • If f and g store function objects of different types, return false.

The problem occurs when the type of the function objects stored by both f and g doesn't have an operator==: the expression f == g fails to compile, as occurs with, e.g., the standard containers. However, this is not implementable for boost::function because it necessarily "erases" some type information after it has been assigned a function object, so it cannot try to call operator== later: it must either find a way to call operator==, or it will never be able to call it later. Note, for instance, what happens if a float value is put into a boost::function object: the user gets an error at the assignment operator or constructor, not in operator(), because the function-call expression must be bound in the constructor or assignment operator.

The most promising approach is to find a method of determining if operator== can be called for a particular type, and then supporting it only when it is available; in other situations, an exception would be thrown. However, to date there is no known way to detect if an arbitrary operator expression f == g is suitably defined. The best solution known has the following undesirable qualities:

  1. Fails at compile-time for objects where operator== is not accessible (e.g., because it is private).
  2. Fails at compile-time if calling operator== is ambiguous.
  3. Appears to be correct if the operator== declaration is correct, even though operator== may not compile.

All of these problems translate into failures in the boost::function constructors or assignment operator, even if the user never invokes operator==.

The other option is to place the burden on users that want to use operator==, e.g., by providing an is_equality_comparable to specialize the trait. This is a workable solution, but is dangerous in practice, because forgetting to specialize the trait will result in unexpected exceptions being thrown from boost::function 's operator==. This essentially negates the usefulness of operator== in the context in which it is most desired: multitarget callbacks. The Signals library has a way around this.
2.

I see void pointers; is this [mess] type safe?

Yes, boost::function is type safe even though it uses void pointers and pointers to functions returning void and taking no arguments. Essentially, all type information is encoded in the functions that manage and invoke function pointers and function objects. Only these functions are instantiated with the exact type that is pointed to by the void pointer or pointer to void function. The reason that both are required is that one may cast between void pointers and object pointers safely or between different types of function pointers (provided a function pointer with the wrong type is not invoked).

3.

Why are there workarounds for void returns? C++ allows them!

Void returns are permitted by the C++ standard, as in this code snippet:

void f();
void g() { return f(); }

This is a valid usage of boost::function because void returns are not used. With void returns, the user attempts to compile ill-formed code similar to:

int f();
void g() { return f(); }

In essence, not using void returns allows boost::function to swallow a return value. This is consistent with allowing the user to assign and invoke functions and function objects with parameters that don't exactly match.
4.

Why (function) cloning?

In November and December of 2000, the issue of cloning vs. reference counting was debated at length and it was decided that cloning gave more predictable semantics. If cloning is incorrect for a particular application a reference-counting allocator could be used.
5.

How much overhead does a call through boost::function incur?

The cost of boost::function can be reasonably consistently measured at around 20ns +/- 10 ns on a modern >2GHz platform versus directly inlining the code.

However, the performance of your application may benefit from or be disadvantaged by boost::function depending on how your C++ optimiser optimises. Similar to a standard function pointer, differences of order of 10% have been noted to the benefit or disadvantage of using boost::function to call a function that contains a tight loop depending on your compilation circumstances.

[Answer provided by Matt Hurd. See http://article.gmane.org/gmane.comp.lib.boost.devel/33278]
Copyright © 2001-2004 Douglas Gregor


S60 Boost Library API Reference: File Index

Boost C++ Libraries
API Reference Guide

S60 Boost Library API Reference File List

Here is a list of all files with brief descriptions:
display.inl Defines the inline constructors for the display data classes

© Nokia 2006-2007

Back to top


Class template function

boost::function — A generalized function pointer that can be used for callbacks or wrapping function objects.

Synopsis

template<typename Signature,   // Function type R (T1, T2, ..., TN)
         typename Allocator = std::allocator<void> >
class function : public functionN<R, T1, T2, ..., TN, Allocator> {
public:
  // types
  typedef R         result_type;
  typedef Allocator allocator_type;
  typedef T1        argument_type;         // If N == 1
  typedef T1        first_argument_type;   // If N == 2
  typedef T2        second_argument_type;  // If N == 2
  typedef T1        arg1_type;
  typedef T2        arg2_type;
     .
     .
     .
  typedef TN        argN_type;

  // static constants
  static const int arity = N;

  // Lambda library support
  template<typename Args>
  struct sig {
    // types
    typedef result_type type;
  };

  // construct/copy/destruct
  function();
  function(const functionN&);
  function(const function&);
  template<typename F> function(F);
  function& operator=(const function&);
  function& operator=(const function&);
  ~function();

  // modifiers
  void swap(const function&);
  void clear();

  // capacity
  bool empty() const;
  operator safe_bool() const;
  bool operator!() const;

  // target access
  template<typename Functor> Functor* target();
  template<typename Functor> const Functor* target() const;
  template<typename Functor> bool contains(const Functor&) const;
  const std::type_info& target_type() const;

  // invocation
  result_type operator()(arg1_type, arg2_type, ..., argN_type) const;
};

// specialized algorithms
template<typename Signature, typename Allocator>
  void swap(function<Signature, Allocator>&, function<Signature, Allocator>&);

// comparison operators
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(const function<Signature, Allocator>&, Functor);
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(Functor, const function<Signature, Allocator>&);
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(const function<Signature, Allocator>&,
                  reference_wrapper<Functor>);
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(reference_wrapper<Functor>,
                  const function<Signature, Allocator>&);
template<typename Signature1, typename Allocator1, typename Signature2,
         typename Allocator2>
  void operator==(const function<Signature1, Allocator1>&,
                  const function<Signature2, Allocator2>&);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(const function<Signature, Allocator>&, Functor);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(Functor, const function<Signature, Allocator>&);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(const function<Signature, Allocator>&,
                  reference_wrapper<Functor>);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(reference_wrapper<Functor>,
                  const function<Signature, Allocator>&);
template<typename Signature1, typename Allocator1, typename Signature2,
         typename Allocator2>
  void operator!=(const function<Signature1, Allocator1>&,
                  const function<Signature2, Allocator2>&);

Description

Class template function is a thin wrapper around the numbered class templates function0, function1, etc. It accepts a function type with N arguments and will will derive from functionN instantiated with the arguments it receives.

The semantics of all operations in class template function are equivalent to that of the underlying functionN object, although additional member functions are required to allow proper copy construction and copy assignment of function objects.

function construct/copy/destruct

  1. function();
    Postconditions: this->empty()
    Throws: Will not throw.
  2. function(const functionN& f);
    Postconditions: Contains a copy of the f's target, if it has one, or is empty if f.empty().
    Throws: Will not throw unless copying the target of f throws.
  3. function(const function& f);
    Postconditions: Contains a copy of the f's target, if it has one, or is empty if f.empty().
    Throws: Will not throw unless copying the target of f throws.
  4. template<typename F> function(F f);
    Requires: F is a function object Callable from this.
    Postconditions: *this targets a copy of f if f is nonempty, or this->empty() if f is empty.
  5. function& operator=(const function& f);
    Postconditions:

    If copy construction does not throw, *this targets a copy of f's target, if it has one, or is empty if f.empty(). If copy construction does throw, this->empty().

  6. function& operator=(const function& f);
    Postconditions: If copy construction of the target of f does not throw, *this targets a copy of f's target, if it has one, or is empty if f.empty().
    Throws: Will not throw when the target of f is a stateless function object or a reference to the function object. If copy construction does throw, this->empty().
  7. ~function();
    Effects: If !this->empty(), destroys the target of this.

function modifiers

  1. void swap(const function& f);
    Effects: Interchanges the targets of *this and f.
  2. void clear();
    Postconditions: this->empty()
    Throws: Will not throw.

function capacity

  1. bool empty() const;
    Returns: false if this has a target, and true otherwise.
    Throws: Will not throw.
  2. operator safe_bool() const;
    Returns: A safe_bool that evaluates false in a boolean context when this->empty(), and true otherwise.
    Throws: Will not throw.
  3. bool operator!() const;
    Returns: this->empty()
    Throws: Will not throw.

function target access

  1. template<typename Functor> Functor* target();
template<typename Functor> const Functor* target() const;
    Returns: If this stores a target of type Functor, returns the address of the target. Otherwise, returns the NULL pointer.
    Throws: Will not throw.
  2. template<typename Functor> bool contains(const Functor& f) const;
    Returns: true if this->target<Functor>() is non-NULL and function_equal(*(this->target<Functor>()), f)
  3. const std::type_info& target_type() const;
    Returns: typeid of the target function object, or typeid(void) if this->empty().
    Throws: Will not throw.

function invocation

  1. result_type operator()(arg1_type a1, arg2_type a2, ..., argN_type aN) const;
    Effects: f(a1, a2, ..., aN), where f is the target of *this.
    Returns: if R is void, nothing is returned; otherwise, the return value of the call to f is returned.
    Throws: bad_function_call if this->empty(). Otherwise, may through any exception thrown by the target function f.

function specialized algorithms

  1. template<typename Signature, typename Allocator>
  void swap(function<Signature, Allocator>& f1,
            function<Signature, Allocator>& f2);
    Effects: f1.swap(f2)

function comparison operators

  1. template<typename Signature, typename Allocator, typename Functor>
  bool operator==(const function<Signature, Allocator>& f, Functor g);
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(Functor g, const function<Signature, Allocator>& f);
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(const function<Signature, Allocator>& f,
                  reference_wrapper<Functor> g);
template<typename Signature, typename Allocator, typename Functor>
  bool operator==(reference_wrapper<Functor> g,
                  const function<Signature, Allocator>& f);
template<typename Signature1, typename Allocator1, typename Signature2,
         typename Allocator2>
  void operator==(const function<Signature1, Allocator1>& f1,
                  const function<Signature2, Allocator2>& f2);
    Returns: True when f stores an object of type Functor and one of the following conditions applies:
    • g is of type reference_wrapper<Functor> and f.target<Functor>() == g.get_pointer().
    • g is not of type reference_wrapper<Functor> and function_equals(*(f.target<Functor>()), g).
    Notes: function objects are not EqualityComparable.
    Rationale: The safe_bool conversion opens a loophole whereby two function instances can be compared via ==, although this is not feasible to implement. The undefined void operator== closes the loophole and ensures a compile-time or link-time error.

  2. template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(const function<Signature, Allocator>& f, Functor g);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(Functor g, const function<Signature, Allocator>& f);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(const function<Signature, Allocator>& f,
                  reference_wrapper<Functor> g);
template<typename Signature, typename Allocator, typename Functor>
  bool operator!=(reference_wrapper<Functor> g,
                  const function<Signature, Allocator>& f);
template<typename Signature1, typename Allocator1, typename Signature2,
         typename Allocator2>
  void operator!=(const function<Signature1, Allocator1>& f1,
                  const function<Signature2, Allocator2>& f2);
    Returns: True when f does not store an object of type Functor or it stores an object of type Functor and one of the following conditions applies:

    • g is of type reference_wrapper<Functor> and f.target<Functor>() != g.get_pointer().

    • g is not of type reference_wrapper<Functor> and !function_equals(*(f.target<Functor>()), g).
    Notes: function objects are not EqualityComparable.
    Rationale: The safe_bool conversion opens a loophole whereby two function instances can be compared via !=, although this is not feasible to implement. The undefined void operator!= closes the loophole and ensures a compile-time or link-time error.
Copyright © 2001-2004 Douglas Gregor


Class function_base

boost::function_base — The common base class for all Boost.Function objects. Objects of type function_base may not be created directly.

Synopsis

class function_base {
public:

  // capacity
  bool empty() const;

  // target access
  template<typename Functor> Functor* target();
  template<typename Functor> const Functor* target() const;
  template<typename Functor> bool contains(const Functor&) const;
  const std::type_info& target_type() const;
};

Description

function_base capacity

  1. bool empty() const;
    Returns:

    false if this has a target, and true otherwise.

    Throws: Will not throw.

function_base target access

  1. template<typename Functor> Functor* target();
template<typename Functor> const Functor* target() const;
    Returns: If this stores a target of type Functor, returns the address of the target. Otherwise, returns the NULL pointer.
    Throws: Will not throw.
  2. template<typename Functor> bool contains(const Functor& f) const;
    Returns: true if this->target<Functor>() is non-NULL and function_equal(*(this->target<Functor>()), f)
  3. const std::type_info& target_type() const;
    Returns: typeid of the target function object, or typeid(void) if this->empty().
    Throws: Will not throw.
Copyright © 2001-2004 Douglas Gregor


Function template function_equal

boost::function_equal: Compares two function objects for equality.

Synopsis

template<typename F, typename G> bool function_equal(const F& f, const G& g);

Description

Returns: | f == g.
Throws: | Only if f == g throws.

Copyright © 2001-2004 Douglas Gregor


Class template functionN

boost::functionN — A set of generalized function pointers that can be used for callbacks or wrapping function objects.

Synopsis

template<typename R, typename T1, typename T2, ..., typename TN,
         typename Allocator = std::allocator<void> >
class functionN : public function_base {
public:
  // types
  typedef R         result_type;
  typedef Allocator allocator_type;
  typedef T1        argument_type;         // If N == 1
  typedef T1        first_argument_type;   // If N == 2
  typedef T2        second_argument_type;  // If N == 2
  typedef T1        arg1_type;
  typedef T2        arg2_type;
     .
     .
     .
  typedef TN        argN_type;

  // static constants
  static const int arity = N;

  // Lambda library support
  template<typename Args>
  struct sig {
    // types
    typedef result_type type;
  };

  // construct/copy/destruct
  functionN();
  functionN(const functionN&);
  template<typename F> functionN(F);
  functionN& operator=(const functionN&);
  ~functionN();

  // modifiers
  void swap(const functionN&);
  void clear();

  // capacity
  bool empty() const;
  operator safe_bool() const;
  bool operator!() const;

  // target access
  template<typename Functor> Functor* target();
  template<typename Functor> const Functor* target() const;
  template<typename Functor> bool contains(const Functor&) const;
  const std::type_info& target_type() const;

  // invocation
  result_type operator()(arg1_type, arg2_type, ..., argN_type) const;
};

// specialized algorithms
template<typename T1, typename T2, ..., typename TN, typename Allocator>
  void swap(functionN<T1, T2, ..., TN, Allocator>&,
            functionN<T1, T2, ..., TN, Allocator>&);

// comparison operators
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(const functionN<T1, T2, ..., TN, Allocator>&, Functor);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(Functor, const functionN<T1, T2, ..., TN, Allocator>&);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(const functionN<T1, T2, ..., TN, Allocator>&,
                  reference_wrapper<Functor>);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(reference_wrapper<Functor>,
                  const functionN<T1, T2, ..., TN, Allocator>&);
template<typename T1, typename T2, ..., typename TN, typename Allocator1,
         typename U1, typename U2, ..., typename UN, typename Allocator2>
  void operator==(const functionN<T1, T2, ..., TN, Allocator1>&,
                  const functionN<U1, U2, ..., UN, Allocator2>&);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(const functionN<T1, T2, ..., TN, Allocator>&, Functor);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(Functor, const functionN<T1, T2, ..., TN, Allocator>&);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(const functionN<T1, T2, ..., TN, Allocator>&,
                  reference_wrapper<Functor>);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(reference_wrapper<Functor>,
                  const functionN<T1, T2, ..., TN, Allocator>&);
template<typename T1, typename T2, ..., typename TN, typename Allocator1,
         typename U1, typename U2, ..., typename UN, typename Allocator2>
  void operator!=(const functionN<T1, T2, ..., TN, Allocator1>&,
                  const functionN<U1, U2, ..., UN, Allocator2>&);

Description

Class template functionN is actually a family of related classes function0, function1, etc., up to some implementation-defined maximum. In this context, N refers to the number of parameters.

functionN construct/copy/destruct

  1. functionN();
    Postconditions: this->empty()
    Throws: Will not throw.
  2. functionN(const functionN& f);
    Postconditions: Contains a copy of the f's target, if it has one, or is empty if f.empty().
    Throws: Will not throw unless copying the target of f throws.
  3. template<typename F> functionN(F f);
    Requires: F is a function object Callable from this.
    Postconditions: *this targets a copy of f if f is nonempty, or this->empty() if f is empty.
  4. functionN& operator=(const functionN& f);
    Postconditions: If copy construction does not throw, *this targets a copy of f's target, if it has one, or is empty if f.empty(). If copy construction does throw, this->empty().
  5. ~functionN();
    Effects: If !this->empty(), destroys the target of this.

functionN modifiers

  1. void swap(const functionN& f);
    Effects: Interchanges the targets of *this and f.
  2. void clear();
    Postconditions: this->empty()

functionN capacity

  1. bool empty() const;
    Returns: false if this has a target, and true otherwise.
    Throws: Will not throw.
  2. operator safe_bool() const;
    Returns: A safe_bool that evaluates false in a boolean context when this->empty(), and true otherwise.
    Throws: Will not throw.
  3. bool operator!() const;
    Returns: this->empty()
    Throws: Will not throw.

functionN target access

  1. template<typename Functor> Functor* target();
template<typename Functor> const Functor* target() const;
    Returns: If this stores a target of type Functor, returns the address of the target. Otherwise, returns the NULL pointer.
    Throws: Will not throw.
  2. template<typename Functor> bool contains(const Functor& f) const;
    Returns: true if this->target<Functor>() is non-NULL and function_equal(*(this->target<Functor>()), f)
  3. const std::type_info& target_type() const;
    Returns: typeid of the target function object, or typeid(void) if this->empty().
    Throws: Will not throw.

functionN invocation

  1. result_type operator()(arg1_type a1, arg2_type a2, ..., argN_type aN) const;
    Effects: f(a1, a2, ..., aN), where f is the target of *this.
    Returns: if R is void, nothing is returned; otherwise, the return value of the call to f is returned.
    Throws: bad_function_call if this->empty(). Otherwise, may through any exception thrown by the target function f.

functionN specialized algorithms

  1. template<typename T1, typename T2, ..., typename TN, typename Allocator>
  void swap(functionN<T1, T2, ..., TN, Allocator>& f1,
            functionN<T1, T2, ..., TN, Allocator>& f2);
    Effects: f1.swap(f2)

functionN comparison operators

  1. template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(const functionN<T1, T2, ..., TN, Allocator>& f, Functor g);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(Functor g, const functionN<T1, T2, ..., TN, Allocator>& f);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(const functionN<T1, T2, ..., TN, Allocator>& f,
                  reference_wrapper<Functor> g);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator==(reference_wrapper<Functor> g,
                  const functionN<T1, T2, ..., TN, Allocator>& f);
template<typename T1, typename T2, ..., typename TN, typename Allocator1,
         typename U1, typename U2, ..., typename UN, typename Allocator2>
  void operator==(const functionN<T1, T2, ..., TN, Allocator1>& f1,
                  const functionN<U1, U2, ..., UN, Allocator2>& f2);
    Returns: True when f stores an object of type Functor and one of the following conditions applies:
    • g is of type reference_wrapper<Functor> and f.target<Functor>() == g.get_pointer().
    • g is not of type reference_wrapper<Functor> and function_equal(*(f.target<Functor>()), g).
    Notes: functionN objects are not EqualityComparable.
    Rationale: The safe_bool conversion opens a loophole whereby two functionN instances can be compared via ==, although this is not feasible to implement. The undefined void operator== closes the loophole and ensures a compile-time or link-time error.

  2. template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(const functionN<T1, T2, ..., TN, Allocator>& f, Functor g);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(Functor g, const functionN<T1, T2, ..., TN, Allocator>& f);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(const functionN<T1, T2, ..., TN, Allocator>& f,
                  reference_wrapper<Functor> g);
template<typename T1, typename T2, ..., typename TN, typename Allocator,
         typename Functor>
  bool operator!=(reference_wrapper<Functor> g,
                  const functionN<T1, T2, ..., TN, Allocator>& f);
template<typename T1, typename T2, ..., typename TN, typename Allocator1,
         typename U1, typename U2, ..., typename UN, typename Allocator2>
  void operator!=(const functionN<T1, T2, ..., TN, Allocator1>& f1,
                  const functionN<U1, U2, ..., UN, Allocator2>& f2);
    Returns: True when f does not store an object of type Functor or it stores an object of type Functor and one of the following conditions applies:
    • g is of type reference_wrapper<Functor> and f.target<Functor>() != g.get_pointer().
    • g is not of type reference_wrapper<Functor> and !function_equal(*(f.target<Functor>()), g).
    Notes: functionN objects are not EqualityComparable.
    Rationale: The safe_bool conversion opens a loophole whereby two functionN instances can be compared via !=, although this is not feasible to implement. The undefined void operator!= closes the loophole and ensures a compile-time or link-time error.
Copyright © 2001-2004 Douglas Gregor


Greatest Common Divisor and Least Common Multiple

Introduction

Synopsis

GCD Function Object

LCM Function Object

Run-time GCD & LCM Determination

Compile time GCD and LCM determination

Header <boost/math/common_factor.hpp>

Demonstration Program

Rationale

History

Credits

Introduction

The class and function templates in <boost/math/common_factor.hpp> provide run-time and compile-time evaluation of the greatest common divisor (GCD) or least common multiple (LCM) of two integers. These facilities are useful for many numeric-oriented generic programming problems.

Synopsis

namespace boost
{
namespace math
{

template < typename IntegerType >
   class gcd_evaluator;
template < typename IntegerType >
   class lcm_evaluator;

template < typename IntegerType >
   IntegerType  gcd( IntegerType const &a, IntegerType const &b );
template < typename IntegerType >
   IntegerType  lcm( IntegerType const &a, IntegerType const &b );

template < unsigned long Value1, unsigned long Value2 >
   struct static_gcd;
template < unsigned long Value1, unsigned long Value2 >
   struct static_lcm;

}
}

GCD Function Object

Header: <boost/math/common_factor_rt.hpp>

template < typename IntegerType >
class boost::math::gcd_evaluator
{
public:
   // Types
   typedef IntegerType  result_type;
   typedef IntegerType  first_argument_type;
   typedef IntegerType  second_argument_type;

   // Function object interface
   result_type  operator ()( first_argument_type const &a,
   second_argument_type const &b ) const;
};

The boost::math::gcd_evaluator class template defines a function object class to return the greatest common divisor of two integers. The template is parameterized by a single type, called IntegerType. This type should be a numeric type that represents integers. The result of the function object is always nonnegative, even if either of the operator arguments is negative.

This function object class template is used in the corresponding version of the GCD function template. If a numeric type wants to customize evaluations of its greatest common divisors, then the type should specialize on the gcd_evaluator class template.

LCM Function Object

Header: <boost/math/common_factor_rt.hpp>

template < typename IntegerType >
class boost::math::lcm_evaluator
{
public:
   // Types
   typedef IntegerType  result_type;
   typedef IntegerType  first_argument_type;
   typedef IntegerType  second_argument_type;

   // Function object interface
   result_type  operator ()( first_argument_type const &a,
   second_argument_type const &b ) const;
};

The boost::math::lcm_evaluator class template defines a function object class to return the least common multiple of two integers. The template is parameterized by a single type, called IntegerType. This type should be a numeric type that represents integers. The result of the function object is always nonnegative, even if either of the operator arguments is negative. If the least common multiple is beyond the range of the integer type, the results are undefined.

This function object class template is used in the corresponding version of the LCM function template. If a numeric type wants to customize evaluations of its least common multiples, then the type should specialize on the lcm_evaluator class template.

Run-time GCD & LCM Determination

Header: <boost/math/common_factor_rt.hpp>

template < typename IntegerType >
IntegerType  boost::math::gcd( IntegerType const &a, IntegerType const &b );

template < typename IntegerType >
IntegerType  boost::math::lcm( IntegerType const &a, IntegerType const &b );

The boost::math::gcd function template returns the greatest common (non-negative) divisor of the two integers passed to it. The boost::math::lcm function template returns the least common (nonnegative) multiple of the two integers passed to it. The function templates are parameterized on the function arguments' IntegerType, which is also the return type. Internally, these function templates use an object of the corresponding version of the gcd_evaluator and lcm_evaluator class templates, respectively.

Compile time GCD and LCM determination

Header: <boost/math/common_factor_ct.hpp>

template < unsigned long Value1, unsigned long Value2 >
struct boost::math::static_gcd
{
   static unsigned long const  value = implementation_defined;
};

template < unsigned long Value1, unsigned long Value2 >
struct boost::math::static_lcm
{
   static unsigned long const  value = implementation_defined;
};

The boost::math::static_gcd and boost::math::static_lcm class templates take two value-based template parameters of the unsigned long type and have a single static constant data member, value, of that same type. The value of that member is the greatest common factor or least common multiple, respectively, of the template arguments. A compile-time error will occur if the least common multiple is beyond the range of an unsigned long.

Example

#include <boost/math/common_factor.hpp>
#include <algorithm>
#include <iterator>

int main()
{
   using std::cout;
   using std::endl;

   cout << "The GCD and LCM of 6 and 15 are "
   << boost::math::gcd(6, 15) << " and "
   << boost::math::lcm(6, 15) << ", respectively."
   << endl;

   cout << "The GCD and LCM of 8 and 9 are "
   << boost::math::static_gcd<8, 9>::value
   << " and "
   << boost::math::static_lcm<8, 9>::value
   << ", respectively." << endl;

   int  a[] = { 4, 5, 6 }, b[] = { 7, 8, 9 }, c[3];
   std::transform( a, a + 3, b, c, boost::math::gcd_evaluator<int>() );
   std::copy( c, c + 3, std::ostream_iterator<int>(cout, " ") );
}

Header <boost/math/common_factor.hpp>

This header simply includes the headers <boost/math/common_factor_ct.hpp> and <boost/math/common_factor_rt.hpp>.

Note: Tthis is a legacy header, it used to contain the actual implementation, but the compile-time and run-time facilities were moved to separate headers (since they were independent of each other).

Demonstration Program

The program common_factor_test.cpp is a demonstration of the results from instantiating various examples of the run-time GCD and LCM function templates and the compile-time GCD and LCM class templates. (The run-time GCD and LCM class templates are tested indirectly through the run-time function templates.)

Rationale

The greatest common divisor and least common multiple functions are used in some numeric contexts, including some of the other Boost libraries. Centralize these functions to one header to improve code factoring and ease maintenance.

Credits

The author of the Boost compilation of GCD and LCM computations is Daryle Walker. The code was prompted by existing code hiding in the implementations of Paul Moore's rational library and Steve Cleary's pool library. The code had updates by Helmut Zeisel.

Copyright © 2001 -2002 Daryle Walker, 2001-2003 Hubert Holin, 2005 John Maddock


Greatest Common Divisor and Least Common Multiple

Name

Greatest Common Divisor and Least Common Multiple: The class and function templates in <boost/math/common_factor.hpp> provide run-time and compile-time evaluation of the greatest common divisor (GCD) or least common multiple (LCM) of two integers.

Synopsis

namespace boost
{
namespace math
{

template < typename IntegerType >
   class gcd_evaluator;
template < typename IntegerType >
   class lcm_evaluator;

template < typename IntegerType >
   IntegerType  gcd( IntegerType const &a, IntegerType const &b );
template < typename IntegerType >
   IntegerType  lcm( IntegerType const &a, IntegerType const &b );

template < unsigned long Value1, unsigned long Value2 >
   struct static_gcd;
template < unsigned long Value1, unsigned long Value2 >
   struct static_lcm;

}
}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


History & Compatibility Notes

  • Version 1.34.0:

    • Boost.Function now implements a small buffer optimization, which can drastically improve the performance when copying or construction Boost.Function objects storing small function objects. For instance, bind(&X:foo, &x, _1, _2) requires no heap allocation when placed into a Boost.Function object. Note that some exception-safety guarantees have changed: assignment provides the basic exception guarantee and swap() may throw.

  • Version 1.30.0:

    • All features deprecated in version 1.29.0 have been removed from Boost.Function.

    • boost::function and boost::functionN objects can be assigned to 0 (semantically equivalent to calling clear()) and compared against 0 (semantically equivalent to calling empty()).

    • The Boost.Function code is now generated entirely by the Preprocessor library, so it is now possible to generate boost::function and boost::functionN class templates for any number of arguments.

    • The boost::bad_function_call exception class was introduced.

  • Version 1.29.0:

    Boost.Function has been partially redesigned to minimize the interface and make it cleaner. Several seldom- or never-used features of the older Boost.Function have been deprecated and will be removed in the near future. Here is a list of features that have been deprecated, the likely impact of the deprecations, and how to adjust your code:

    • The boost::function class template syntax has changed. The old syntax, e.g., boost::function<int, float, double, std::string>, has been changed to a more natural syntax boost::function<int (float, double, std::string)>, where all return and argument types are encoded in a single function type parameter. Any other template parameters (e.g., the Allocator) follow this single parameter.

      The resolution to this change depends on the abilities of your compiler: if your compiler supports template partial specialization and can parse function types (most do), modify your code to use the newer syntax (preferable) or directly use one of the functionN classes whose syntax has not changed. If your compiler does not support template partial specialization or function types, you must take the latter option and use the numbered Boost.Function classes. This option merely requires changing types such as boost::function<void, int, int> to boost::function2<void, int, int> (adding the number of function arguments to the end of the class name).

      Support for the old syntax with the boost::function class template will persist for a short while, but will eventually be removed to provide better error messages and link compatibility.

    • The invocation policy template parameter (Policy) has been deprecated and will be removed. There is no direct equivalent to this rarely used feature.

    • The mixin template parameter (Mixin) has been deprecated and will be removed. There is not direct equivalent to this rarely used feature.

    • The set methods have been deprecated and will be removed. Use the assignment operator instead.

Copyright © 2001-2004 Douglas Gregor


intrusive_ptr

Intrusive_ptr class template

Introduction
Synopsis
Members
Free Functions

Introduction

The intrusive_ptr class template stores a pointer to an object with an embedded reference count. Every new intrusive_ptr instance increments the reference count by using an unqualified call to the function intrusive_ptr_add_ref, passing the pointer as an argument. Similarly, when an intrusive_ptr is destroyed, it calls intrusive_ptr_release; this function is responsible for destroying the object when its reference count drops to zero. The user is expected to provide suitable definitions of these two functions. On compilers that support argument-dependent lookup, intrusive_ptr_add_ref and intrusive_ptr_release should be defined in the namespace that corresponds to their parameter; otherwise, the definitions need to go in namespace boost.

The class template is parameterized on T, the type of the object pointed to intrusive_ptr<T> can be implicitly converted to intrusive_ptr<U> whenever T* can be implicitly converted to U*.

The main reasons to use intrusive_ptr are:

  • Some existing frameworks or OSes provide objects with embedded reference counts;

  • The memory footprint of intrusive_ptr is the same as the corresponding raw pointer;

  • intrusive_ptr<T> can be constructed from an arbitrary raw pointer of type T *.

As a general rule, if it isn't obvious whether intrusive_ptr better fits the needs than shared_ptr, try a shared_ptr-based design first.

Synopsis

namespace boost {

  template<class T> class intrusive_ptr {

    public:

      typedef T element_type;

      intrusive_ptr(); // never throws
      intrusive_ptr(T * p, bool add_ref = true);

      intrusive_ptr(intrusive_ptr const & r);
      template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);

      ~intrusive_ptr();

      intrusive_ptr & operator=(intrusive_ptr const & r);
      template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
      template<class Y> intrusive_ptr & operator=(T * r);

      T & operator*() const; // never throws
      T * operator->() const; // never throws
      T * get() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(intrusive_ptr & b); // never throws
  };

  template<class T, class U>
    bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

  template<class T, class U>
    bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

  template<class T>
    bool operator==(intrusive_ptr<T> const & a, T * b); // never throws

  template<class T>
    bool operator!=(intrusive_ptr<T> const & a, T * b); // never throws

  template<class T>
    bool operator==(T * a, intrusive_ptr<T> const & b); // never throws

  template<class T>
    bool operator!=(T * a, intrusive_ptr<T> const & b); // never throws

  template<class T, class U>
    bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

  template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws

  template<class T> T * get_pointer(intrusive_ptr<T> const & p); // never throws

  template<class T, class U>
    intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws

  template<class T, class U>
    intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r); // never throws

  template<class T, class U>
    intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r); // never throws

  template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p);

}

Members

element_type

typedef T element_type;

Provides the type of the template parameter T.

constructors

intrusive_ptr(); // never throws

Postconditions: get() == 0.

Throws: nothing

intrusive_ptr(T * p, bool add_ref = true);

Effects: if(p != 0 && add_ref) intrusive_ptr_add_ref(p);.

Postconditions: get() == p.

intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);

Effects: if(r.get() != 0) intrusive_ptr_add_ref(r.get());

Postconditions: get() == r.get()

destructor

~intrusive_ptr();

Effects: if(get() != 0) intrusive_ptr_release(get());

assignment

intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);

Effects: Equivalent to intrusive_ptr(r).swap(*this).

Returns: *this

indirection

T & operator*() const; // never throws

Requirements: get() != 0

Returns: *get()

Throws: nothing

T * operator->() const; // never throws

Requirements: get() != 0

Returns: get()

Throws: nothing

get

T * get() const; // never throws

Returns: the stored pointer.

Throws: nothing

conversions

operator unspecified-bool-type () const; // never throws

Returns: an unspecified value that, when used in boolean contexts, is equivalent to get() != 0.

Throws: nothing.

Notes: This conversion operator allows intrusive_ptr objects to be used in boolean contexts, like if (p && p->valid()) {}. The actual target type is typically a pointer to a member function, avoiding many of the implicit conversion pitfalls.

swap

void swap(intrusive_ptr & b); // never throws

Effects: Exchanges the contents of the two smart pointers.

Throws: nothing.

Free Functions

comparison

template<class T, class U>
  bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

Returns: a.get() == b.get()

Throws: nothing

template<class T, class U>
  bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

Returns: a.get() != b.get()

Throws: nothing

template<class T, class U>
  bool operator==(intrusive_ptr<T> const & a, U * b); // never throws

Returns: a.get() == b

Throws: nothing

template<class T, class U>
  bool operator!=(intrusive_ptr<T> const & a, U * b); // never throws

Returns: a.get() != b

Throws: nothing

template<class T, class U>
  bool operator==(T * a, intrusive_ptr<U> const & b); // never throws

Returns: a == b.get()

Throws: nothing

template<class T, class U>
  bool operator!=(T * a, intrusive_ptr<U> const & b); // never throws

Returns: a != b.get()

Throws: nothing

template<class T, class U>
  bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

Returns: std::less<T *>()(a.get(), b.get())

Throws: nothing

Notes: Allows intrusive_ptr objects to be used as keys in associative containers.

swap

template<class T>
  void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws

Effects: Equivalent to a.swap(b)

Throws: nothing

Notes: Matches the interface of std::swap. Provided as an aid to generic programming.

get_pointer

template<class T>
  T * get_pointer(intrusive_ptr<T> const & p); // never throws

Returns: p.get()

Throws: nothing

Notes: Provided as an aid to generic programming. Used by mem_fn.

static_pointer_cast

template<class T, class U>
  intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws

Returns: intrusive_ptr<T>(static_cast<T*>(r.get()))

Throws: nothing

const_pointer_cast

template<class T, class U>
  intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r); // never throws

Returns: intrusive_ptr<T>(const_cast<T*>(r.get()))

Throws: nothing

dynamic_pointer_cast

template<class T, class U>
  intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r);

Returns: intrusive_ptr<T>(dynamic_cast<T*>(r.get()))

Throws: nothing

operator<<

template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p);

Effects: os << p.get();

Returns: os

Copyright © 2003-2005 Peter Dimov. Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


intrusive_ptr

Name

intrusive_ptr: Class template stores a pointer to an object with an embedded reference count.

Synopsis

namespace boost {

  template<class T> class intrusive_ptr {

    public:

      typedef T element_type;

      intrusive_ptr(); // never throws
      intrusive_ptr(T * p, bool add_ref = true);

      intrusive_ptr(intrusive_ptr const & r);
      template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);

      ~intrusive_ptr();

      intrusive_ptr & operator=(intrusive_ptr const & r);
      template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
      template<class Y> intrusive_ptr & operator=(T * r);

      T & operator*() const; // never throws
      T * operator->() const; // never throws
      T * get() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(intrusive_ptr & b); // never throws
  };

  template<class T, class U>
    bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

  template<class T, class U>
    bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

  template<class T>
    bool operator==(intrusive_ptr<T> const & a, T * b); // never throws

  template<class T>
    bool operator!=(intrusive_ptr<T> const & a, T * b); // never throws

  template<class T>
    bool operator==(T * a, intrusive_ptr<T> const & b); // never throws

  template<class T>
    bool operator!=(T * a, intrusive_ptr<T> const & b); // never throws

  template<class T, class U>
    bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b); // never throws

  template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b); // never throws

  template<class T> T * get_pointer(intrusive_ptr<T> const & p); // never throws

  template<class T, class U>
    intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r); // never throws

  template<class T, class U>
    intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r); // never throws

  template<class T, class U>
    intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r); // never throws

  template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p);

}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Complex Number Inverse Trigonometric Functions

Implementation and Accuracy

asin

acos

atan

asinh

acosh

atanh

History

The following complex number algorithms are the inverses of trigonometric functions currently present in the C++ standard. Equivalents to these functions are part of the C99 standard, and will be part of the forthcoming Technical Report on C++ Standard Library Extensions.

Implementation and Accuracy

Although there are simple formulae available for all of these functions, a naive implementation that uses these formulae could fail for some input values. The Boost versions of these functions are implemented using the methodology described in "Implementing the Complex Arcsine and Arccosine Functions Using Exception Handling" by T. E. Hull Thomas F. Fairgrieve and Ping Tak Peter Tang, ACM Transactions on Mathematical Software, Vol. 23, No. 3, September 1997. This means that the functions are well defined over the entire complex number range, and produce accurate values even at the extremes of that range, whereas a naive formula could cause overflow or underflow to occur during the calculation, even though the result is actually a representable value. The maximum theoretical relative error for all of these functions is less than 9.5E for every machine-representable point in the complex plane. Please refer to comments in the header files themselves and to the above mentioned paper for more information on the implementation methodology.

asin

#include <boost/math/complex/asin.hpp>

Synopsis

template<class T>
std::complex<T> asin(const std::complex<T>& z);

Return value :This function returns the inverse sine of the complex number z.

Formula: asin

acos

#include <boost/math/complex/acos.hpp>

Synopsis

template<class T>
std::complex<T> acos(const std::complex<T>& z);

Return value: This function returns the inverse cosine of the complex number z.

Formula: acos

atan

#include <boost/math/complex/atan.hpp>

Synopsis

template<class T>
std::complex<T> atan(const std::complex<T>& z);

Return value: This function returns the inverse tangent of the complex number z.

Formula: atan

asinh

#include <boost/math/complex/asinh.hpp>

Synopsis

template<class T>
std::complex<T> asinh(const std::complex<T>& z);

Return value: This function returns the inverse hyperbolic sine of the complex number z.

Formula: asinh

acosh

#include <boost/math/complex/acosh.hpp>

Synopsis

template<class T>
std::complex<T> acosh(const std::complex<T>& z);

Return value: This function returns the inverse hyperbolic cosine of the complex number z.

Formula: acosh

atanh

#include <boost/math/complex/atanh.hpp>

Synopsis

template<class T>
std::complex<T> atanh(const std::complex<T>& z);

Return value: This function returns the inverse hyperbolic tangent of the complex number z.

Formula: atanh

Copyright © 2001 -2002 Daryle Walker, 2001-2003 Hubert Holin, 2005 John Maddock


Class template is_reference_wrapper

boost::is_reference_wrapper — Determine if a type T is an instantiation of reference_wrapper.

Synopsis

template<typename T>
class is_reference_wrapper {
public:
  // static constants
  static const bool value = unspecified;
};

Description

The value static constant will be true if the type T is a specialization of reference_wrapper .

Copyright © 1999, 2000 Jaakko Järvi
Copyright © 2001, 2002 Peter Dimov
Copyright © 2002 David Abrahams


License

Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:

The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


S60 Boost Library API Reference: Main Page

Boost C++ Libraries
API Reference Guide

S60 Boost Library API Reference Documentation

  • Main Page
  • Files
  • Directories

© Nokia 2006-2007

Back to top


Math Special Functions

Overview

Header Files

Synopsis

acosh

asinh

atanh

expm1

hypot

log1p

sinc_pi

sinhc_pi

Overview

The Special Functions library currently provides eight templated special functions, in namespace boost. Use the functions sinc_pi and sinhc_pi to implement quaternions and octonions.

The functions acosh , asinh and atanh are entirely classical, use the function sinc_pi in signal processing tasks, and the function sinhc_pi is an ad'hoc function whose naming is modelled on sinc_pi and hyperbolic functions.

The functions log1p, expm1 and hypot are all part of the C99 standard but not yet C++. The user needs the functions log1p and hypot for the complex number inverse trigonometric functions.

The functions implemented can throw standard exceptions, but no exception specification has been made.

Header Files

The interface and implementation for each function (or forms of a function) are both supplied by one header file:

  • acosh.hpp
  • asinh.hpp
  • atanh.hpp
  • expm1.hpp
  • hypot.hpp
  • log1p.hpp
  • sinc.hpp
  • sinhc.hpp

Synopsis

namespace boost{ namespace math
{
template<typename T>
T acosh(const T x);

template<typename T>
T asinh(const T x);

template<typename T>
T atanh(const T x);

template<typename T>
T expm1(const T x);

template<typename T>
T hypot(const T x);

template<typename T>
T log1p(const T x);

template<typename T>
T sinc_pi(const T x);

template<typename T, template<typename> class U>
U<T> sinc_pi(const U<T> x);

template<typename T>
T sinhc_pi(const T x);

template<typename T, template<typename> class U>
U<T> sinhc_pi(const U<T> x);

}
}

acosh

template<typename T>
T acosh(const T x);

Computes the reciprocal of (the restriction to the range of [0;+ ∞ [) the hyperbolic cosine function, at x. Values returned are positive. Use the generalized Taylor series near 1 and Laurent series near infinity to ensure accuracy.

If x is in the range ]- ∞ ;+1[ a quiet NaN is returned (if the system allows, otherwise a domain_error exception is generated).

asinh

template<typename T>
T asinh(const T x);

Computes the reciprocal of the hyperbolic sine function. Use Taylor series at the origin and Laurent series near infinity to ensure accuracy.

atanh

template<typename T>
T atanh(const T x);

Computes the reciprocal of the hyperbolic tangent function, at x. Use Taylor series at the origin to ensure accuracy.

If x is in the range ]- ∞ ;-1[ or in the range ]+1;+ ∞ [ a quiet NaN is returned (if the system allows, otherwise a domain_error exception is generated).

If x is in the range `[-1;-1+ ε [, minus infinity is returned (if the system allows, otherwise an out_of_range exception is generated), with ε denoting numeric_limits<T>::epsilon().

If x is in the range ]+1- ε ;+1], plus infinity is returned (if the system allows, otherwise an out_of_range exception is generated), with ε denoting numeric_limits<T>::epsilon().

expm1

template <class T>
T expm1(T t);

Return value: This function returns ex - 1 .

For small x, then ex is very close to 1, as a result calculating ex - 1 results in catastrophic cancellation errors when x is small. expm1 calculates ex - 1 using a series expansion when x is small (giving an accuracy of less than 2ε ).

Finally when BOOST_HAS_EXPM1 is defined then the float/double/long double specializations of this template forward to the platform's native implementation of this function.

hypot

template <class T>
T hypot(T x, T y);

Return value: The function computes hypot avoiding any undue underflow and overflow.

When calculating hypot it is quite easy for the intermediate terms to either overflow or underflow, even though the result is representable. One possible alternative form is hypot2, but that can overflow or underflow if x and y are of very differing magnitudes. The hypot function takes care of all the special cases for the user.

log1p

template <class T>
T log1p(T x);

Return value: This function returns the natural logarithm of x+1.

There are many situations where it is desirable to compute log(x+1). However, for small x then x+1 suffers from catastrophic cancellation errors so that x+1 == 1 and log(x+1) == 0, when in fact for very small x, the best approximation to log(x+1) would be x. log1p calculates the best approximation to log(1+x) using a Taylor series expansion for accuracy (less than 2ε ). Note that there are faster methods available, for example using the equivalence:

log(1+x) == (log(1+x) * x) / ((1-x) - 1)

However, experience has shown that these methods tend to fail once the compiler's optimizations are turned on. In contrast, the series expansion method seems to be reasonably immune to optimizer-induced errors.

Finally when BOOST_HAS_LOG1P is defined then the float/double/long double specializations of this template simply forward to the platform's native implementation of this function.

sinc_pi

template<typename T>
T sinc_pi(const T x);

template<typename T, template<typename> class U>
U<T> sinc_pi(const U<T> x);

Computes the Sinus Cardinal of x. The second form is for complexes, quaternions, octonions. Use the Taylor series at the origin to ensure accuracy.

sinhc_pi

template<typename T>
T sinhc_pi(const T x);

template<typename T, template<typename> class U>
U<T> sinhc_pi(const U<T> x);

Computes the Hyperbolic Sinus Cardinal of x. The second form is for complexes, quaternions, octonions. Use the Taylor series at the origin to ensure accuracy.

Copyright © 2001 -2002 Daryle Walker, 2001-2003 Hubert Holin, 2005 John Maddock


Math Special Functions

Name

Math special functions : The Special Functions library currently provides eight templated special functions, in namespace boost. Two of these (sinc_pi and sinhc_pi) are required for implementations of quaternions and octonions.

Synopsis

namespace boost{ namespace math
{
template<typename T>
T acosh(const T x);

template<typename T>
T asinh(const T x);

template<typename T>
T atanh(const T x);

template<typename T>
T expm1(const T x);

template<typename T>
T hypot(const T x);

template<typename T>
T log1p(const T x);

template<typename T>
T sinc_pi(const T x);

template<typename T, template<typename> class U>
U<T> sinc_pi(const U<T> x);

template<typename T>
T sinhc_pi(const T x);

template<typename T, template<typename> class U>
U<T> sinhc_pi(const U<T> x);

}
}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost: mem_fn.hpp documentation

mem_fn.hpp

Purpose

Frequently Asked Questions

Interface

Synopsis

Common requirements

get_pointer

mem_fn

Implementation

Files

Dependencies

Number of Arguments

"__stdcall", "__cdecl" and "__fastcall" Support

Purpose

boost::mem_fn is a generalization of the standard functions std::mem_fun and std::mem_fun_ref. It supports member function pointers with more than one argument, and the returned function object can take a pointer, a reference, or a smart pointer to an object instance as its first argument. mem_fn also supports pointers to data members by treating them as functions taking no arguments and returning a (const) reference to the member.

The purpose of mem_fn is twofold. First, it allows users to invoke a member function on a container with the familiar syntax, even when the container stores smart pointers.

    std::for_each(v.begin(), v.end(), boost::mem_fn(&Shape::draw));

Second, it can be used as a building block by library developers that want to treat a pointer to member function as a function object. A library might define an enhanced for_each algorithm with an overload of the form:

template<class It, class R, class T> void for_each(It first, It last, R (T::*pmf) ())
{
    std::for_each(first, last, boost::mem_fn(pmf));
}

that will allow the convenient syntax:

    for_each(v.begin(), v.end(), &Shape::draw);

When documenting the feature, the library author will simply state:

template<class It, class R, class T> void for_each(It first, It last, R (T::*pmf) ());

Effects: equivalent to std::for_each(first, last, boost::mem_fn(pmf));

where boost::mem_fn can be a link to this page. See the documentation of bind for an example.

mem_fn takes one argument, a pointer to a member, and returns a function object suitable for use with standard or user-defined algorithms:

struct X
{
    void f();
};

void g(std::vector<X> & v)
{
    std::for_each(v.begin(), v.end(), boost::mem_fn(&X::f));
};

void h(std::vector<X *> const & v)
{
    std::for_each(v.begin(), v.end(), boost::mem_fn(&X::f));
};

void k(std::vector<boost::shared_ptr<X> > const & v)
{
    std::for_each(v.begin(), v.end(), boost::mem_fn(&X::f));
};

The returned function object takes the same arguments as the input member function plus a "flexible" first argument that represents the object instance.

When the function object is invoked with a first argument x that is neither a pointer nor a reference to the appropriate class (X in the example above), it uses get_pointer(x) to obtain a pointer from x. Library authors can "register" their smart pointer classes by supplying an appropriate get_pointer overload, allowing mem_fn to recognize and support them.

[Note: get_pointer is not restricted to return a pointer. Any object that can be used in a member function call expression (x->*pmf)(...) will work.]

[Note: the library uses an unqualified call to get_pointer. Therefore, it will find, through argument-dependent lookup, get_pointer overloads that are defined in the same namespace as the corresponding smart pointer class, in addition to any boost::get_pointer overloads.]

All function objects returned by mem_fn expose a result_type typedef that represents the return type of the member function. For data members, result_type is defined as the type of the member.

Frequently Asked Questions

Can mem_fn be used instead of the standard std::mem_fun[_ref] adaptors?

Yes. For simple uses, mem_fn provides additional functionality that the standard adaptors do not. Complicated expressions that use std::bind1st, std::bind2nd or Boost.Compose along with the standard adaptors can be rewritten using boost::bind that automatically takes advantage of mem_fn.

Should I replace every occurence of std::mem_fun[_ref] with mem_fn in my existing code?

No, unless there is a good reason to do so. mem_fn is not 100% compatible with the standard adaptors, although it comes pretty close. In particular, mem_fn does not return objects of type std::[const_]mem_fun[1][_ref]_t, as the standard adaptors do, and it is not possible to fully describe the type of the first argument using the standard argument_type and first_argument_type nested typedefs. Libraries that need adaptable function objects in order to function might not like mem_fn.

Does mem_fn work with COM methods?

Yes, if the user #define BOOST_MEM_FN_ENABLE_STDCALL.

Why isn't BOOST_MEM_FN_ENABLE_STDCALL defined automatically?

Non-portable extensions, in general, should default to off to prevent vendor lock-in. Had BOOST_MEM_FN_ENABLE_STDCALL been defined automatically, the user could have accidentally taken advantage of it without realizing that thecode is, perhaps, no longer portable. In addition, it is possible for the default calling convention to be __stdcall, in which case enabling __stdcall support will result in duplicate definitions.

Interface

Synopsis

namespace boost
{

template<class T> T * get_pointer(T * p);

template<class R, class T> unspecified-1 mem_fn(R (T::*pmf) ());

template<class R, class T> unspecified-2 mem_fn(R (T::*pmf) () const);

template<class R, class T> unspecified-2-1 mem_fn(R T::*pm);

template<class R, class T, class A1> unspecified-3 mem_fn(R (T::*pmf) (A1));

template<class R, class T, class A1> unspecified-4 mem_fn(R (T::*pmf) (A1) const);

template<class R, class T, class A1, class A2> unspecified-5 mem_fn(R (T::*pmf) (A1, A2));

template<class R, class T, class A1, class A2> unspecified-6 mem_fn(R (T::*pmf) (A1, A2) const);

// implementation defined number of additional overloads for more arguments

}

Common requirements

All unspecified-N types mentioned in the Synopsis are CopyConstructible and Assignable. Their copy constructors and assignment operators do not throw exceptions. unspecified-N::result_type is defined as the return type of the member function pointer passed as an argument to mem_fn (R in the Synopsis.) unspecified-2-1::result_type is defined as R.

get_pointer

template<class T> T * get_pointer(T * p)

Returns: p.

Throws: Nothing

mem_fn

template<class R, class T> unspecified-1 mem_fn(R (T::*pmf) ())

Returns: a function object f such that the expression f(t) is equivalent to (t.*pmf)() when t is an l-value of type T or derived, (get_pointer(t)->*pmf)() otherwise.

Throws: Nothing

template<class R, class T> unspecified-2 mem_fn(R (T::*pmf) () const)

Returns: a function object f such that the expression f(t) is equivalent to (t.*pmf)() when t is of type T [const] or derived, (get_pointer(t)->*pmf)() otherwise.

Throws: Nothing

template<class R, class T> unspecified-2-1 mem_fn(R T::*pm)

Returns: a function object f such that the expression f(t) is equivalent to t.*pm when t is of type T [const] or derived, get_pointer(t)->*pm otherwise.

Throws: Nothing

template<class R, class T, class A1> unspecified-3 mem_fn(R (T::*pmf) (A1))

Returns: a function object f such that the expression f(t, a1) is equivalent to (t.*pmf)(a1) when t is an l-value of type T or derived, (get_pointer(t)->*pmf)(a1) otherwise.

Throws: Nothing

template<class R, class T, class A1> unspecified-4 mem_fn(R (T::*pmf) (A1) const)

Returns: a function object f such that the expression f(t, a1) is equivalent to (t.*pmf)(a1) when t is of type T [const] or derived, (get_pointer(t)->*pmf)(a1) otherwise.

Throws: Nothing.

template<class R, class T, class A1, class A2> unspecified-5 mem_fn(R (T::*pmf) (A1, A2))

Returns: a function object f such that the expression f(t, a1, a2) is equivalent to (t.*pmf)(a1, a2) when t is an l-value of type T or derived, (get_pointer(t)->*pmf)(a1, a2) otherwise.

Throws: Nothing.

template<class R, class T, class A1, class A2> unspecified-6 mem_fn(R (T::*pmf) (A1, A2) const)

Returns: a function object f such that the expression f(t, a1, a2) is equivalent to (t.*pmf)(a1, a2) when t is of type T [const] or derived, (get_pointer(t)->*pmf)(a1, a2) otherwise.

Throws: Nothing.

Implementation

Files

  • boost/mem_fn.hpp (main header)

  • boost/bind/mem_fn_cc.hpp (used by mem_fn.hpp, do not include directly)

  • boost/bind/mem_fn_vw.hpp (used by mem_fn.hpp, do not include directly)

  • boost/bind/mem_fn_template.hpp (used by mem_fn.hpp, do not include directly)

  • libs/bind/test/mem_fn_test.cpp (test)

  • libs/bind/test/mem_fn_derived_test.cpp (test with derived objects)

  • libs/bind/test/mem_fn_fastcall_test.cpp (test for __fastcall)

  • libs/bind/test/mem_fn_stdcall_test.cpp (test for __stdcall)

  • libs/bind/test/mem_fn_void_test.cpp (test for void returns)

Dependencies

  • Boost.Config

Number of Arguments

This implementation supports member functions with up to eight arguments. This is not an inherent limitation of the design, but an implementation detail.

"__stdcall", "__cdecl" and "__fastcall" Support

Some platforms allow several types of member functions that differ by their calling convention (the rules by which the function is invoked: how are arguments passed, how is the return value handled, and who cleans up the stack - if any.)

For example, Windows API functions and COM interface member functions use a calling convention known as __stdcall. Borland VCL components use __fastcall. UDK, the component model of OpenOffice.org, uses __cdecl.

To use mem_fn with __stdcall member functions, #define the macro BOOST_MEM_FN_ENABLE_STDCALL before including, directly or indirectly, <boost/mem_fn.hpp>.

To use mem_fn with __fastcall member functions, #define the macro BOOST_MEM_FN_ENABLE_FASTCALL before including <boost/mem_fn.hpp>.

To use mem_fn with __cdecl member functions, #define the macro BOOST_MEM_FN_ENABLE_CDECL before including <boost/mem_fn.hpp>.

It is best to define these macros in the project options, via -D on the command line, or as the first line in the translation unit (.cpp file) where mem_fn is used. Not following this rule can lead to obscure errors when a header includes mem_fn.hpp before the macro has been defined.

[Note: this is a non-portable extension. It is not part of the interface.]

[Note: Some compilers provide only minimal support for the __stdcall keyword.]

Copyright © 2001, 2002 by Peter Dimov and Multi Media Ltd. Copyright 2003-2005 Peter Dimov. Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


Miscellaneous Notes

Boost.Function vs. Function Pointers

Performance

Combatting virtual function "bloat"

Boost.Function vs. Function Pointers

Boost.Function has several advantages over function pointers, namely:

  • Boost.Function allows arbitrary compatible function objects to be targets (instead of requiring an exact function signature).

  • Boost.Function may be used with argument-binding and other function object construction libraries.

  • Boost.Function has predictible behavior when an empty function object is called.

And, of course, function pointers have several advantages over Boost.Function:

  • Function pointers are smaller (the size of one pointer instead of three)

  • Function pointers are faster (Boost.Function may require two calls through function pointers)

  • Function pointers are backward-compatible with C libraries.

  • More readable error messages.

Performance

Function object wrapper size

Copying efficiency

Invocation efficiency

Function object wrapper size

Function object wrappers will be the size of two function pointers plus one function pointer or data pointer (whichever is larger). On common 32-bit platforms, this amounts to 12 bytes per wrapper. Additionally, the function object target will be allocated on the heap.

Copying efficiency

Copying function object wrappers may require allocating memory for a copy of the function object target. The default allocator may be replaced with a faster custom allocator or one may choose to allow the function object wrappers to only store function object targets by reference (using ref) if the cost of this cloning becomes prohibitive.

Invocation efficiency

With a properly inlining compiler, an invocation of a function object requires one call through a function pointer. If the call is to a free function pointer, an additional call must be made to that function pointer (unless the compiler has very powerful interprocedural analysis).

Combatting virtual function "bloat"

The use of virtual functions tends to cause 'code bloat' on many compilers. When a class contains a virtual function, it is necessary to emit an additional function that classifies the type of the object. These auxiliary functions increase the size of the executable significantly when many boost::function objects are used.

In Boost.Function, an alternative but equivalent approach was taken using free functions instead of virtual functions. The Boost.Function object essentially holds two pointers to make a valid target call: a void pointer to the function object it contains and a void pointer to an "invoker" that can call the function object, given the function pointer. This invoker function performs the argument and return value conversions Boost.Function provides. A third pointer points to a free function called the "manager", which handles the cloning and destruction of function objects. The scheme is typesafe because the only functions that actually handle the function object, the invoker and the manager, are instantiated given the type of the function object, so they can safely cast the incoming void pointer (the function object pointer) to the appropriate type.

Copyright © 2001-2004 Douglas Gregor


Octonions

Overview
Header File
Synopsis
Template Class octonion
Octonion Specializations
Octonion Member Typedefs
Octonion Member Functions
Octonion Non-Member Operators
Octonion Value Operations
Quaternion Creation Functions
Octonions Transcendentals

Overview

Octonions, like quaternions, are a relative of complex numbers.

Octonions see some use in theoretical physics.

In practical terms, an octonion is simply an octuple of real numbers ( α,β,γ,δ,ε,ζ,η,θ ), which we can write in the form o = α + βi + γj + δk + εe' + ζi' + ηj' + θk' , where i, j and k are the same objects as for quaternions, and e', i', j' and k' are distinct objects which play essentially the same kind of role as i (or j or k).

Addition and a multiplication is defined on the set of octonions, which generalize their quaternionic counterparts. The main novelty this time is that the multiplication is not only not commutative, is now not even associative (i.e. there are quaternions x, y and z such that x(yz) ≠ (xy)z). A way of remembering things is by using the following multiplication table:

Octonions (and their kin) are described in far more details in this other document (with errata and addenda).

Some traditional constructs, such as the exponential, carry over without too much change into the realms of octonions, but other, such as taking a square root, do not (the fact that the exponential has a closed form is a result of the author, but the fact that the exponential exists at all for octonions is known since quite a long time ago).

Header File

The interface and implementation are both supplied by the header file octonion.hpp.

Synopsis

namespace boost{ namespace math{

template<typename T> class octonion;
template<>           class octonion<float>;
template<>           class octonion<double>;
template<>           class octonion<long double>;

// operators

template<typename T> octonion<T> operator + (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator + (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator + (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator - (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator - (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator - (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator * (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator * (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator * (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator / (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator / (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator / (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator + (octonion<T> const & o);
template<typename T> octonion<T> operator - (octonion<T> const & o);

template<typename T> bool operator == (T const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, T const & rhs);
template<typename T> bool operator == (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator == (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> bool operator != (T const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, T const & rhs);
template<typename T> bool operator != (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator != (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T, typename charT, class traits>
::std::basic_istream<charT,traits> & operator >> (::std::basic_istream<charT,traits> & is, octonion<T> & o);

template<typename T, typename charT, class traits>
::std::basic_ostream<charT,traits> & operator << (::std::basic_ostream<charT,traits> & os, octonion<T> const & o);

// values

template<typename T> T           real(octonion<T> const & o);
template<typename T> octonion<T> unreal(octonion<T> const & o);

template<typename T> T           sup(octonion<T> const & o);
template<typename T> T           l1(octonion<T>const & o);
template<typename T> T           abs(octonion<T> const & o);
template<typename T> T           norm(octonion<T>const  & o);
template<typename T> octonion<T> conj(octonion<T> const & o);

template<typename T> octonion<T> spherical(T const & rho, T const & theta, T const & phi1, T const & phi2, T const & phi3, T const & phi4, T const & phi5, T const & phi6);
template<typename T> octonion<T> multipolar(T const & rho1, T const & theta1, T const & rho2, T const & theta2, T const & rho3, T const & theta3, T const & rho4, T const & theta4);
template<typename T> octonion<T> cylindrical(T const & r, T const & angle, T const & h1, T const & h2, T const & h3, T const & h4, T const & h5, T const & h6);

// transcendentals

template<typename T> octonion<T> exp(octonion<T> const & o);
template<typename T> octonion<T> cos(octonion<T> const & o);
template<typename T> octonion<T> sin(octonion<T> const & o);
template<typename T> octonion<T> tan(octonion<T> const & o);
template<typename T> octonion<T> cosh(octonion<T> const & o);
template<typename T> octonion<T> sinh(octonion<T> const & o);
template<typename T> octonion<T> tanh(octonion<T> const & o);

template<typename T> octonion<T> pow(octonion<T> const & o, int n);

}  } // namespaces

Template Class octonion

namespace boost{ namespace math {
  template<typename T>
class octonion
{
public:
   typedef T value_type;

   explicit  octonion(T const & requested_a = T(), T const & requested_b = T(), T const & requested_c = T(), T const & requested_d = T(), T const & requested_e = T(), T const & requested_f = T(), T const & requested_g = T(), T const & requested_h = T());
   explicit  octonion(::std::complex<T> const & z0, ::std::complex<T> const & z1 = ::std::complex<T>(), ::std::complex<T> const & z2 = ::std::complex<T>(), ::std::complex<T> const & z3 = ::std::complex<T>());
   explicit  octonion(::boost::math::quaternion<T> const & q0, ::boost::math::quaternion<T> const & q1 = ::boost::math::quaternion<T>());
   template<typename X>
   explicit  octonion(octonion<X> const & a_recopier);

   T                             real() const;
   octonion<T>                   unreal() const;

   T                             R_component_1() const;
   T                             R_component_2() const;
   T                             R_component_3() const;
   T                             R_component_4() const;
   T                             R_component_5() const;
   T                             R_component_6() const;
   T                             R_component_7() const;
   T                             R_component_8() const;

   ::std::complex<T>             C_component_1() const;
   ::std::complex<T>             C_component_2() const;
   ::std::complex<T>             C_component_3() const;
   ::std::complex<T>             C_component_4() const;

   ::boost::math::quaternion<T>  H_component_1() const;
   ::boost::math::quaternion<T>  H_component_2() const;

   octonion<T> & operator = (octonion<T> const  & a_affecter);
   template<typename X>
   octonion<T> & operator = (octonion<X> const  & a_affecter);
   octonion<T> & operator = (T const  & a_affecter);
   octonion<T> & operator = (::std::complex<T> const & a_affecter);
   octonion<T> & operator = (::boost::math::quaternion<T> const & a_affecter);

   octonion<T> & operator += (T const & rhs);
   octonion<T> & operator += (::std::complex<T> const & rhs);
   octonion<T> & operator += (::boost::math::quaternion<T> const & rhs);
   template<typename X>
   octonion<T> & operator += (octonion<X> const & rhs);

   octonion<T> & operator -= (T const & rhs);
   octonion<T> & operator -= (::std::complex<T> const & rhs);
   octonion<T> & operator -= (::boost::math::quaternion<T> const & rhs);
   template<typename X>
   octonion<T> & operator -= (octonion<X> const & rhs);

   octonion<T> & operator *= (T const & rhs);
   octonion<T> & operator *= (::std::complex<T> const & rhs);
   octonion<T> & operator *= (::boost::math::quaternion<T> const & rhs);
   template<typename X>
   octonion<T> & operator *= (octonion<X> const & rhs);

   octonion<T> & operator /= (T const & rhs);
   octonion<T> & operator /= (::std::complex<T> const & rhs);
   octonion<T> & operator /= (::boost::math::quaternion<T> const & rhs);
   template<typename X>
   octonion<T> & operator /= (octonion<X> const & rhs);
};

} } // namespaces

Octonion Specializations

namespace boost{ namespace math{

template<>
class octonion<float>
{
public:
   typedef float value_type;

   explicit  octonion(float const & requested_a = 0.0f, float const & requested_b = 0.0f, float const & requested_c = 0.0f, float const & requested_d = 0.0f, float const & requested_e = 0.0f, float const & requested_f = 0.0f, float const & requested_g = 0.0f, float const & requested_h = 0.0f);
   explicit  octonion(::std::complex<float> const & z0, ::std::complex<float> const & z1 = ::std::complex<float>(), ::std::complex<float> const & z2 = ::std::complex<float>(), ::std::complex<float> const & z3 = ::std::complex<float>());
   explicit  octonion(::boost::math::quaternion<float> const & q0, ::boost::math::quaternion<float> const & q1 = ::boost::math::quaternion<float>());
   explicit  octonion(octonion<double> const & a_recopier);
   explicit  octonion(octonion<long double> const & a_recopier);

   float                             real() const;
   octonion<float>                   unreal() const;

   float                             R_component_1() const;
   float                             R_component_2() const;
   float                             R_component_3() const;
   float                             R_component_4() const;
   float                             R_component_5() const;
   float                             R_component_6() const;
   float                             R_component_7() const;
   float                             R_component_8() const;

   ::std::complex<float>             C_component_1() const;
   ::std::complex<float>             C_component_2() const;
   ::std::complex<float>             C_component_3() const;
   ::std::complex<float>             C_component_4() const;

   ::boost::math::quaternion<float>  H_component_1() const;
   ::boost::math::quaternion<float>  H_component_2() const;

   octonion<float> & operator = (octonion<float> const  & a_affecter);
   template<typename X>
   octonion<float> & operator = (octonion<X> const  & a_affecter);
   octonion<float> & operator = (float const  & a_affecter);
   octonion<float> & operator = (::std::complex<float> const & a_affecter);
   octonion<float> & operator = (::boost::math::quaternion<float> const & a_affecter);

   octonion<float> & operator += (float const & rhs);
   octonion<float> & operator += (::std::complex<float> const & rhs);
   octonion<float> & operator += (::boost::math::quaternion<float> const & rhs);
   template<typename X>
   octonion<float> & operator += (octonion<X> const & rhs);

   octonion<float> & operator -= (float const & rhs);
   octonion<float> & operator -= (::std::complex<float> const & rhs);
   octonion<float> & operator -= (::boost::math::quaternion<float> const & rhs);
   template<typename X>
   octonion<float> & operator -= (octonion<X> const & rhs);

   octonion<float> & operator *= (float const & rhs);
   octonion<float> & operator *= (::std::complex<float> const & rhs);
   octonion<float> & operator *= (::boost::math::quaternion<float> const & rhs);
   template<typename X>
   octonion<float> & operator *= (octonion<X> const & rhs);

   octonion<float> & operator /= (float const & rhs);
   octonion<float> & operator /= (::std::complex<float> const & rhs);
   octonion<float> & operator /= (::boost::math::quaternion<float> const & rhs);
   template<typename X>
   octonion<float> & operator /= (octonion<X> const & rhs);
};

template<>
class octonion<double>
{
public:
   typedef double value_type;

   explicit  octonion(double const & requested_a = 0.0, double const & requested_b = 0.0, double const & requested_c = 0.0, double const & requested_d = 0.0, double const & requested_e = 0.0, double const & requested_f = 0.0, double const & requested_g = 0.0, double const & requested_h = 0.0);
   explicit  octonion(::std::complex<double> const & z0, ::std::complex<double> const & z1 = ::std::complex<double>(), ::std::complex<double> const & z2 = ::std::complex<double>(), ::std::complex<double> const & z3 = ::std::complex<double>());
   explicit  octonion(::boost::math::quaternion<double> const & q0, ::boost::math::quaternion<double> const & q1 = ::boost::math::quaternion<double>());
   explicit  octonion(octonion<float> const & a_recopier);
   explicit  octonion(octonion<long double> const & a_recopier);

   double                             real() const;
   octonion<double>                   unreal() const;

   double                             R_component_1() const;
   double                             R_component_2() const;
   double                             R_component_3() const;
   double                             R_component_4() const;
   double                             R_component_5() const;
   double                             R_component_6() const;
   double                             R_component_7() const;
   double                             R_component_8() const;

   ::std::complex<double>             C_component_1() const;
   ::std::complex<double>             C_component_2() const;
   ::std::complex<double>             C_component_3() const;
   ::std::complex<double>             C_component_4() const;

   ::boost::math::quaternion<double>  H_component_1() const;
   ::boost::math::quaternion<double>  H_component_2() const;

   octonion<double> & operator = (octonion<double> const  & a_affecter);
   template<typename X>
   octonion<double> & operator = (octonion<X> const  & a_affecter);
   octonion<double> & operator = (double const  & a_affecter);
   octonion<double> & operator = (::std::complex<double> const & a_affecter);
   octonion<double> & operator = (::boost::math::quaternion<double> const & a_affecter);

   octonion<double> & operator += (double const & rhs);
   octonion<double> & operator += (::std::complex<double> const & rhs);
   octonion<double> & operator += (::boost::math::quaternion<double> const & rhs);
   template<typename X>
   octonion<double> & operator += (octonion<X> const & rhs);

   octonion<double> & operator -= (double const & rhs);
   octonion<double> & operator -= (::std::complex<double> const & rhs);
   octonion<double> & operator -= (::boost::math::quaternion<double> const & rhs);
   template<typename X>
   octonion<double> & operator -= (octonion<X> const & rhs);

   octonion<double> & operator *= (double const & rhs);
   octonion<double> & operator *= (::std::complex<double> const & rhs);
   octonion<double> & operator *= (::boost::math::quaternion<double> const & rhs);
   template<typename X>
   octonion<double> & operator *= (octonion<X> const & rhs);

   octonion<double> & operator /= (double const & rhs);
   octonion<double> & operator /= (::std::complex<double> const & rhs);
   octonion<double> & operator /= (::boost::math::quaternion<double> const & rhs);
   template<typename X>
   octonion<double> & operator /= (octonion<X> const & rhs);
};

template<>
class octonion<long double>
{
public:
   typedef long double value_type;

   explicit   octonion(long double const & requested_a = 0.0L, long double const & requested_b = 0.0L, long double const & requested_c = 0.0L, long double const & requested_d = 0.0L, long double const & requested_e = 0.0L, long double const & requested_f = 0.0L, long double const & requested_g = 0.0L, long double const & requested_h = 0.0L);
   explicit   octonion( ::std::complex<long double> const & z0, ::std::complex<long double> const & z1 = ::std::complex<long double>(), ::std::complex<long double> const & z2 = ::std::complex<long double>(), ::std::complex<long double> const & z3 = ::std::complex<long double>());
   explicit   octonion( ::boost::math::quaternion<long double> const & q0, ::boost::math::quaternion<long double> const & z1 = ::boost::math::quaternion<long double>());
   explicit   octonion(octonion<float> const & a_recopier);
   explicit   octonion(octonion<double> const & a_recopier);

   long double                             real() const;
   octonion<long double>                   unreal() const;

   long double                             R_component_1() const;
   long double                             R_component_2() const;
   long double                             R_component_3() const;
   long double                             R_component_4() const;
   long double                             R_component_5() const;
   long double                             R_component_6() const;
   long double                             R_component_7() const;
   long double                             R_component_8() const;

   ::std::complex<long double>             C_component_1() const;
   ::std::complex<long double>             C_component_2() const;
   ::std::complex<long double>             C_component_3() const;
   ::std::complex<long double>             C_component_4() const;

   ::boost::math::quaternion<long double>  H_component_1() const;
   ::boost::math::quaternion<long double>  H_component_2() const;

   octonion<long double> & operator = (octonion<long double> const  & a_affecter);
   template<typename X>
   octonion<long double> & operator = (octonion<X> const  & a_affecter);
   octonion<long double> & operator = (long double const  & a_affecter);
   octonion<long double> & operator = (::std::complex<long double> const & a_affecter);
   octonion<long double> & operator = (::boost::math::quaternion<long double> const & a_affecter);

   octonion<long double> & operator += (long double const & rhs);
   octonion<long double> & operator += (::std::complex<long double> const & rhs);
   octonion<long double> & operator += (::boost::math::quaternion<long double> const & rhs);
   template<typename X>
   octonion<long double> & operator += (octonion<X> const & rhs);

   octonion<long double> & operator -= (long double const & rhs);
   octonion<long double> & operator -= (::std::complex<long double> const & rhs);
   octonion<long double> & operator -= (::boost::math::quaternion<long double> const & rhs);
   template<typename X>
   octonion<long double> & operator -= (octonion<X> const & rhs);

   octonion<long double> & operator *= (long double const & rhs);
   octonion<long double> & operator *= (::std::complex<long double> const & rhs);
   octonion<long double> & operator *= (::boost::math::quaternion<long double> const & rhs);
   template<typename X>
   octonion<long double> & operator *= (octonion<X> const & rhs);

   octonion<long double> & operator /= (long double const & rhs);
   octonion<long double> & operator /= (::std::complex<long double> const & rhs);
   octonion<long double> & operator /= (::boost::math::quaternion<long double> const & rhs);
   template<typename X>
   octonion<long double> & operator /= (octonion<X> const & rhs);
};

} } // namespaces

Octonion Member Typedefs

        value_type

        Template version:

typedef T value_type;

        Float specialization version:

typedef float value_type;

        Double specialization version:

typedef double value_type;

        Long double specialization version:

typedef long double value_type;

        These provide easy access to the type the template is built upon.

Octonion Member Functions

        Constructors

        Template version:

explicit  octonion(T const & requested_a = T(), T const & requested_b = T(), T const & requested_c = T(), T const & requested_d = T(), T const & requested_e = T(), T const & requested_f = T(), T const & requested_g = T(), T const & requested_h = T());
explicit  octonion(::std::complex<T> const & z0, ::std::complex<T> const & z1 = ::std::complex<T>(), ::std::complex<T> const & z2 = ::std::complex<T>(), ::std::complex<T> const & z3 = ::std::complex<T>());
explicit  octonion(::boost::math::quaternion<T> const & q0, ::boost::math::quaternion<T> const & q1 = ::boost::math::quaternion<T>());
template<typename X>
explicit octonion(octonion<X> const & a_recopier);

        Float specialization version:

explicit  octonion(float const & requested_a = 0.0f, float const & requested_b = 0.0f, float const & requested_c = 0.0f, float const & requested_d = 0.0f, float const & requested_e = 0.0f, float const & requested_f = 0.0f, float const & requested_g = 0.0f, float const & requested_h = 0.0f);
explicit  octonion(::std::complex<float> const & z0, ::std::complex<float> const & z1 = ::std::complex<float>(), ::std::complex<float> const & z2 = ::std::complex<float>(), ::std::complex<float> const & z3 = ::std::complex<float>());
explicit  octonion(::boost::math::quaternion<float> const & q0, ::boost::math::quaternion<float> const & q1 = ::boost::math::quaternion<float>());
explicit  octonion(octonion<double> const & a_recopier);
explicit  octonion(octonion<long double> const & a_recopier);

        Double specialization version:

explicit  octonion(double const & requested_a = 0.0, double const & requested_b = 0.0, double const & requested_c = 0.0, double const & requested_d = 0.0, double const & requested_e = 0.0, double const & requested_f = 0.0, double const & requested_g = 0.0, double const & requested_h = 0.0);
explicit  octonion(::std::complex<double> const & z0, ::std::complex<double> const & z1 = ::std::complex<double>(), ::std::complex<double> const & z2 = ::std::complex<double>(), ::std::complex<double> const & z3 = ::std::complex<double>());
explicit  octonion(::boost::math::quaternion<double> const & q0, ::boost::math::quaternion<double> const & q1 = ::boost::math::quaternion<double>());
explicit  octonion(octonion<float> const & a_recopier);
explicit  octonion(octonion<long double> const & a_recopier);

        Long double specialization version:

explicit  octonion(long double const & requested_a = 0.0L, long double const & requested_b = 0.0L, long double const & requested_c = 0.0L, long double const & requested_d = 0.0L, long double const & requested_e = 0.0L, long double const & requested_f = 0.0L, long double const & requested_g = 0.0L, long double const & requested_h = 0.0L);
explicit  octonion( ::std::complex<long double> const & z0, ::std::complex<long double> const & z1 = ::std::complex<long double>(), ::std::complex<long double> const & z2 = ::std::complex<long double>(), ::std::complex<long double> const & z3 = ::std::complex<long double>());
explicit  octonion(::boost::math::quaternion<long double> const & q0, ::boost::math::quaternion<long double> const & q1 = ::boost::math::quaternion<long double>());
explicit  octonion(octonion<float> const & a_recopier);
explicit  octonion(octonion<double> const & a_recopier);

        A default constructor is provided for each form, which initializes each component
        to the default values for their type (i.e. zero for floating numbers). This
        constructor also accepts one to eight base type arguments. A constructor
        is also provided to build octonions from one to four complex numbers sharing
        the same base type, and another taking one or two quaternions sharing the
        same base type. The unspecialized template also sports a templatized copy
        constructor, while the specialized forms have copy constructors from the
        other two specializations, which are explicit when a risk of precision loss
        exists. For the unspecialized form, the base type's constructors must not
        throw.

        Destructors and untemplated copy constructors (from the same type) are provided
        by the compiler. Converting copy constructors make use of a templated helper
        function in a "detail" subnamespace.

        Other member functions

        Real and Unreal Parts

T            real()   const;
octonion<T>  unreal() const;

        Like complex number, octonions do have a meaningful notion of "real
        part", but unlike them there is no meaningful notion of "imaginary
        part". Instead there is an "unreal part" which itself is a
        octonion, and usually nothing simpler (as opposed to the complex number case).
        These are returned by the first two functions.

        Individual Real Components

T   R_component_1() const;
T   R_component_2() const;
T   R_component_3() const;
T   R_component_4() const;
T   R_component_5() const;
T   R_component_6() const;
T   R_component_7() const;
T   R_component_8() const;

        A octonion having eight real components,
        these are returned by these eight functions. Hence
           real
        and
        R_component_1 return the same
        value.

        Individual Complex Components

::std::complex<T> C_component_1() const;
::std::complex<T> C_component_2() const;
::std::complex<T> C_component_3() const;
::std::complex<T> C_component_4() const;

        A octonion likewise has four complex components. Actually, octonions are
        indeed a (left) vector field over the complexes, but beware, as for any octonion
        o =
α + βi + γj + δk + εe' + ζi' + ηj' + θk'
         we also have o = (
α + βi) + (γ + δi)j + (ε + ζi)e' + (η - θi)j'
         (note the minus sign
        in the last factor). What the    C_component_n functions return, however, are
        the complexes which could be used to build the octonion using the constructor,
        and not the components of the octonion on
        the basis (1, j, e', j').

        Individual Quaternion Components

::boost::math::quaternion<T> H_component_1() const;
::boost::math::quaternion<T> H_component_2() const;

        Likewise, for any octonion o =
α + βi + γj + δk + εe' + ζi' + ηj' + θk'
         we also have o = (
α + βi + γj + δk) + (ε + ζi + ηj - θj)e'
        , though there is no meaningful vector-space-like structure
        based on the quaternions. What the
         H_component_n functions return are the
        quaternions which could be used to build the octonion using the constructor.

        Octonion Member Operators

        Assignment Operators

octonion<T> & operator = (octonion<T> const & a_affecter);
template<typename X>
octonion<T> & operator = (octonion<X> const & a_affecter);
octonion<T> & operator = (T const & a_affecter);
octonion<T> & operator = (::std::complex<T> const & a_affecter);
octonion<T> & operator = (::boost::math::quaternion<T> const & a_affecter);

        These perform the expected assignment, with type modification if necessary
        (for instance, assigning from a base type will set the real part to that
        value, and all other components to zero). For the unspecialized form, the
        base type's assignment operators must not throw.

        Other Member Operators

octonion<T> & operator += (T const & rhs)
octonion<T> & operator += (::std::complex<T> const & rhs);
octonion<T> & operator += (::boost::math::quaternion<T> const & rhs);
template<typename X>
octonion<T> & operator += (octonion<X> const & rhs);

        These perform the mathematical operation
        (*this)+rhs
        and store the result in *this.
        The unspecialized form has exception guards, which the specialized forms
        do not, so as to insure exception safety. For the unspecialized form, the
        base type's assignment operators must not throw.

octonion<T> & operator -= (T const & rhs)
octonion<T> & operator -= (::std::complex<T> const & rhs);
octonion<T> & operator -= (::boost::math::quaternion<T> const & rhs);
template<typename X>
octonion<T> & operator -= (octonion<X> const & rhs);

        These perform the mathematical operation
        (*this)-rhs
        and store the result in *this.
        The unspecialized form has exception guards, which the specialized forms
        do not, so as to insure exception safety. For the unspecialized form, the
        base type's assignment operators must not throw.

octonion<T> & operator *= (T const & rhs)
octonion<T> & operator *= (::std::complex<T> const & rhs);
octonion<T> & operator *= (::boost::math::quaternion<T> const & rhs);
template<typename X>
octonion<T> & operator *= (octonion<X> const & rhs);

        These perform the mathematical operation (*this)*rhs
        in this order (order is important as multiplication is not commutative for
        octonions) and store the result in
        *this. The unspecialized form has exception
        guards, which the specialized forms do not, so as to insure exception safety.
        For the unspecialized form, the base type's assignment operators must not
        throw. Also, for clarity's sake, always group the factors in a
        multiplication by groups of two, as the multiplication is not even associative
        on the octonions (though there are of course cases where this does not matter,
        it usually does).

octonion<T> & operator /= (T const & rhs)
octonion<T> & operator /= (::std::complex<T> const & rhs);
octonion<T> & operator /= (::boost::math::quaternion<T> const & rhs);
template<typename X>
octonion<T> & operator /= (octonion<X> const & rhs);

        These perform the mathematical operation
        (*this)*inverse_of(rhs)
        in this order (order is important as multiplication is not commutative for
        octonions) and store the result in *this. The unspecialized form has exception
        guards, which the specialized forms do not, so as to insure exception safety.
        For the unspecialized form, the base type's assignment operators must not
        throw. As for the multiplication, remember to group any two factors using
        parenthesis.

Octonion Non-Member Operators

        Unary Plus and Minus Operators

template<typename T> octonion<T> operator + (octonion<T> const & o);

        This unary operator simply returns
         o.

template<typename T> octonion<T> operator - (octonion<T> const & o);

        This unary operator returns the opposite of
         o.

        Binary Addition Operators

template<typename T> octonion<T> operator + (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator + (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator + (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, octonion<T> const & rhs);

        These operators return
         octonion<T>(lhs) += rhs.

        Binary Subtraction Operators

template<typename T> octonion<T> operator - (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator - (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator - (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, octonion<T> const & rhs);

        These operators return octonion<T>(lhs) -= rhs.

        Binary Multiplication Operators

template<typename T> octonion<T> operator * (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator * (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator * (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, octonion<T> const & rhs);

        These operators return
         octonion<T>(lhs) *= rhs.

        Binary Division Operators

template<typename T> octonion<T> operator / (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator / (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator / (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, octonion<T> const & rhs);

        These operators return
         octonion<T>(lhs) /= rhs.
        It is an error to divide by zero.

        Binary Equality Operators

template<typename T> bool operator == (T const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, T const & rhs);
template<typename T> bool operator == (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator == (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, octonion<T> const & rhs);

        These return true if and only if the four components of
         octonion<T>(lhs) are
        equal to their counterparts in  octonion<T>(rhs). As
        with any floating-type entity, this is essentially meaningless.

        Binary Inequality Operators

template<typename T> bool operator != (T const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, T const & rhs);
template<typename T> bool operator != (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator != (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, octonion<T> const & rhs);

        These return true if and only if octonion<T>(lhs) == octonion<T>(rhs)
        is
        false. As with any floating-type entity, this is essentially meaningless.

        Stream Extractor

template<typename T, typename charT, class traits>
::std::basic_istream<charT,traits> & operator >> (::std::basic_istream<charT,traits> & is, octonion<T> & o);

        Extracts an octonion o.
        The program accepts any format which seems reasonable. However, since this leads to a
        great many ambiguities, decisions were made to lift these. In case of doubt,
        stick to lists of reals.

        The input values must be convertible to
         T. If bad input is encountered, calls
         is.setstate(ios::failbit) (which may throw
         ios::failure
        (27.4.5.3))returns
         is.

        Stream Inserter

template<typename T, typename charT, class traits>
::std::basic_ostream<charT,traits> & operator << (::std::basic_ostream<charT,traits> & os, octonion<T> const & o);

        Inserts the octonion
         o
        onto
        the stream  os as if it were
        implemented as follows:

template<typename T, typename charT, class traits>
::std::basic_ostream<charT,traits> & operator << ( ::std::basic_ostream<charT,traits> & os,
octonion<T> const & o)
{
   ::std::basic_ostringstream<charT,traits> s;

   s.flags(os.flags());
   s.imbue(os.getloc());
   s.precision(os.precision());

   s << '(' << o.R_component_1() << ','
      << o.R_component_2() << ','
      << o.R_component_3() << ','
      << o.R_component_4() << ','
      << o.R_component_5() << ','
      << o.R_component_6() << ','
      << o.R_component_7() << ','
      << o.R_component_8() << ')';

   return os << s.str();
}

Octonion Value Operations

        Real and Unreal

template<typename T> T  real(octonion<T> const & o);
template<typename T> octonion<T> unreal(octonion<T> const & o);

        These return
         o.real()
        and
         o.unreal()
        respectively.

        conj

template<typename T> octonion<T> conj(octonion<T> const & o);

        This returns the conjugate of the octonion.

        sup

template<typename T> T   sup(octonion<T> const & o);

        This return the
        sup norm (the greatest among
        abs(o.R_component_1())...abs(o.R_component_8())) of the octonion.

        l1

template<typename T> T   l1(octonion<T> const & o);

        This return the
        l1
        norm (abs(o.R_component_1())+...+abs(o.R_component_8())) of the octonion.

        abs

template<typename T> T   abs(octonion<T> const & o);

        This return the magnitude (Euclidian norm) of the octonion.

        norm

template<typename T> T  norm(octonion<T>const  & o);

        This return the (Cayley) norm of the octonion. The term "norm"
        might be confusing, as most people associate it with the Euclidian norm (and
        quadratic functionals). For this version of (the mathematical objects known
        as) octonions, the Euclidian norm (also known as magnitude) is the square
        root of the Cayley norm.

Quaternion Creation Functions

template<typename T> octonion<T> spherical(T const & rho, T const & theta, T const & phi1, T const & phi2, T const & phi3, T const & phi4, T const & phi5, T const & phi6);
template<typename T> octonion<T> multipolar(T const & rho1, T const & theta1, T const & rho2, T const & theta2, T const & rho3, T const & theta3, T const & rho4, T const & theta4);
template<typename T> octonion<T> cylindrical(T const & r, T const & angle, T const & h1, T const & h2, T const & h3, T const & h4, T const & h5, T const & h6);

        These build

octonions in a way similar to the way polar builds complex numbers,
        as there is no strict equivalent to polar coordinates for

octonions.

spherical is a simple transposition
        of polar, it takes as inputs
        a (positive) magnitude and a point on the

hypersphere, given by three angles.
        The first of these,
theta has a natural range of

-pi
        to
+pi, and the other two have natural ranges of

-pi/2 to

+pi/2 (as is the
        case with the usual spherical coordinates in
R3
        ). Due to the many symmetries and periodicities, nothing
        untoward happens if the magnitude is negative or the angles are outside their
        natural ranges. The expected degeneracy (a magnitude of zero ignores the
        angles settings...) do happen however.

cylindrical is likewise a
        simple transposition of the usual cylindrical coordinates in
R3
        , which in turn is another derivative of planar polar
        coordinates. The first two inputs are the polar coordinates of the first
        C component of the
        octonion. The third and fourth inputs are placed into the third and fourth
        R components of the
        octonion, respectively.

multipolar is yet another
        simple generalization of polar coordinates. This time, both C components of the octonion are given
        in polar coordinates.

        In this version of our implementation of

octonions, there is no analogue
        of the complex value operation
arg as the situation is somewhat more complicated.

Octonions Transcendentals

        There is no

log or

sqrt provided for

octonions in this implementation,
        and
pow is likewise restricted
        to integral powers of the exponent. There are several reasons to this: on
        the one hand, the equivalent of analytic continuation for

octonions ("branch
        cuts") remains to be investigated thoroughly and to avoid the confusion
        introduced in the standard by exponentiations of complexes by complexes
        (which is well defined, but not in the standard...). For example, saying
        that
pow(0,0) is "implementation
        defined" is unnecessary.

        However several transcendentals are provided, chief among which is the
        exponential. That it allows for a "closed formula" is a result
        of the author (the existence and definition of the exponential, on the

octonions
        among others, on the other hand, is a few centuries old). Basically, any
        converging power series with real coefficients which allows for a closed
        formula in C can be
        transposed to O. More
        transcendentals of this type could be added in a further revision upon request.
        It should be noted that it is these functions which force the dependency
        upon the boost/math/special_functions/sinc.hpp
        and the boost/math/special_functions/sinhc.hpp
        headers.

        exp

template<typename T>
octonion<T> exp(octonion<T> const & o);

        Computes the exponential of the octonion.

        cos

template<typename T>
octonion<T> cos(octonion<T> const & o);

        Computes the cosine of the octonion

        sin

template<typename T>
octonion<T> sin(octonion<T> const & o);

        Computes the sine of the octonion.

        tan

template<typename T>
octonion<T> tan(octonion<T> const & o);

        Computes the tangent of the octonion.

        cosh

template<typename T>
octonion<T> cosh(octonion<T> const & o);

Computes the hyperbolic cosine of the octonion.

        sinh

template<typename T>
octonion<T> sinh(octonion<T> const & o);

        Computes the hyperbolic sine of the octonion.

        tanh

template<typename T>
octonion<T> tanh(octonion<T> const & o);

        Computes the hyperbolic tangent of the octonion.

        pow

template<typename T>
octonion<T>  pow(octonion<T> const & o, int n);

        Computes the n-th power of the
        octonion q.

Copyright © 2001 -2002 Daryle Walker, 2001-2003 Hubert Holin, 2005
      John Maddock


Octonoins

Name

Octonions: Octonions, like quaternions, are a relative of complex numbers.

Synopsis

namespace boost{ namespace math{

template<typename T> class octonion;
template<>           class octonion<float>;
template<>           class octonion<double>;
template<>           class octonion<long double>;

// operators

template<typename T> octonion<T> operator + (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator + (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator + (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator + (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator - (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator - (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator - (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator - (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator * (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator * (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator * (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator * (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator / (T const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, T const & rhs);
template<typename T> octonion<T> operator / (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> octonion<T> operator / (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> octonion<T> operator / (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> octonion<T> operator + (octonion<T> const & o);
template<typename T> octonion<T> operator - (octonion<T> const & o);

template<typename T> bool operator == (T const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, T const & rhs);
template<typename T> bool operator == (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator == (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> bool operator == (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T> bool operator != (T const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, T const & rhs);
template<typename T> bool operator != (::std::complex<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator != (::boost::math::quaternion<T> const & lhs, octonion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, ::boost::math::quaternion<T> const & rhs);
template<typename T> bool operator != (octonion<T> const & lhs, octonion<T> const & rhs);

template<typename T, typename charT, class traits>
::std::basic_istream<charT,traits> & operator >> (::std::basic_istream<charT,traits> & is, octonion<T> & o);

template<typename T, typename charT, class traits>
::std::basic_ostream<charT,traits> & operator << (::std::basic_ostream<charT,traits> & os, octonion<T> const & o);

// values

template<typename T> T           real(octonion<T> const & o);
template<typename T> octonion<T> unreal(octonion<T> const & o);

template<typename T> T           sup(octonion<T> const & o);
template<typename T> T           l1(octonion<T>const & o);
template<typename T> T           abs(octonion<T> const & o);
template<typename T> T           norm(octonion<T>const  & o);
template<typename T> octonion<T> conj(octonion<T> const & o);

template<typename T> octonion<T> spherical(T const & rho, T const & theta, T const & phi1, T const & phi2, T const & phi3, T const & phi4, T const & phi5, T const & phi6);
template<typename T> octonion<T> multipolar(T const & rho1, T const & theta1, T const & rho2, T const & theta2, T const & rho3, T const & theta3, T const & rho4, T const & theta4);
template<typename T> octonion<T> cylindrical(T const & r, T const & angle, T const & h1, T const & h2, T const & h3, T const & h4, T const & h5, T const & h6);

// transcendentals

template<typename T> octonion<T> exp(octonion<T> const & o);
template<typename T> octonion<T> cos(octonion<T> const & o);
template<typename T> octonion<T> sin(octonion<T> const & o);
template<typename T> octonion<T> tan(octonion<T> const & o);
template<typename T> octonion<T> cosh(octonion<T> const & o);
template<typename T> octonion<T> sinh(octonion<T> const & o);
template<typename T> octonion<T> tanh(octonion<T> const & o);

template<typename T> octonion<T> pow(octonion<T> const & o, int n);

}  } // namespaces

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Quaternions

Overview

Header File

Synopsis

Template Class quaternion

Quaternion Specializations

Quaternion Member Typedefs

Quaternion Member Functions

Quaternion Non-Member Operators

Quaternion Value Operations

Quaternion Creation Functions

Quaternion Transcendentals

Overview

Quaternions are a relative of complex numbers.

Quaternions are in fact part of a small hierarchy of structures built upon the real numbers, which comprise only the set of real numbers (traditionally named R), the set of complex numbers (traditionally named C), the set of quaternions (traditionally named H) and the set of octonions (traditionally named O), which possess interesting mathematical properties (chief among which is the fact that they are division algebras, i.e. where the following property is true: if y is an element of that algebra and is not equal to zero, then yx = yx', where x and x' denote elements of that algebra, implies that x = x'). Each member of the hierarchy is a super-set of the former.

One of the most important aspects of quaternions is that they provide an efficient way to parameterize rotations in R3 (the usual three-dimensional space) and R4 .

In practical terms, a quaternion is simply a quadruple of real numbers ( α,β,γ,δ ). The user can write it in the form q = α + βi + γj + δk , where i is the same object as for complex numbers, and j and k are distinct objects which play essentially the same kind of role as i.

An addition and a multiplication is defined on the set of quaternions, which generalize their real and complex counterparts. The main novelty here is that the multiplication is not commutative (i.e. there are quaternions x and y such that xy ≠ yx). A good mnemotechnical way of remembering things is by using the formula i*i = j*j = k*k = -1.

Quaternions (and their kin) are described in far more details in this other document (with errata and addenda).

Some traditional constructs, such as the exponential, carry over without too much change into the realms of quaternions, but other, such as taking a square root, do not.

Header File

The interface and implementation are both supplied by the header file quaternion.hpp.

Synopsis

namespace boost{ namespace math
{
template<typename T> class quaternion;
template<>           class quaternion<float>;
template<>           class quaternion<double>;
template<>           class quaternion<long double>;

// operators
template<typename T> quaternion<T> operator + (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator + (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator + (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator + (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator + (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator - (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator - (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator - (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator - (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator - (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator * (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator * (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator * (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator * (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator * (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator / (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator / (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator / (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator / (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator / (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator + (quaternion<T> const & q);
template<typename T> quaternion<T> operator - (quaternion<T> const & q);

template<typename T> bool operator == (T const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator == (quaternion<T> const & lhs, T const & rhs);
template<typename T> bool operator == (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator == (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator == (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> bool operator != (T const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator != (quaternion<T> const & lhs, T const & rhs);
template<typename T> bool operator != (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator != (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator != (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T, typename charT, class traits>
::std::basic_istream<charT,traits>& operator >> (::std::basic_istream<charT,traits> & is, quaternion<T> & q);

template<typename T, typename charT, class traits>
::std::basic_ostream<charT,traits>& operator operator << (::std::basic_ostream<charT,traits> & os, quaternion<T> const & q);

// values
template<typename T>	T              real(quaternion<T> const & q);
template<typename T>	quaternion<T>  unreal(quaternion<T> const & q);

template<typename T>	T              sup(quaternion<T> const & q);
template<typename T>	T              l1(quaternion<T> const & q);
template<typename T>	T              abs(quaternion<T> const & q);
template<typename T>	T              norm(quaternion<T>const  & q);
template<typename T>	quaternion<T>  conj(quaternion<T> const & q);

template<typename T>	quaternion<T>  spherical(T const & rho, T const & theta, T const & phi1, T const & phi2);
template<typename T>	quaternion<T>  semipolar(T const & rho, T const & alpha, T const & theta1, T const & theta2);
template<typename T>	quaternion<T>  multipolar(T const & rho1, T const & theta1, T const & rho2, T const & theta2);
template<typename T>	quaternion<T>  cylindrospherical(T const & t, T const & radius, T const & longitude, T const & latitude);
template<typename T>	quaternion<T>  cylindrical(T const & r, T const & angle, T const & h1, T const & h2);

// transcendentals
template<typename T>	quaternion<T>  exp(quaternion<T> const & q);
template<typename T>	quaternion<T>  cos(quaternion<T> const & q);
template<typename T>	quaternion<T>  sin(quaternion<T> const & q);
template<typename T>	quaternion<T>  tan(quaternion<T> const & q);
template<typename T>	quaternion<T>  cosh(quaternion<T> const & q);
template<typename T>	quaternion<T>  sinh(quaternion<T> const & q);
template<typename T>	quaternion<T>  tanh(quaternion<T> const & q);
template<typename T>	quaternion<T>  pow(quaternion<T> const & q, int n);

} // namespace math
} // namespace boost

Template Class quaternion

namespace boost{ namespace math{

template<typename T>
class quaternion
{
public:

   typedef T value_type;

   explicit quaternion(T const & requested_a = T(), T const & requested_b = T(), T const & requested_c = T(), T const & requested_d = T());
   explicit quaternion(::std::complex<T> const & z0, ::std::complex<T> const & z1 = ::std::complex<T>());
   template<typename X>
   explicit quaternion(quaternion<X> const & a_recopier);

   T                  real() const;
   quaternion<T>      unreal() const;
   T                  R_component_1() const;
   T                  R_component_2() const;
   T                  R_component_3() const;
   T                  R_component_4() const;
   ::std::complex<T>  C_component_1() const;
   ::std::complex<T>  C_component_2() const;

   quaternion<T>&     operator = (quaternion<T> const  & a_affecter);
   template<typename X>
   quaternion<T>&     operator = (quaternion<X> const  & a_affecter);
   quaternion<T>&     operator = (T const  & a_affecter);
   quaternion<T>&     operator = (::std::complex<T> const & a_affecter);

   quaternion<T>&     operator += (T const & rhs);
   quaternion<T>&     operator += (::std::complex<T> const & rhs);
   template<typename X>
   quaternion<T>&     operator += (quaternion<X> const & rhs);

   quaternion<T>&     operator -= (T const & rhs);
   quaternion<T>&     operator -= (::std::complex<T> const & rhs);
   template<typename X>
   quaternion<T>&     operator -= (quaternion<X> const & rhs);

   quaternion<T>&     operator *= (T const & rhs);
   quaternion<T>&     operator *= (::std::complex<T> const & rhs);
   template<typename X>
   quaternion<T>&     operator *= (quaternion<X> const & rhs);

   quaternion<T>&     operator /= (T const & rhs);
   quaternion<T>&     operator /= (::std::complex<T> const & rhs);
   template<typename X>
   quaternion<T>&     operator /= (quaternion<X> const & rhs);
};
} // namespace math
} // namespace boost

Quaternion Specializations

namespace boost{ namespace math{
template<>
class quaternion<float>
{
public:
   typedef float value_type;
   explicit quaternion(float const & requested_a = 0.0f, float const & requested_b = 0.0f, float const & requested_c = 0.0f, float const & requested_d = 0.0f);
   explicit quaternion(::std::complex<float> const & z0, ::std::complex<float> const & z1 = ::std::complex<float>());
   explicit quaternion(quaternion<double> const & a_recopier);
   explicit quaternion(quaternion<long double> const & a_recopier);
   float                              real() const;
   quaternion<float>                  unreal() const;
   float                              R_component_1() const;
   float                              R_component_2() const;
   float                              R_component_3() const;
   float                              R_component_4() const;
   ::std::complex<float>                                C_component_1() const;
   ::std::complex<float>                                  C_component_2() const;
   quaternion<float>&                         operator = (quaternion<float> const       & a_affecter);
   template<typename X>
   quaternion<float>&                         operator = (quaternion<X> const       & a_affecter);
   quaternion<float>&                 operator = (float const        & a_affecter);
   quaternion<float>&                         operator = (::std::complex<float> const & a_affecter);
   quaternion<float>&                         operator += (float const & rhs);
   quaternion<float>&                         operator += (::std::complex<float> const & rhs);
   template<typename X>
   quaternion<float>&                         operator += (quaternion<X> const & rhs);
   quaternion<float>&                         operator -= (float const & rhs);
   quaternion<float>&                         operator -= (::std::complex<float> const & rhs);
   template<typename X>
   quaternion<float>&                         operator -= (quaternion<X> const & rhs);
   quaternion<float>&                         operator *= (float const & rhs);
   quaternion<float>&                         operator *= (::std::complex<float> const & rhs);
   template<typename X>
   quaternion<float>&                         operator *= (quaternion<X> const & rhs);
   quaternion<float>&                 operator /= (float const & rhs);
   quaternion<float>&                         operator /= (::std::complex<float> const & rhs);
   quaternion<float>&                         operator /= (quaternion<X> const & rhs);
};
template<>
class quaternion<double>
{
public:
   typedef double value_type;
   explicit quaternion(double const & requested_a = 0.0, double const & requested_b = 0.0, double const & requested_c = 0.0, double const & requested_d = 0.0);
   explicit quaternion(::std::complex<double> const & z0, ::std::complex<double> const & z1 = ::std::complex<double>());
   explicit quaternion(quaternion<float> const & a_recopier);
   explicit quaternion(quaternion<long double> const & a_recopier);
   double                                  real() const;
   quaternion<double>                        unreal() const;
   double                                  R_component_1() const;
   double                                  R_component_2() const;
   double                                  R_component_3() const;
   double                                  R_component_4() const;
   ::std::complex<double>                                            C_component_1() const;
   ::std::complex<double>                                            C_component_2() const;
   quaternion<double>&                             operator = (quaternion<double> const        & a_affecter);
   template<typename X>
   quaternion<double>&                             operator = (quaternion<X> const        & a_affecter);
   quaternion<double>&                                 operator = (double const         & a_affecter);
   quaternion<double>&                             operator = (::std::complex<double> const & a_affecter);
   quaternion<double>&                             operator += (double const & rhs);
   quaternion<double>&                             operator += (::std::complex<double> const & rhs);
   template<typename X>
   quaternion<double>&                             operator += (quaternion<X> const & rhs);
   quaternion<double>&                             operator -= (double const & rhs);
   quaternion<double>&                                 operator -= (::std::complex<double> const & rhs);
   template<typename X>
   quaternion<double>&                             operator -= (quaternion<X> const & rhs);
   quaternion<double>&                             operator *= (double const & rhs);
   quaternion<double>&                             operator *= (::std::complex<double> const & rhs);
   template<typename X>
   quaternion<double>&                             operator *= (quaternion<X> const & rhs);
   quaternion<double>&                                 operator /= (double const & rhs);
   quaternion<double>&                             operator /= (::std::complex<double> const & rhs);
   template<typename X>
   quaternion<double>&                             operator /= (quaternion<X> const & rhs);
};

template<>
class quaternion<long double>
{
public:
   typedef long double value_type;
   explicit quaternion(long double const & requested_a = 0.0L, long double const & requested_b = 0.0L, long double const & requested_c = 0.0L, long double const & requested_d = 0.0L);
   explicit quaternion(::std::complex<long double> const & z0, ::std::complex<long double> const & z1 = ::std::complex<long double>());
   explicit quaternion(quaternion<float> const & a_recopier);
   explicit quaternion(quaternion<double> const & a_recopier);
   long double                                  real() const;
   quaternion<long double>                        unreal() const;
   long double                                  R_component_1() const;
   long double                                  R_component_2() const;
   long double                                  R_component_3() const;
   long double                                  R_component_4() const;
   ::std::complex<long double>                                            C_component_1() const;
   ::std::complex<long double>                                            C_component_2() const;
   quaternion<long double>&                                 operator = (quaternion<long double> const         & a_affecter);
   template<typename X>
   quaternion<long double>&                             operator = (quaternion<X> const        & a_affecter);
   quaternion<long double>&                             operator = (long double const        & a_affecter);
   quaternion<long double>&                             operator = (::std::complex<long double> const & a_affecter);
   quaternion<long double>&                             operator += (long double const & rhs);
   quaternion<long double>&                                 operator += (::std::complex<long double> const & rhs);
   template<typename X>
   quaternion<long double>&                             operator += (quaternion<X> const & rhs);
   quaternion<long double>&                             operator -= (long double const & rhs);
   quaternion<long double>&                             operator -= (::std::complex<long double> const & rhs);
   template<typename X>
   quaternion<long double>&                                 operator -= (quaternion<X> const & rhs);
   quaternion<long double>&                             operator *= (long double const & rhs);
   quaternion<long double>&                             operator *= (::std::complex<long double> const & rhs);
   template<typename X>
   quaternion<long double>&                             operator *= (quaternion<X> const & rhs);
   quaternion<long double>&                             operator /= (long double const & rhs);
   quaternion<long double>&                             operator /= (::std::complex<long double> const & rhs);
   template<typename X>
   quaternion<long double>&                             operator /= (quaternion<X> const & rhs);
};

} // namespace math
} // namespace boost

Quaternion Member Typedefs

value_type

Template version: typedef T value_type;

Float specialization version: typedef float value_type;

Double specialization version: typedef double value_type;

Long double specialization version: typedef long double value_type;

These provide easy access to the type the template is built upon.

Quaternion Member Functions

Constructors

Template version:

explicit quaternion(T const & requested_a = T(), T const & requested_b = T(), T const & requested_c = T(), T const & requested_d = T());
explicit quaternion(::std::complex<T> const & z0, ::std::complex<> const & z1 = ::std::complex<T>());
template<typename X>
explicit quaternion(quaternion<X> const & a_recopier);

Float specialization version:

explicit quaternion(float const & requested_a = 0.0f, float const & requested_b = 0.0f, float const & requested_c = 0.0f, float const & requested_d = 0.0f);
explicit quaternion(::std::complex<float> const & z0,::std::complex<float> const & z1 = ::std::complex<float>());
explicit quaternion(quaternion<double> const & a_recopier);
explicit quaternion(quaternion<long double> const & a_recopier);

Double specialization version:

explicit quaternion(double const & requested_a = 0.0, double const & requested_b = 0.0, double const & requested_c = 0.0, double const & requested_d = 0.0);
explicit quaternion(::std::complex<double> const & z0, ::std::complex<double> const & z1 = ::std::complex<double>());
explicit quaternion(quaternion<float> const & a_recopier);
explicit quaternion(quaternion<long double> const & a_recopier);

Long double specialization version:

explicit quaternion(long double const & requested_a = 0.0L, long double const & requested_b = 0.0L, long double const & requested_c = 0.0L, long double const & requested_d = 0.0L);
explicit quaternion(	::std::complex<long double> const & z0, ::std::complex<long double> const & z1 = ::std::complex<long double>());
explicit quaternion(quaternion<float> const & a_recopier);
explicit quaternion(quaternion<double> const & a_recopier);

A default constructor is provided for each form, which initializes each component to the default values for their type (i.e. zero for floating numbers). This constructor can also accept one to four base type arguments. A constructor is also provided to build quaternions from one or two complex numbers sharing the same base type. The unspecialized template also sports a templarized copy constructor, while the specialized forms have copy constructors from the other two specializations, which are explicit when a risk of precision loss exists. For the unspecialized form, the base type's constructors must not throw.

Destructors and untemplated copy constructors (from the same type) are provided by the compiler. Converting copy constructors make use of a templated helper function in a "detail" subnamespace.

Other member functions

Real and Unreal Parts

T             real() const;
quaternion<T> unreal() const;

Like complex number, quaternions do have a meaningful notion of "real part", but unlike them there is no meaningful notion of "imaginary part". Instead there is an "unreal part" which itself is a quaternion, and usually nothing simpler (as opposed to the complex number case). These are returned by the first two functions.

Individual Real Components

T R_component_1() const;
T R_component_2() const;
T R_component_3() const;
T R_component_4() const;

A quaternion having four real components, are returned by these four functions. Hence real and R_component_1 return the same value.

Individual Complex Components

::std::complex<T>	C_component_1() const;
::std::complex<T>	C_component_2() const;

A quaternion likewise has two complex components, and as seen above, for any quaternion q = α + βi + γj + δk we also have q = ( α + βi) + (γ + δi)j . These functions return them. The real part of q.C_component_1() is the same as q.real().

Quaternion Member Operators

Assignment Operators

quaternion<T>& operator = (quaternion<T> const & a_affecter);
template<typename X>
quaternion<T>& operator = (quaternion<X> const& a_affecter);
quaternion<T>& operator = (T const& a_affecter);
quaternion<T>& operator = (::std::complex<T> const& a_affecter);

These perform the expected assignment, with type modification if necessary (for instance, assigning from a base type will set the real part to that value, and all other components to zero). For the unspecialized form, the base type's assignment operators must not throw.

Addition Operators

quaternion<T>& operator += (T const & rhs)
quaternion<T>& operator += (::std::complex<T> const & rhs);
template<typename X>
quaternion<T>& operator += (quaternion<X> const & rhs);

These perform the mathematical operation (*this)+rhs and store the result in *this. The unspecialized form has exception guards, which the specialized forms do not, so as to insure exception safety. For the unspecialized form, the base type's assignment operators must not throw.

Subtraction Operators

quaternion<T>& operator -= (T const & rhs)
quaternion<T>& operator -= (::std::complex<T> const & rhs);
template<typename X>
quaternion<T>& operator -= (quaternion<X> const & rhs);

These perform the mathematical operation (*this)-rhs and store the result in *this. The unspecialized form has exception guards, which the specialized forms do not, so as to insure exception safety. For the unspecialized form, the base type's assignment operators must not throw.

Multiplication Operators

quaternion<T>& operator *= (T const & rhs)
quaternion<T>& operator *= (::std::complex<T> const & rhs);
template<typename X>
quaternion<T>& operator *= (quaternion<X> const & rhs);

These perform the mathematical operation (*this)*rhs in this order (order is important as multiplication is not commutative for quaternions) and store the result in *this. The unspecialized form has exception guards, which the specialized forms do not, so as to insure exception safety. For the unspecialized form, the base type's assignment operators must not throw.

Division Operators

quaternion<T>& operator /= (T const &rhs)
quaternion<T>& operator /= (::std::complex<T> const & rhs);
template<typename X>
quaternion<T>& operator /= (quaternion<X> const & rhs);

These perform the mathematical operation (*this)*inverse_of(rhs) in this order (order is important as multiplication is not commutative for quaternions) and store the result in *this. The unspecialized form has exception guards, which the specialized forms do not, so as to insure exception safety. For the unspecialized form, the base type's assignment operators must not throw.

Quaternion Non-Member Operatorss

Unary Plus

templatetypename TT>
quaternion<T> operator + (quaternion<T> const & q);

This unary operator simply returns q.

Unary Minus

templatetypename TT>
quaternion<T> operator - (quaternion<T> const & q);

This unary operator returns the opposite of q.

Binary Addition Operators

templatetypename T>	quaternion<T>	operator + (T const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator + (quaternion<T> const & lhs, T const & rhs);
template<typename T>	quaternion<T>	operator + (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator + (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T>	quaternion<T>	operator + (quaternion<T> const & lhs, quaternion<T> const & rhs);

These operators return quaternion<T>(lhs) += rhs.

Binary Subtraction Operators

template<typename T>	quaternion<T>	operator - (T const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator - (quaternion<T> const & lhs, T const & rhs);
template<typename T>	quaternion<T>	operator - (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator - (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T>	quaternion<T>	operator - (quaternion<T> const & lhs, quaternion<T> const & rhs);

These operators return quaternion<T>(lhs) -= rhs.

Binary Multiplication Operators

template<typename T>	quaternion<T>	operator * (T const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator * (quaternion<T> const & lhs, T const & rhs);
template<typename T>	quaternion<T>	operator * (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator * (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T>	quaternion<T>	operator * (quaternion<T> const & lhs, quaternion<T> const & rhs);

These operators return quaternion<T>(lhs) *= rhs.

Binary Division Operators

template<typename T>	quaternion<T>	operator / (T const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator / (quaternion<T> const & lhs, T const & rhs);
template<typename T>	quaternion<T>	operator / (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T>	quaternion<T>	operator / (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T>	quaternion<T>	operator / (quaternion<T> const & lhs, quaternion<T> const & rhs);

These operators return quaternion<T>(lhs) /= rhs. It is an error to divide by zero.

Equality Operators

template<typename T>	bool	operator == (T const & lhs, quaternion<T> const & rhs);
template<typename T>	bool	operator == (quaternion<T> const & lhs, T const & rhs);
template<typename T>	bool	operator == (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T>	bool	operator == (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T>	bool	operator == (quaternion<T> const & lhs, quaternion<T> const & rhs);

These return true if and only if the four components of quaternion<T>(lhs) are equal to their counterparts in quaternion<T>(rhs). As with any floating-type entity, this is essentially meaningless.

Inequality Operators

template<typename T>	bool	operator != (T const & lhs, quaternion<T> const & rhs);
template<typename T>	bool	operator != (quaternion<T> const & lhs, T const & rhs);
template<typename T>	bool	operator != (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T>	bool	operator != (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T>	bool	operator != (quaternion<T> const & lhs, quaternion<T> const & rhs);

These return true if and only if quaternion<T>(lhs) == quaternion<T>(rhs) is false. As with any floating-type entity, this is essentially meaningless.

Stream Extractor

template<typename T, typename charT, class traits>
::std::basic_istream<charT,traits>& operator >> (::std::basic_istream<charT,traits> & is, quaternion<T> & q);

Extracts a quaternion q of one of the following forms (with a, b, c and d of type T):

a (a), (a,b), (a,b,c), (a,b,c,d) (a,(c)), (a,(c,d)), ((a)), ((a),c), ((a),(c)), ((a),(c,d)), ((a,b)), ((a,b),c), ((a,b),(c)), ((a,b),(c,d))

The input values must be convertible to T. If bad input is encountered, calls is.setstate(ios::failbit) (which may throw ios::failure (27.4.5.3)).

Returns: is

The rationale for the list of accepted formats is that either there is a list of upto four real numbers, or a couple of complex numbers, and in that case if it is formatted as a proper complex number, then it should be accepted. Thus potential ambiguities are lifted (for instance (a,b) is (a,b,0,0) and not (a,0,b,0), i.e. it is parsed as a list of two real numbers and not two complex numbers which happen to have imaginary parts equal to zero).

Stream Inserter

templatetypename T, typename charT, class traits>
::std::basic_ostream<charT,traits>& operator << (::std::basic_ostream<charT,traits> & os, quaternion<T> const & q);

Inserts the quaternion q onto the stream os as if it were implemented as follows:

template<typename T, typename charTT, class traits>
std::basic_ostream<charT,traits>& operator << (
               ::std::basic_ostream<charT,traits> & os,
               quaternion<T> const & q)
{
   ::std::basic_ostringstream<charT,traits>s;
   s.flags(os.flags());
   s.imbue(os.getloc());
   s.precision(os.precision());;
   s << '(' << q.R_component_1() << ','
            << q.R_component_2() << ','
            << q.R_component_3() << ','
            << q.R_component_4() << ')';
   returnos << s.str();;
}

Quaternion Value Operations

real and unreal

templatetypename T>	T                    real(quaternion<T> const & q);
template<typename T>	quaternion<T>               unreal(quaternion<T> const & q);

These return q.real() and q.unreal() respectively.

conj

template<typename T>	quaternion<T>                conj(quaternion<;T> const & q);

This returns the conjugate of the quaternion.

sup

template<typename T> T sup(quaternion<T> const & q);

This return the sup norm (the greatest among abs(q.R_component_1())...abs(q.R_component_4())) of the quaternion.

l1

templatetypename TT>	T                l1(quaternion<T> const & q);

This return the l1 norm (abs(q.R_component_1())+...+abs(q.R_component_4())) of the quaternion.

abs

template<typename T>	T                abs(quaternion<T> const & q);

This return the magnitude (Euclidian norm) of the quaternion.

norm

template<typename T>	T                norm(quaternion<T>const                & q);

Quaternion Creation Functionss

templatetypename T>	quaternion<T>	spherical(T const & rho, T const & theta, T const & phi1, T const & phi2);
template<typename T>	quaternion<T>	semipolar(T const & rho, T const & alpha, T const & theta1, T const & theta2);
template<typename T>	quaternion<T>	multipolar(T constt & rho1, T const & theta1, T const & rho2, T const & theta2);
template<typename T>	quaternion<T>	cylindrospherical(T const & t, T const & radius, T const & longitude, T const & latitude);
template<typename T>	quaternion<T>	cylindrical(T constt & r, T const & angle, T const & h1, T const & h2);

These build quaternions in a way similar to the way polar builds complex numbers, as there is no strict equivalent to polar coordinates for quaternions.

Spherical is a simple transposition of polar, it takes as inputs a (positive) magnitude and a point on the hypersphere, given by three angles. The first of these, theta has a natural range of -pi to +pi, and the other two have natural ranges of -pi/2 to +pi/2 (as is the case with the usual spherical coordinates in R3 ). Due to the many symmetries and periodicities, nothing untoward happens if the magnitude is negative or the angles are outside their natural ranges. The expected degeneracy (a magnitude of zero ignores the angles settings) do happen however.

Cylindrical is likewise a simple transposition of the usual cylindrical coordinates in R3 , which in turn is another derivative of planar polar coordinates. The first two inputs are the polar coordinates of the first C component of the quaternion. The third and fourth inputs are placed into the third and fourth R

Multipolar is yet another simple generalization of polar coordinates. This time, both C components of the quaternion are given in polar coordinates.

Cylindrospherical is specific to quaternions. It is often interesting to consider HH as the Cartesian product of R by R3 (the quaternionic multiplication as then a special form, as given here). This function therefore builds a quaternion from this representation, with the R3 component given in usual R3

Semipolar is another generator which is specific to quaternions. It takes as a first input the magnitude of the quaternion, as a second input an angle in the range 0 to +pi/2 such that magnitudes of the first two C components of the quaternion are the product of the first input and the sine and cosine of this angle, respectively, and finally as third and fourth inputs angles in the range -pi/2 to +pi/2 which represent the arguments of the first and second C components of the quaternion, respectively. As usual, nothing untoward happens if what should be magnitudes are negative numbers or angles are out of their natural ranges, as symmetries and periodicities kick in.

In this version of implementation of quaternions, there is no analogue of the complex value operation arg as the situation is somewhat more complicated. Unit quaternions are linked both to rotations in R3 and in R4 , and the correspondences are not too complicated, but there is currently a lack of standard (de facto or de jure) matrix library with which the conversions could work. This should be remedied in a further revision. In the mean time, an example of how this could be done is presented here for RR3 , and here for R4 .

Quaternion Transcendentals

There is no logsqrt provided for quaternions in this implementation, and pow is likewise restricted to integral powers of the exponent. There are several reasons to this: on the one hand, the equivalent of analytic continuation for quaternions ("branch cuts") remains to be investigated thoroughly , and to avoid the nonsense introduced in the standard by exponentiations of complexes by complexes (which is well defined, but not in the standard). For example, saying that poww(0,0) is "implementation defined" is just unnecessary.

C can be transposed to H. More transcendentals of this type could be added in a further revision upon request. It should be noted that it is these functions which force the dependency upon the boost/math/special_functions/sinc.hpp and the boost/math/special_functions/sinhc.hpp headers.

expp

template<typename T>	quaternion<T> exp(quaternion<T> const & q);

Computes the exponential of the quaternion.

cos

template<typename T>	quaternion<T>                  cos(quaternion<T> const & q);

Computes the cosine of the quaternion

sin

templatetypename T>	quaternion<T>               sin(quaternion<T> const & q);

Computes the sine of the quaternion.

tan

template<typename T>	quaternion<T>               tan(quaternion<T> const & q);

Computes the tangent of the quaternion.

cosh

templateetypename T>	quaternion<T>               cosh(quaternion<T> const & q);

Computes the hyperbolic cosine of the quaternion.

sinh

template<typename T>	quaternion<T>                 sinh(quaternion<T> const & q);

Computes the hyperbolic sine of the quaternion.

tanh

templatetypename T>	quaternion<T>              tanh(quaternion<;T> const & q);

pow

template<typename T>	quaternion<T>              pow(quaternion<T> const & q, int n);

Computes the n-th power of the quaternion q.

Copyright © 2001 -2002 Daryle Walker, 2001-2003 Hubert Holin, 2005 John Maddock


Quarternions

Name

Quaternions: Quaternions are a relative of complex numbers.

Synopsis

namespace boost{ namespace math
{
template<typename T> class quaternion;
template<>           class quaternion<float>;
template<>           class quaternion<double>;
template<>           class quaternion<long double>;

// operators
template<typename T> quaternion<T> operator + (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator + (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator + (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator + (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator + (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator - (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator - (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator - (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator - (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator - (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator * (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator * (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator * (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator * (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator * (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator / (T const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator / (quaternion<T> const & lhs, T const & rhs);
template<typename T> quaternion<T> operator / (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> quaternion<T> operator / (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> quaternion<T> operator / (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> quaternion<T> operator + (quaternion<T> const & q);
template<typename T> quaternion<T> operator - (quaternion<T> const & q);

template<typename T> bool operator == (T const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator == (quaternion<T> const & lhs, T const & rhs);
template<typename T> bool operator == (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator == (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator == (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T> bool operator != (T const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator != (quaternion<T> const & lhs, T const & rhs);
template<typename T> bool operator != (::std::complex<T> const & lhs, quaternion<T> const & rhs);
template<typename T> bool operator != (quaternion<T> const & lhs, ::std::complex<T> const & rhs);
template<typename T> bool operator != (quaternion<T> const & lhs, quaternion<T> const & rhs);

template<typename T, typename charT, class traits>
::std::basic_istream<charT,traits>& operator >> (::std::basic_istream<charT,traits> & is, quaternion<T> & q);

template<typename T, typename charT, class traits>
::std::basic_ostream<charT,traits>& operator operator << (::std::basic_ostream<charT,traits> & os, quaternion<T> const & q);

// values
template<typename T>	T              real(quaternion<T> const & q);
template<typename T>	quaternion<T>  unreal(quaternion<T> const & q);

template<typename T>	T              sup(quaternion<T> const & q);
template<typename T>	T              l1(quaternion<T> const & q);
template<typename T>	T              abs(quaternion<T> const & q);
template<typename T>	T              norm(quaternion<T>const  & q);
template<typename T>	quaternion<T>  conj(quaternion<T> const & q);

template<typename T>	quaternion<T>  spherical(T const & rho, T const & theta, T const & phi1, T const & phi2);
template<typename T>	quaternion<T>  semipolar(T const & rho, T const & alpha, T const & theta1, T const & theta2);
template<typename T>	quaternion<T>  multipolar(T const & rho1, T const & theta1, T const & rho2, T const & theta2);
template<typename T>	quaternion<T>  cylindrospherical(T const & t, T const & radius, T const & longitude, T const & latitude);
template<typename T>	quaternion<T>  cylindrical(T const & r, T const & angle, T const & h1, T const & h2);

// transcendentals
template<typename T>	quaternion<T>  exp(quaternion<T> const & q);
template<typename T>	quaternion<T>  cos(quaternion<T> const & q);
template<typename T>	quaternion<T>  sin(quaternion<T> const & q);
template<typename T>	quaternion<T>  tan(quaternion<T> const & q);
template<typename T>	quaternion<T>  cosh(quaternion<T> const & q);
template<typename T>	quaternion<T>  sinh(quaternion<T> const & q);
template<typename T>	quaternion<T>  tanh(quaternion<T> const & q);
template<typename T>	quaternion<T>  pow(quaternion<T> const & q, int n);

} // namespace math
} // namespace boost

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Boost.Ref

Introduction
Reference
Header <boost/ref.hpp>

Introduction

The Ref library is a small library that is useful for passing references to function templates (algorithms) that would usually take copies of their arguments. It defines the class template boost::reference_wrapper<T> , the two functions boost::ref and boost::cref that return instances of boost::reference_wrapper<T>, and the two traits classes boost::is_reference_wrapper<T> and boost::unwrap_reference<T> .

The purpose of boost::reference_wrapper<T> is to contain a reference to an object of type T. It is primarily used to "feed" references to function templates (algorithms) that take their parameter by value.

To support this usage, boost::reference_wrapper<T> provides an implicit conversion to T&. This usually allows the function templates to work on references unmodified.

boost::reference_wrapper<T> is both CopyConstructible and Assignable (ordinary references are not Assignable).

The expression boost::ref(x) returns a boost::reference_wrapper<X>(x) where X is the type of x. Similarly, boost::cref(x) returns a boost::reference_wrapper<X const>(x).

The expression boost::is_reference_wrapper<T>::value is true if T is a reference_wrapper, and false otherwise.

The type-expression boost::unwrap_reference<T>::type is T::type if T is a reference_wrapper, T otherwise.

Copyright © 1999, 2000 Jaakko Järvi, Copyright © 2001, 2002 Peter Dimov, Copyright © 2002 David Abrahams
Subject to the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


Reference

Header <boost/ref.hpp>

Header <boost/ref.hpp>

namespace boost {
  template<typename T> class reference_wrapper;
  reference_wrapper<T> ref(T&);
  reference_wrapper<T const> cref(T const&);
  template<typename T> class is_reference_wrapper;
  template<typename T> class unwrap_reference;
}

Copyright © 1999, 2000 Jaakko Järvi
Copyright © 2001, 2002 Peter Dimov
Copyright © 2002 David Abrahams


Class template reference_wrapper

boost::reference_wrapper — Contains a reference to an object of type T.

Synopsis

template<typename T>
class reference_wrapper {
public:
  // types
  typedef T type;

  // construct/copy/destruct
  explicit reference_wrapper(T&);

  // access
  operator T&() const;
  T& get() const;
  T* get_pointer() const;
};

// constructors
reference_wrapper<T> ref(T&);
reference_wrapper<T const> cref(T const&);

Description

reference_wrapper is primarily used to "feed" references to function templates (algorithms) that take their parameter by value. It provides an implicit conversion to T&, which usually allows the function templates to work on references unmodified.

reference_wrapper construct/copy/destruct

  1. explicit reference_wrapper(T& t);
    Effects: Constructs a reference_wrapper object that stores a reference to t.
    Throws: Does not throw

reference_wrapper access

  1. operator T&() const;
    Returns: The stored reference.
    Throws: Does not throw
  2. T& get() const;
    Returns: The stored reference.
    Throws: Does not throw
  3. T* get_pointer() const;
    Returns: A pointer to the object referenced by the stored reference.
    Throws: Does not throw

reference_wrapper constructors

  1. reference_wrapper<T> ref(T& t);
    Returns: reference_wrapper <T>(t)
    Throws: Does not throw
  2. reference_wrapper<T const> cref(T const& t);
    Returns: reference_wrapper <T const>(t)
    Throws: Does not throw
Copyright © 1999, 2000 Jaakko Järvi
Copyright © 2001, 2002 Peter Dimov
Copyright © 2002 David Abrahams


scoped_array

scoped_array class template

The scoped_array class template stores a pointer to a dynamically allocated array. (Dynamically allocated arrays are allocated with the C++ new[] expression.) The array pointed to is deleted, either on destruction of the scoped_array, or via an explicit reset.

The scoped_array template is a simple solution. It supplies a basic "resource acquisition is initialization" facility, without shared-ownership or transfer-of-ownership semantics. Both its name and enforcement of semantics (by being noncopyable) signal its intent to retain ownership solely within the current scope. Because it is noncopyable, it is safer than shared_array for pointers which should not be copied.

Because scoped_array is so simple, in its usual implementation every operation is as fast as a built-in array pointer and it has no more space overhead that a built-in array pointer.

It cannot be used in C++ standard library containers. See shared_array if scoped_array does not meet the needs.

It cannot correctly hold a pointer to a single object. See scoped_ptr for usage.

A std::vector is an alternative to a scoped_array that is a bit heavier duty but far more flexible. A boost::array is an alternative that does not use dynamic allocation.

The class template is parameterized on T, the type of the object pointed to. T must meet the smart pointer common requirements.

Synopsis

namespace boost {

  template<class T> class scoped_array : noncopyable {

    public:
      typedef T element_type;

      explicit scoped_array(T * p = 0); // never throws
      ~scoped_array(); // never throws

      void reset(T * p = 0); // never throws

      T & operator[](std::ptrdiff_t i) const; // never throws
      T * get() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(scoped_array & b); // never throws
  };

  template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b); // never throws

}

Members

element_type

typedef T element_type;

Provides the type of the stored pointer.

constructors

explicit scoped_array(T * p = 0); // never throws

Constructs a scoped_array, storing a copy of p, which must have been allocated via a C++ new[] expression or be 0. T is not required be a complete type. See the smart pointer common requirements.

destructor

~scoped_array(); // never throws

Deletes the array pointed to by the stored pointer. Note that delete[] on a pointer with a value of 0 is harmless. The guarantee that this does not throw exceptions depends on the requirement that the deleted array's objects' destructors do not throw exceptions. See the smart pointer common requirements.

reset

void reset(T * p = 0); // never throws

Deletes the array pointed to by the stored pointer and then stores a copy of p, which must have been allocated via a C++ new[] expression or be 0. The guarantee that this does not throw exceptions depends on the requirement that the deleted array's objects' destructors do not throw exceptions. See the smart pointer common requirements.

subscripting

T & operator[](std::ptrdiff_t i) const; // never throws

Returns a reference to element i of the array pointed to by the stored pointer. Behavior is undefined and almost certainly undesirable if the stored pointer is 0, or if i is less than 0 or is greater than or equal to the number of elements in the array.

get

T * get() const; // never throws

Returns the stored pointer. T need not be a complete type. See the smart pointer common requirements.

conversions

operator unspecified-bool-type () const; // never throws

Returns an unspecified value that, when used in boolean contexts, is equivalent to get() != 0.

swap

void swap(scoped_array & b); // never throws

Exchanges the contents of the two smart pointers. T need not be a complete type. See the smart pointer common requirements.

Free Functions

swap

template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b); // never throws

Equivalent to a.swap(b). Matches the interface of std::swap. Provided as an aid to generic programming.

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Copyright 2002-2005 Peter Dimov. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


scoped_array

Name

scoped_array: Class template stores a pointer to a dynamically allocated array.

Synopsis

namespace boost {

  template<class T> class scoped_array : noncopyable {

    public:
      typedef T element_type;

      explicit scoped_array(T * p = 0); // never throws
      ~scoped_array(); // never throws

      void reset(T * p = 0); // never throws

      T & operator[](std::ptrdiff_t i) const; // never throws
      T * get() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(scoped_array & b); // never throws
  };

  template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b); // never throws

}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


scoped_ptr

scoped_ptr class template

The scoped_ptr class template stores a pointer to a dynamically allocated object. (Dynamically allocated objects are allocated with the C++ new expression.) The object pointed to is guaranteed to be deleted, either on destruction of the scoped_ptr, or via an explicit reset. See the example .

The scoped_ptr template is a simple solution. It supplies a basic "resource acquisition is initialization" facility, without shared-ownership or transfer-of-ownership semantics. Both its name and enforcement of semantics (by being noncopyable) signal its intent to retain ownership solely within the current scope. Because it is noncopyable, it is safer than shared_ptr or std::auto_ptr for pointers which should not be copied.

Because scoped_ptr is simple, in its usual implementation every operation is as fast as for a built-in pointer and it has no more space overhead than a built-in pointer.

scoped_ptr cannot be used in C++ Standard Library containers. Use a shared_ptr instead.

scoped_ptr cannot correctly hold a pointer to a dynamically allocated array. See scoped_array for usage.

The class template is parameterized on T, the type of the object pointed to. T must meet the smart pointer common requirements.

Synopsis

namespace boost {

  template<class T> class scoped_ptr : noncopyable {

   public:
     typedef T element_type;

     explicit scoped_ptr(T * p = 0); // never throws
     ~scoped_ptr(); // never throws

     void reset(T * p = 0); // never throws

     T & operator*() const; // never throws
     T * operator->() const; // never throws
     T * get() const; // never throws

     operator unspecified-bool-type() const; // never throws

     void swap(scoped_ptr & b); // never throws
  };

  template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws

}

Members

element_type

typedef T element_type;

Provides the type of the stored pointer.

constructors

explicit scoped_ptr(T * p = 0); // never throws

Constructs a scoped_ptr, storing a copy of p, which maybe allocated via a C++ new expression or be 0. T is not required be a complete type. See the smart pointer common requirements.

destructor

~scoped_ptr(); // never throws

Destroys the object pointed to by the stored pointer, if any, as if by using delete this->get().

The guarantee that this does not throw exceptions depends on the requirement that the deleted object's destructor does not throw exceptions. See the smart pointer common requirements.

reset

void reset(T * p = 0); // never throws

Deletes the object pointed to by the stored pointer and then stores a copy of p, which maybe allocated via a C++ new expression or be 0. The guarantee that this does not throw exceptions depends on the requirement that the deleted object's destructor does not throw exceptions. See the smart pointer common requirements.

indirection

T & operator*() const; // never throws

Returns a reference to the object pointed to by the stored pointer. Behavior is undefined if the stored pointer is 0.

T * operator->() const; // never throws

Returns the stored pointer. Behavior is undefined if the stored pointer is 0.

get

T * get() const; // never throws

Returns the stored pointer. T need not be a complete type. See the smart pointer common requirements.

conversions

operator unspecified-bool-type () const; // never throws

Returns an unspecified value that, when used in Boolean contexts, is equivalent to get() != 0.

swap

void swap(scoped_ptr & b); // never throws

Exchanges the contents of the two smart pointers. T need not be a complete type. See the smart pointer common requirements.

Free Functions

swap

template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws

Equivalent to a.swap(b). Matches the interface of std::swap. Provided as an aid to generic programming.

Example

The code snippet below shows an example that uses scoped_ptr.

#include <boost/scoped_ptr.hpp>
#include <iostream>

struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } };

class MyClass {
    boost::scoped_ptr<int> ptr;
  public:
    MyClass() : ptr(new int) { *ptr = 0; }
    int add_one() { return ++*ptr; }
};

int main()
{
    boost::scoped_ptr<Shoe> x(new Shoe);
    MyClass my_instance;
    std::cout << my_instance.add_one() << '\n';
    std::cout << my_instance.add_one() << '\n';
}

The example program produces the beginning of a nursery rhyme:

1
2
Buckle my shoe

Rationale

The primary reason to use scoped_ptr rather than auto_ptr is to let readers of the code know that the programmer intends to apply "resource acquisition is initialization" only for the current scope, and does not intend to transfer ownership.

A secondary reason to use scoped_ptr is to prevent a later maintenance programmer from adding a function that transfers ownership by returning the auto_ptr, because the maintenance programmer saw auto_ptr, and assumed ownership could safely be transferred.

Think of bool vs int. Under the covers bool is usually just an int. Indeed, some argued against including bool in the C++ standard owing to this. But by coding bool rather than int, the readers know the programmers intent. Similarly, using scoped_ptr; signals intent.

It has been suggested that scoped_ptr<T> is equivalent to std::auto_ptr<T> const. Ed Brey pointed out, however, that reset will not work on a std::auto_ptr<T> const.

Handle/Body Idiom

One common usage of scoped_ptr is to implement a handle/body (also called pimpl) idiom which avoids exposing the body (implementation) in the header file.

The scoped_ptr_example_test.cpp sample program includes a header file, scoped_ptr_example.hpp, which uses a scoped_ptr<> to an incomplete type to hide the implementation. The instantiation of member functions which require a complete type occurs in the scoped_ptr_example.cpp implementation file.

Note:The scoped_ptr does not have a release() member as, it makes it possible to transfer ownership of the held pointer hterby weakening its role as a way of imiting resource lifetime to a given context. Use std::auto_ptr where transfer of ownership is required. (supplied by Dave Abrahams)

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Copyright 2002-2005 Peter Dimov. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


scoped_ptr

Name

scoped_ptr: Class template stores a pointer to a dynamically allocated object.

Synopsis

namespace boost {

  template<class T> class scoped_ptr : noncopyable {

   public:
     typedef T element_type;

     explicit scoped_ptr(T * p = 0); // never throws
     ~scoped_ptr(); // never throws

     void reset(T * p = 0); // never throws

     T & operator*() const; // never throws
     T * operator->() const; // never throws
     T * get() const; // never throws

     operator unspecified-bool-type() const; // never throws

     void swap(scoped_ptr & b); // never throws
  };

  template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); // never throws

}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


shared_array

shared_array class template

The shared_array class template stores a pointer to a dynamically allocated array. (Use the C++ new[] expression to dynamically allocate an array.) The object pointed to is deleted when the last shared_array pointing to it is destroyed or reset.

Every shared_array meets the CopyConstructible and Assignable requirements of the C++ Standard Library, and so can be used in standard library containers. Comparison operators are supplied so that shared_array works with the standard library's associative containers.

Normally, a shared_array cannot correctly hold a pointer to an object that has been allocated with the non-array form of new. For usage See shared_ptr .

Cycles of shared_array instances will not be reclaimed as the implementation uses reference counting. For example, if main() holds a shared_array to A, which directly or indirectly holds a shared_array back to A, A's use count will be 2. Destruction of the original shared_array will leave A dangling with a use count of 1.

A shared_ptr to a std::vector is an alternative to a shared_array that is a bit heavier duty but far more flexible.

The class template is parameterized on T, the type of the object pointed to. T must meet the smart pointer common requirements.

Synopsis

namespace boost {

  template<class T> class shared_array {

    public:
      typedef T element_type;

      explicit shared_array(T * p = 0);
      template<class D> shared_array(T * p, D d);
      ~shared_array(); // never throws

      shared_array(shared_array const & r); // never throws

      shared_array & operator=(shared_array const & r); // never throws

      void reset(T * p = 0);
      template<class D> void reset(T * p, D d);

      T & operator[](std::ptrdiff_t i) const() const; // never throws
      T * get() const; // never throws

      bool unique() const; // never throws
      long use_count() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(shared_array<T> & b); // never throws
  };

  template<class T>
    bool operator==(shared_array<T> const & a, shared_array<T> const & b); // never throws
  template<class T>
    bool operator!=(shared_array<T> const & a, shared_array<T> const & b); // never throws
  template<class T>
    bool operator<(shared_array<T> const & a, shared_array<T> const & b); // never throws

  template<class T> void swap(shared_array<T> & a, shared_array<T> & b); // never throws

}

Members

element_type

typedef T element_type;

Provides the type of the stored pointer.

constructors

explicit shared_array(T * p = 0);

Constructs a shared_array, storing a copy of p, which must be a pointer to an array that was allocated via a C++ new[] expression or be 0. Afterwards, the use count is 1 (even if p == 0; see ~shared_array). The only exception which may be thrown by this constructor is std::bad_alloc. If an exception is thrown, delete[] p is called.

template<class D> shared_array(T * p, D d);

Constructs a shared_array, storing a copy of p and of d. Afterwards, the use count is 1. D's copy constructor and destructor must not throw. When the the time comes to delete the array pointed to by p, the object d is used in the statement d(p). Invoking the object d with parameter p in this way must not throw. The only exception which may be thrown by this constructor is std::bad_alloc. If an exception is thrown, d(p) is called.

shared_array(shared_array const & r); // never throws

Constructs a shared_array, as if by storing a copy of the pointer stored in r. Afterwards, the use count for all copies is 1 more than the initial use count.

destructor

~shared_array(); // never throws

Decrements the use count. Then, if the use count is 0, deletes the array pointed to by the stored pointer.

Note :That delete[] on a pointer with a value of 0 is harmless. T need not be a complete type. The guarantee that this does not throw exceptions depends on the requirement that the deleted object's destructor does not throw exceptions. See the smart pointer common requirements.

assignment

shared_array & operator=(shared_array const & r); // never throws

Constructs a new shared_array as described above, then replaces this shared_array with the new one, destroying the replaced object.

reset

void reset(T * p = 0);

Constructs a new shared_array as described above, then replaces this shared_array with the new one, destroying the replaced object. The only exception which may be thrown is std::bad_alloc. If an exception is thrown, delete[] p is called.

template<class D> void reset(T * p, D d);

Constructs a new shared_array as described above, then replaces this shared_array with the new one, destroying the replaced object. D's copy constructor must not throw. The only exception which may be thrown is std::bad_alloc. If an exception is thrown, d(p) is called.

indexing

T & operator[](std::ptrdiff_t i) const; // never throws

Returns a reference to element i of the array pointed to by the stored pointer. Behavior is undefined and almost certainly undesirable if the stored pointer is 0, or if i is less than 0 or is greater than or equal to the number of elements in the array.

get

T * get() const; // never throws

Returns the stored pointer. T need not be a complete type. See the smart pointer common requirements.

unique

bool unique() const; // never throws

Returns true if no other shared_array is sharing ownership of the stored pointer, false otherwise. T need not be a complete type. See the smart pointer common requirements.

use_count

long use_count() const; // never throws

Returns the number of shared_array objects sharing ownership of the stored pointer. T need not be a complete type. See the smart pointer common requirements.

Because use_count is not necessarily efficient to implement for implementations of shared_array that do not use an explicit reference count, it might be removed from some future version. Thus it should be used for debugging purposes only, and not production code.

conversions

operator unspecified-bool-type () const; // never throws

Returns an unspecified value that, when used in boolean contexts, is equivalent to get() != 0.

swap

void swap(shared_ptr & b); // never throws

Exchanges the contents of the two smart pointers. T need not be a complete type. See the smart pointer common requirements.

Free Functions

comparison

template<class T>
  bool operator==(shared_array<T> const & a, shared_array<T> const & b); // never throws
template<class T>
  bool operator!=(shared_array<T> const & a, shared_array<T> const & b); // never throws
template<class T>
  bool operator<(shared_array<T> const & a, shared_array<T> const & b); // never throws

Compares the stored pointers of the two smart pointers. T need not be a complete type. See the smart pointer common requirements.

The operator< overload is provided to define an ordering so that shared_array objects can be used in associative containers such as std::map. The implementation uses std::less<T *> to perform the comparison. This ensures that the comparison is handled correctly, since the standard mandates that relational operations on pointers are unspecified (5.9 [expr.rel] paragraph 2) but std::less<> on pointers is well-defined (20.3.3 [lib.comparisons] paragraph 8).

swap

template<class T>
  void swap(shared_array<T> & a, shared_array<T> & b) // never throws

Equivalent to a.swap(b). Matches the interface of std::swap. Provided as an aid to generic programming.

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Copyright 2002-2005 Peter Dimov. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


shared_array

Name

shared_array: Class template stores a pointer to a dynamically allocated array.

Synopsis

namespace boost {

  template<class T> class shared_array {

    public:
      typedef T element_type;

      explicit shared_array(T * p = 0);
      template<class D> shared_array(T * p, D d);
      ~shared_array(); // never throws

      shared_array(shared_array const & r); // never throws

      shared_array & operator=(shared_array const & r); // never throws

      void reset(T * p = 0);
      template<class D> void reset(T * p, D d);

      T & operator[](std::ptrdiff_t i) const() const; // never throws
      T * get() const; // never throws

      bool unique() const; // never throws
      long use_count() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(shared_array<T> & b); // never throws
  };

  template<class T>
    bool operator==(shared_array<T> const & a, shared_array<T> const & b); // never throws
  template<class T>
    bool operator!=(shared_array<T> const & a, shared_array<T> const & b); // never throws
  template<class T>
    bool operator<(shared_array<T> const & a, shared_array<T> const & b); // never throws

  template<class T> void swap(shared_array<T> & a, shared_array<T> & b); // never throws

}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


shared_ptr

shared_ptr class template

Introduction
Best Practices
Synopsis
Members
Free Functions
Example
Handle/Body Idiom
Thread Safety
Frequently Asked Questions
Smart Pointer Timings
Programming Techniques

Introduction

The shared_ptr class template stores a pointer to a dynamically allocated object, typically with a C++ new-expression. The object pointed to is deleted when the last shared_ptr pointing to it is destroyed or reset. See the example.

Every shared_ptr meets the CopyConstructible and Assignable requirements of the C++ Standard Library, and so can be used in standard library containers. Comparison operators are supplied so that shared_ptr works with the standard library's associative containers.

Normally, a shared_ptr cannot correctly hold a pointer to a dynamically allocated array. See shared_array for usage.

Because the implementation uses reference counting, cycles of shared_ptr instances will not be reclaimed. For example, if main() holds a shared_ptr to A, which directly or indirectly holds a shared_ptr back to A, A's use count will be 2. Destruction of the original shared_ptr will leave A dangling with a use count of 1. Use weak_ptr to "break cycles."

The class template is parameterized on T, the type of the object pointed to. shared_ptr and most of its member functions place no requirements on T; it is allowed to be an incomplete type, or void. Member functions that do place additional requirements (constructors, reset) are explicitly documented below.

shared_ptr<T> can be implicitly converted to shared_ptr<U> whenever T* can be implicitly converted to U*. In particular, shared_ptr<T> is implicitly convertible to shared_ptr<T const>, to shared_ptr<U> where U is an accessible base of T, and to shared_ptr<void>.

shared_ptr is now part of TR1, the first C++ Library Technical Report.

This implementation conforms to the TR1 specification, with the only exception that it resides in namespace boost instead of std::tr1.

Best Practices

A simple guideline that nearly eliminates the possibility of memory leaks is: always use a named smart pointer variable to hold the result of new. Every occurrence of the new keyword in the code should have the form:

shared_ptr<T> p(new Y);

It is, acceptable to use another smart pointer in place of shared_ptr ; having T and Y be the same type, or passing arguments to Y's constructor is also OK.

Observe this guideline, and there is no need for explicit deletes; try/catch constructs will be rare.

Avoid using unnamed shared_ptr temporaries to save typing; to see why this is dangerous, consider this example:

void f(shared_ptr<int>, int);
int g();

void ok()
{
    shared_ptr<int> p(new int(2));
    f(p, g());
}

void bad()
{
    f(shared_ptr<int>(new int(2)), g());
}

The function ok follows the guideline to the letter, whereas bad constructs the temporary shared_ptr in place, admitting the possibility of a memory leak. Since function arguments are evaluated in unspecified order, it is possible for new int(2) to be evaluated first, g() second, and we may never get to the shared_ptr constructor if g throws an exception. See Herb Sutter's treatment (also here) of the issue for more information.

Synopsis

namespace boost {

  class bad_weak_ptr: public std::exception;

  template<class T> class weak_ptr;

  template<class T> class shared_ptr {

    public:

      typedef T element_type;

      shared_ptr(); // never throws
      template<class Y> explicit shared_ptr(Y * p);
      template<class Y, class D> shared_ptr(Y * p, D d);
      template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
      ~shared_ptr(); // never throws

      shared_ptr(shared_ptr const & r); // never throws
      template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
      template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
      template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);

      shared_ptr & operator=(shared_ptr const & r); // never throws
      template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
      template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);

      void reset(); // never throws
      template<class Y> void reset(Y * p);
      template<class Y, class D> void reset(Y * p, D d);
      template<class Y, class D, class A> void reset(Y * p, D d, A a);

      T & operator*() const; // never throws
      T * operator->() const; // never throws
      T * get() const; // never throws

      bool unique() const; // never throws
      long use_count() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(shared_ptr & b); // never throws
  };

  template<class T, class U>
    bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

  template<class T, class U>
    bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

  template<class T, class U>
    bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

  template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws

  template<class T> T * get_pointer(shared_ptr<T> const & p); // never throws

  template<class T, class U>
    shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws

  template<class T, class U>
    shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r); // never throws

  template<class T, class U>
    shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r); // never throws

  template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);

  template<class D, class T>
    D * get_deleter(shared_ptr<T> const & p);
}

Members

element_type

typedef T element_type;

Provides the type of the template parameter T.

constructors

shared_ptr(); // never throws

Effects: Constructs an empty shared_ptr.

Postconditions: use_count() == 0 && get() == 0

Throws: nothing

[The nothrow guarantee is important, since reset() is specified in terms of the default constructor; this implies that the constructor must not allocate memory.]

template<class Y> explicit shared_ptr(Y * p);

Requirements: p must be convertible to T *. Y must be a complete type. The expression delete p must be well-formed, must not invoke undefined behavior, and must not throw exceptions.

Effects: Constructs a shared_ptr that owns the pointer p.

Postconditions: use_count() == 1 && get() == p

Throws: std::bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.

Exception safety: If an exception is thrown, delete p is called.

Notes: p must be a pointer to an object that was allocated via a C++ new expression or be 0. The post condition that use count is 1 holds even if p is 0; invoking delete on a pointer that has a value of 0 is harmless.

[This constructor has been changed to a template in order to remember the actual pointer type passed. The destructor will call delete with the same pointer, complete with its original type, even when T does not have a virtual destructor, or is void.

The optional intrusive counting support has been dropped as it exposes too much implementation details and doesn't interact well with weak_ptr. The current implementation uses a different mechanism, enable_shared_from_this, to solve the "shared_ptr from this" problem.]

template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);

Requirements: p must be convertible to T *. D must be CopyConstructible. The copy constructor and destructor of D must not throw. The expression d(p) must be well-formed, must not invoke undefined behavior, and must not throw exceptions. A must be an Allocator, as described in section 20.1.5 (Allocator requirements) of the C++ Standard.

Effects: Constructs a shared_ptr that owns the pointer p and the deleter d. The second constructor allocates memory using a copy of a.

Postconditions: use_count() == 1 && get() == p.

Throws: std::bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.

Exception safety: If an exception is thrown, d(p) is called.

Notes: When the the time comes to delete the object pointed to by p, the stored copy of d is invoked with the stored copy of p as an argument.

[Custom deallocators allow a factory function returning a shared_ptr to insulate the user from its memory allocation strategy. Since the deallocator is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation. For example, a "no-op" deallocator is useful when returning a shared_ptr to a statically allocated object, and other variations allow a shared_ptr to be used as a wrapper for another smart pointer, easing interoperability.

The support for custom deallocators does not impose significant overhead. Other shared_ptr features still require a deallocator to be kept.

The requirement that the copy constructor of D does not throw comes from the pass by value. If the copy constructor throws, the pointer is leaked. Removing the requirement requires a pass by (const) reference.

The main problem with pass by reference lies in its interaction with rvalues. A const reference may still cause a copy, and will require a const operator(). A non-const reference will not bind to an rvalue at all. A good solution to this problem is the rvalue reference proposed in N1377/N1385.]

shared_ptr(shared_ptr const & r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws

Effects: If r is empty, constructs an empty shared_ptr; otherwise, constructs a shared_ptr that shares ownership with r.

Postconditions: get() == r.get() && use_count() == r.use_count().

Throws: nothing.

template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);

Effects: Constructs a shared_ptr that shares ownership with r and stores a copy of the pointer stored in r.

Postconditions: use_count() == r.use_count().

Throws: bad_weak_ptr when r.use_count() == 0.

Exception safety: If an exception is thrown, the constructor has no effect.

template<class Y> shared_ptr(std::auto_ptr<Y> & r);

Effects: Constructs a shared_ptr, as if by storing a copy of r.release().

Postconditions: use_count() == 1.

Throws: std::bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.

Exception safety: If an exception is thrown, the constructor has no effect.

[This constructor takes a the source auto_ptr by reference and not by value, and cannot accept auto_ptr temporaries. This is by design, as the constructor offers the strong guarantee; an rvalue reference would solve this problem, too.]

destructor

~shared_ptr(); // never throws

Effects:

  • If *this is empty, or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
  • Otherwise, if *this owns a pointer p and a deleter d, d(p) is called.
  • Otherwise, *this owns a pointer p, and delete p is called.

Throws: nothing

assignment

shared_ptr & operator=(shared_ptr const & r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);

Effects: Equivalent to shared_ptr(r).swap(*this).

Returns: *this

Notes: The use count updates caused by the temporary object construction and destruction are not considered observable side effects, and the implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary. In particular, in the example:

shared_ptr<int> p(new int);
shared_ptr<void> q(p);
p = p;
q = p;

both assignments may be no-ops.

reset

void reset(); // never throws

Effects: Equivalent to shared_ptr().swap(*this).

template<class Y> void reset(Y * p);

Effects: Equivalent to shared_ptr(p).swap(*this).

template<class Y, class D> void reset(Y * p, D d);

Effects: Equivalent to shared_ptr(p, d).swap(*this).

template<class Y, class D, class A> void reset(Y * p, D d, A a);

Effects: Equivalent to shared_ptr(p, d, a).swap(*this).

indirection

T & operator*() const; // never throws

Requirements: The stored pointer must not be 0.

Returns: a reference to the object pointed to by the stored pointer.

Throws: nothing

T * operator->() const; // never throws

Requirements: The stored pointer must not be 0.

Returns: the stored pointer.

Throws: nothing

get

T * get() const; // never throws

Returns: the stored pointer.

Throws: nothing

unique

bool unique() const; // never throws

Returns: use_count() == 1.

Throws: nothing

Notes: unique() may be faster than use_count(). If you are using unique() to implement copy on write, do not rely on a specific value when the stored pointer is zero.

use_count

long use_count() const; // never throws

Returns: the number of shared_pt r objects, *this included, that share ownership with *this, or an unspecified nonnegative value when *this is empty.

Throws: nothing

Notes: use_count() is not necessarily efficient. Use only for debugging and testing purposes, not for production code.

conversions

operator unspecified-bool-type () const; // never throws

Returns: an unspecified value that, when used in boolean contexts, is equivalent to get() != 0.

Throws: nothing

Notes: This conversion operator allows shared_ptr objects to be used in boolean contexts, like if (p && p->valid()) {}. The actual target type is typically a pointer to a member function, avoiding many of the implicit conversion pitfalls.

[The conversion to bool is not merely syntactic sugar. It allows shared_ptrs to be declared in conditions when using dynamic_pointer_cast or weak_ptr::lock.]

swap

void swap(shared_ptr & b); // never throws

Effects: Exchanges the contents of the two smart pointers.

Throws: nothing

Free Functions

comparison

template<class T, class U>
  bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

Returns: a.get() == b.get().

Throws: nothing

template<class T, class U>
  bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

Returns: a.get() != b.get().

Throws: nothing

template<class T, class U>
  bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

Returns: an unspecified value such that

  • operator< is a strict weak ordering as described in section 25.3 [lib.alg.sorting] of the C++ standard;

  • under the equivalence relation defined by operator<, !(a < b) && !(b < a), two shared_ptr instances are equivalent if and only if they share ownership or are both empty.

Throws: nothing.

Notes: Allows shared_ptr objects to be used as keys in associative containers.

[Operator< has been preferred over a std::less specialization for consistency and legality reasons, as std::less is required to return the results of operator<, and many standard algorithms use operator< instead of std::less for comparisons when a predicate is not supplied. Composite objects, like std::pair, also implement their operator< in terms of their contained subobjects' operator<.The rest of the comparison operators are omitted by design.]

swap

template<class T>
  void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws

Effects: Equivalent to a.swap(b).

Throws: nothing.

Notes: Matches the interface of std::swap. Provided as an aid to generic programming.

[swap is defined in the same namespace as shared_ptr as this is currently the only legal way to supply a swap function that has a chance to be used by the standard library.]

get_pointer

template<class T>
  T * get_pointer(shared_ptr<T> const & p); // never throws

Returns: p.get().

Throws: nothing.

Notes: Provided as an aid to generic programming. Used by mem_fn.

static_pointer_cast

template<class T, class U>
  shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws

Requires: The expression static_cast<T*>(r.get()) must be well-formed.

Returns: If r is empty, an empty shared_ptr<T>; otherwise, a shared_ptr<T> object that stores a copy of static_cast<T*>(r.get()) and shares ownership with r.

Throws: nothing.

Notes: the seemingly equivalent expression

shared_ptr<T>(static_cast<T*>(r.get()))

will eventually result in undefined behavior, attempting to delete the same object twice.

const_pointer_cast

template<class T, class U>
  shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r); // never throws

Requires: The expression const_cast<T*>(r.get()) must be well-formed.

Returns: If r is empty, an empty shared_ptr<T>; otherwise, a shared_ptr<T> object that stores a copy of const_cast<T*>(r.get()) and shares ownership with r.

Throws: nothing.

Notes: the seemingly equivalent expression:

shared_ptr<T>(const_cast<T*>(r.get()))

will eventually result in undefined behavior, attempting to delete the same object twice.

dynamic_pointer_cast

template<class T, class U>
  shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r);

Requires: The expression dynamic_cast<T*>(r.get()) must be well-formed and its behavior defined.

Returns:

  • When dynamic_cast<T*>(r.get()) returns a nonzero value, a shared_ptr<T> object that stores a copy of it and shares ownership with r;

  • Otherwise, an empty shared_ptr<T> object.

Throws: nothing.

Notes: the seemingly equivalent expression

shared_ptr<T>(dynamic_cast<T*>(r.get()))

will eventually result in undefined behavior, attempting to delete the same object twice.

operator<<

template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);

Effects: os << p.get();.

Returns: os

get_deleter

template<class D, class T>
    D * get_deleter(shared_ptr<T> const & p);

Returns: If *this owns a deleter d of type (cv-unqualified) D, returns &d; otherwise returns 0.

Example

See shared_ptr_example.cpp for a complete example program. The program builds a std::vector and std::set of shared_ptr objects.

Note that after the containers have been populated, some of the shared_ptr objects will have a use count of 1 rather than a use count of 2, since the set is a std::set rather than a std::multiset, and thus does not contain duplicate entries. Furthermore, the use count may be even higher at various times while push_back and insert container operations are performed. More complicated yet, the container operations may throw exceptions under a variety of circumstances. Getting the memory management and exception handling in this example right without a smart pointer would be a nightmare.

Handle/Body Idiom

One common usage of shared_ptr is to implement a handle/body (also called pimpl) idiom which avoids exposing the body (implementation) in the header file.

The shared_ptr_example2_test.cpp sample program includes a header file, shared_ptr_example2.hpp, which uses a shared_ptr<> to an incomplete type to hide the implementation. The instantiation of member functions which require a complete type occurs in the shared_ptr_example2.cpp implementation file. Note that there is no need for an explicit destructor. Unlike ~scoped_ptr, ~shared_ptr does not require that T be a complete type.

Thread Safety

shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)

Any other simultaneous accesses result in undefined behavior.

Examples:

shared_ptr<int> p(new int(42));

//--- Example 1 ---

// thread A
shared_ptr<int> p2(p); // reads p

// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe

//--- Example 2 ---

// thread A
p.reset(new int(1912)); // writes p

// thread B
p2.reset(); // OK, writes p2

//--- Example 3 ---

// thread A
p = p3; // reads p3, writes p

// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write

//--- Example 4 ---

// thread A
p3 = p2; // reads p2, writes p3

// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"

//--- Example 5 ---

// thread A
p3.reset(new int(1));

// thread B
p3.reset(new int(2)); // undefined, multiple writes

Starting with Boost release 1.33.0, shared_ptr uses a lock-free implementation on the following platforms:

  • GNU GCC on x86 or x86-64;
  • GNU GCC on IA64;
  • Metrowerks CodeWarrior on PowerPC;
  • GNU GCC on PowerPC;
  • Windows.

If the program is single-threaded and does not link to any libraries that might have used shared_ptr in its default configuration, then #define the macro BOOST_SP_DISABLE_THREADS on a project-wide basis to switch to ordinary non-atomic reference count updates.

(Defining BOOST_SP_DISABLE_THREADS in some, but not all, translation units is technically a violation of the One Definition Rule and undefined behavior. Nevertheless, the implementation attempts to do its best to accommodate the request to use non-atomic updates in those translation units. No guarantees, though.)

Define the macro BOOST_SP_USE_PTHREADS to turn off the lock-free platform-specific implementation and fall back to the generic pthread_mutex_t-based code.

Frequently Asked Questions

Q. There are several variations of shared pointers, with different tradeoffs; why does the smart pointer library supply only a single implementation? It would be useful to be able to experiment with each type so as to find the most suitable for the job at hand?

A. An important goal of shared_ptr is to provide a standard shared-ownership pointer. Having a single pointer type is important for stable library interfaces, since different shared pointers typically cannot interoperate, i.e. a reference counted pointer (used by library A) cannot share ownership with a linked pointer (used by library B.)

Q. Why doesn't shared_ptr have template parameters supplying traits or policies to allow extensive user customization?

A. Parameterization discourages users. The shared_ptr template is carefully crafted to meet common needs without extensive parameterization. Some day a highly configurable smart pointer may be invented that is also very easy to use and very hard to misuse. Until then, shared_ptr is the smart pointer of choice for a wide range of applications. (If interested in policy based smart pointers read Modern C++ Design by Andrei Alexandrescu.)

Q. I am not convinced. Default parameters can be used where appropriate to hide the complexity. Again, why not policies?

A. Template parameters affect the type. See the answer to the first question above.

Q. Why doesn't shared_ptr use a linked list implementation?

A. A linked list implementation does not offer enough advantages to offset the added cost of an extra pointer. See timings page. In addition, it is expensive to make a linked list implementation thread safe.

Q. Why doesn't shared_ptr (or any of the other Boost smart pointers) supply an automatic conversion to T*?

A. Automatic conversion is believed to be too error prone.

Q. Why does shared_ptr supply use_count()?

A. As an aid to writing test cases and debugging displays. One of the progenitors had use_count(), and it was useful in tracking down bugs in a complex project that turned out to have cyclic-dependencies.

Q. Why doesn't shared_ptr specify complexity requirements?

A. Because complexity requirements limit implementors and complicate the specification without apparent benefit to shared_ptr users. For example, error-checking implementations might become non-conforming if they had to meet stringent complexity requirements.

Q. Why doesn't shared_ptr provide a release() function?

A. shared_ptr cannot give away ownership unless it's unique() because the other copy will still destroy the object.

Consider:

shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2

int * p = a.release();

// Who owns p now? b will still call delete on it in its destructor.

Furthermore, the pointer returned by release() would be difficult to deallocate reliably, as the source shared_ptr could have been created with a custom deleter.

Q. Why is operator->() const, but its return value is a non-const pointer to the element type?

A. Shallow copy pointers, including raw pointers, typically don't propagate constness. It makes little sense for them to do so, as you can always obtain a non-const pointer from a const one and then proceed to modify the object through it.shared_ptr is "as close to raw pointers as possible but no closer".

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Copyright 2002-2005 Peter Dimov. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


shared_ptr

Name

shared_ptr: Class template stores a pointer to a dynamically allocated object, typically with a C++ new-expression.

Synopsis

namespace boost {

  class bad_weak_ptr: public std::exception;

  template<class T> class weak_ptr;

  template<class T> class shared_ptr {

    public:

      typedef T element_type;

      shared_ptr(); // never throws
      template<class Y> explicit shared_ptr(Y * p);
      template<class Y, class D> shared_ptr(Y * p, D d);
      template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
      ~shared_ptr(); // never throws

      shared_ptr(shared_ptr const & r); // never throws
      template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
      template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
      template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);

      shared_ptr & operator=(shared_ptr const & r); // never throws
      template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
      template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);

      void reset(); // never throws
      template<class Y> void reset(Y * p);
      template<class Y, class D> void reset(Y * p, D d);
      template<class Y, class D, class A> void reset(Y * p, D d, A a);

      T & operator*() const; // never throws
      T * operator->() const; // never throws
      T * get() const; // never throws

      bool unique() const; // never throws
      long use_count() const; // never throws

      operator unspecified-bool-type() const; // never throws

      void swap(shared_ptr & b); // never throws
  };

  template<class T, class U>
    bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

  template<class T, class U>
    bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

  template<class T, class U>
    bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

  template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws

  template<class T> T * get_pointer(shared_ptr<T> const & p); // never throws

  template<class T, class U>
    shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws

  template<class T, class U>
    shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r); // never throws

  template<class T, class U>
    shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r); // never throws

  template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);

  template<class D, class T>
    D * get_deleter(shared_ptr<T> const & p);
}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Smart Pointers

Introduction
Common Requirements
Exception Safety
Exception-specifications

Introduction

Smart pointers are objects that store pointers to dynamically allocated (heap) objects. They behave much like built-in C++ pointers except that they automatically delete the object pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects. They can also be used to keep track of dynamically allocated objects shared by multiple owners.

Conceptually, smart pointers are seen as owning the object pointed to, and thus responsible for deletion of the object when it is no longer needed.

The smart pointer library provides five smart pointer class templates:

scoped_ptr | <boost/scoped_ptr.hpp> | Simple sole ownership of single objects. Non-copyable.
scoped_array | <boost/scoped_array.hpp> | Simple sole ownership of arrays. Non-copyable.
shared_ptr | <boost/shared_ptr.hpp> | Object ownership shared among multiple pointers
shared_array | <boost/shared_array.hpp> | Array ownership shared among multiple pointers.
weak_ptr | <boost/weak_ptr.hpp> | Non-owning observers of an object owned by shared_ptr.
intrusive_ptr | <boost/intrusive_ptr.hpp> | Shared ownership of objects with an embedded reference count.

These templates are designed to complement the std::auto_ptr template.

A page on compatibility with older versions of the Boost smart pointer library describes some of the changes since earlier versions of the smart pointer implementation.

A page on smart pointer timings will be of interest to those curious about performance issues.

A page on smart pointer programming techniques lists some advanced applications of shared_ptr and weak_ptr.

Common Requirements

The smart pointer class templates have a template parameter, T, that specifies the type of the object pointed to by the smart pointer. The behavior of the smart pointer templates is undefined if the destructor or operator delete for objects of type T throw exceptions.

T may be an incomplete type at the point of smart pointer declaration. Unless otherwise specified, it is required that T be a complete type at the point of smart pointer instantiation. Implementations are required to diagnose (treat as an error) all violations of this requirement, including deletion of an incomplete type. See the description of the checked_delete function template.

Note: shared_ptr does not have this restriction, as most of its member functions do not require T to be a complete type.

Rationale

The requirements on T are crafted to maximize safety yet allow handle-body (also called pimpl) and similar idioms. In these idioms a smart pointer may appear in translation units where T is an incomplete type. This separates interface from implementation and hides implementation from translation units which merely use the interface.

Note: scoped_ptr requires that T be a complete type at destruction time, but shared_ptr does not.

Exception Safety

Several functions in the smart pointer classes are specified as having "no effect" or "no effect except such-and-such" if an exception is thrown. This means that when an exception is thrown by an object of one of these classes, the entire program state remains the same as it was prior to the function call which resulted in the exception being thrown. This amounts to a guarantee that there are no detectable side effects. Other functions never throw exceptions. The only exception ever thrown by functions which do throw (assuming T meets the common requirements) is std::bad_alloc, and is thrown only by functions which are explicitly documented as possibly throwing std::bad_alloc.

Exception-specifications

Exception-specifications are not used; see exception-specification rationale.

All the smart pointer templates contain member functions which can never throw exceptions, because they neither throw exceptions themselves, nor call other functions which may throw exceptions. These members are indicated by a comment: // never throws.

Functions which destroy objects of the pointed to type are prohibited from throwing exceptions by the common requirements.

Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Permission to copy, use, modify, sell and distribute this document is
granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Name

smart_ptr: Objects that store pointers to dynamically allocated (heap) objects.

Synopsis

NA

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.


Smart Pointer Timings

In late January 2000, Mark Borgerding put forward a suggestion to boost for a new design of smart pointer whereby an intrusive doubly linked list is used to join together all instances of smart pointers sharing a given raw pointer. This allowed avoidance of the costly heap allocation of a reference count that occurred in the initial construction of the then current version of boost::shared_ptr. Of course, nothing is for free and the benefit here was gained at the expense of increased size and more costly copy operations. A debate ensued on the boost mailing list and the tests which this page describes were performed to provide a guide for current and future investigations into smart pointer implementation strategies.

Description

Two tests were run: the first aimed to obtain timings for two basic individual operations:

  1. Initial construction from raw pointer.
  2. An amortized copy operation consisting of half an assignment and half a copy construction - designed to reflect average usage.

The second attempted to gain more insight into normal usage by timing the fill and sort algorithms for vectors and lists filled with the various smart pointers.

Five smart pointer implementation strategies were tested:

  1. Counted pointer using a heap allocated reference count, this is referred to as simple counted.
  2. Counted pointer using a special purpose allocator for the reference count - special counted.
  3. Counted pointer using an intrusive reference count - intrusive.
  4. Linked pointer as described above - linked.
  5. Cyclic pointer, a counted implementation using a std::deque for allocation with provision for weak pointers and garbage collection of cycles of pointers - cyclic.

on two compilers:

  1. MSVC 6.0 service pack 3, using default release optimization mode (/O2 - optimized for speed, no inlining of functions defined outside a class body unless specified as inline).
  2. gcc 2.95.2 using full optimization (-O3 -DNDEBUG).

Additionally, generated pointer sizes (taking into account struct alignment) were compared, as were generated code sizes for MSVC mainly by manual inspection of generated assembly code - a necessity due to function inlining.

All tests were run on a PII-200 running Windows NT version 4.0

Operation Timing Test Results

The following graphs show the overall time in nanoseconds to acquire a pointer (default construction) perform n amortized copy operations on it and finally release it. The initial allocation time for the contained pointer is not included, although the time for it's deallocation is. The contained pointer pointed to a trivial class, but for the inclusion of an intrusive reference count for the benefit of the intrusive counted shared pointer. A dumb pointer (i.e. a smart pointer that simply acquires and releases its contained pointer with no extra overhead) and a raw pointer were also included for comparison.

MSVC speed graph
GCC speed graph

Fitting straight lines to the above plots gives the following figures for initialization and amortized copy operation for the two compilers (times in nanoseconds, errors at two standard deviations) : -

MSVC

 | initialization | copy operation
simple counted | 3000 +/- 170 | 104 +/- 31
special counted | 1330 +/- 50 | 85 +/- 9
intrusive | 1000 +/- 20 | 71 +/- 3
linked | 970 +/- 60 | 136 +/- 10
cyclic | 1290 +/- 70 | 112 +/- 12
dumb | 1020 +/- 20 | 10 +/- 4
raw | 1038 +/- 30 | 10 +/- 5

GCC

 | initialization | copy operation
simple counted | 4620 +/- 150 | 301 +/- 28
special counted | 1990 +/- 40 | 264 +/- 7
intrusive | 1590 +/- 70 | 181 +/- 12
linked | 1470 +/- 140 | 345 +/- 26
cyclic | 2180 +/- 100 | 330 +/- 18
dumb | 1590 +/- 70 | 74 +/- 12
raw | 1430 +/- 60 | 27 +/- 11

Note that the above times include a certain amount of loop overhead etc. for each operation. An estimate of the pure smart pointer operation time 'overhead' can be obtained by subtracting the dumb or raw figure from the smart pointer time of interest.

Detail

The test involved iterating a loop which creates raw pointers. These were then shared among a varying number (set size) of smart pointers. A range of set sizes was used and then a line fitted to get a linear relation with number of initializations and copy-operations. A spreadsheet was used for the line fit, and to produce the performance graphs above.

Container Test Results

This test was devised to gain some insight in to operation within real life programs. Smart pointers were used to fill standard containers which were then sorted.

In this case, the contained pointer pointed to a class which initializes a private data member to a random value in its default constructor. This value is used subsequently for the sort comparison test. The class also contains an intrusive reference count for the benefit of the intrusive counted pointer.

All times are in seconds for 300,000 contained pointers.

GCC

 | vector |  | list
 | fill | sort | fill | sort
simple counted | 46.54 | 2.44 | 47.09 | 3.22
special counted | 14.02 | 2.83 | 7.28 | 3.21
intrusive | 12.15 | 1.91 | 7.99 | 3.08
linked | 12.46 | 2.32 | 8.14 | 3.27
cyclic | 22.60 | 3.19 | 1.63 | 3.18
raw | 11.81 | 0.24 | 27.51 | 0.77

MSVC

 | vector |  | list
 | fill | sort | fill | sort
simple counted | 1.83 | 2.37 | 1.86 | 4.85
special counted | 1.04 | 2.35 | 1.38 | 4.58
intrusive | 1.04 | 1.84 | 1.16 | 4.29
linked | 1.08 | 2.00 | 1.21 | 4.33
cyclic | 1.38 | 2.84 | 1.47 | 4.73
raw | 0.67 | 0.28 | 1.24 | 1.81

Code Size

The following code sizes were determined by inspection of generated code for MSVC only. Sizes are given in the form N / M / I where:

  • N is the instruction count of the operation

  • M is the size of the code in bytes

  • I determines whether generated code was inlined or not I = inline, O = "outline"

 | ptr() | ptr(p) | ptr(ptr) | op=() | ~ptr()
simple counted | 38/110/O | 38/110/O | 9/23/I | 22/57/I | 17/40/I
special counted | 50/141/O | 50/141/O | 9/23/I | 23/64/I | 13/38/I
intrusive | 1/2/I | 3/6/I | 3/6/I | 6/11/I | 6/11/I
linked | 5/19/I | 5/15/I | 10/30/I | 27/59/I | 14/38/I

During the code inspection, a couple of minor points were noticed: -

  • Function inlining was critical to performance.

  • For MSVC, at least, a "delete 0" caused execution of 11 assembly instructions, including a function call. So in cases where performance is at an absolute premium it can be worth inserting the extra manual test.

Data Size

The following smart pointer sizes were obtained in bytes

 | MSVC | GCC
simple counted | 8 | 8
special counted | 8 | 12
intrusive | 4 | 4
linked | 12 | 12
cyclic | 8 | 8

Summary

The timing results mainly speak for themselves: clearly an intrusive pointer outperforms all others and a simple heap based counted pointer has poor performance relative to other implementations. The selection of an optimal non-intrusive smart pointer implementation is more application dependent, however. Where small numbers of copies are expected, it is likely that the linked implementation will be favoured. Conversely, for larger numbers of copies a counted pointer with some type of special purpose Allocator looks like a win. Other factors to bear in mind are: -

  • Deterministic individual, as opposed to amortized, operation time. This weighs against any implementation depending on an Allocator.

  • Multithreaded synchronization. This weighs against an implementation which spreads its information as in the case of linked pointer.

© Copyright Gavin Collings 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Smart Pointer Programming Techniques

Using incomplete classes for implementation hiding
The "Pimpl" idiom
Using abstract classes for implementation hiding
 Preventing delete px.get()
Using a shared_ptr to hold a pointer to an array
Encapsulating allocation details, wrapping factory functions
 Using a shared_ptr to hold a pointer to a statically allocated object
Using a shared_ptr to hold a pointer to a COM object
Using a shared_ptr to hold a pointer to an object with an embedded reference count
Using a shared_ptr to hold another shared ownership smart pointer
Obtaining a shared_ptr from a raw pointer
Obtaining a shared_ptr (weak_ptr) to this in a constructor
Obtaining a shared_ptr to this
Using shared_ptr as a smart counted handle
Using shared_ptr to execute code on block exit
Using shared_ptr<void> to hold an arbitrary object
Associating arbitrary data with heterogeneous shared_ptr instances
Using shared_ptr as a CopyConstructible mutex lock
Using shared_ptr to wrap member function calls
Delayed deallocation
 Weak pointers to objects not managed by a shared_ptr

Using incomplete classes for implementation hiding

A proven technique (that works in C, too) for separating interface from implementation is to use a pointer to an incomplete class as an opaque handle:

class FILE;

FILE * fopen(char const * name, char const * mode);
void fread(FILE * f, void * data, size_t size);
void fclose(FILE * f);

It is possible to express the above interface using shared_ptr, eliminating the need to manually call fclose:

class FILE;

shared_ptr<FILE> fopen(char const * name, char const * mode);
void fread(shared_ptr<FILE> f, void * data, size_t size);

This technique relies on shared_ptr's ability to execute a custom deleter, eliminating the explicit call to fclose, and on the fact that shared_ptr<X> can be copied and destroyed when X is incomplete.

The "Pimpl" idiom

A C++ specific variation of the incomplete class pattern is the "Pimpl" idiom. The incomplete class is not exposed to the user; it is hidden behind a forwarding facade. shared_ptr can be used to implement a "Pimpl":

// file.hpp:

class file
{
private:

    class impl;
    shared_ptr<impl> pimpl_;

public:

    file(char const * name, char const * mode);

    // compiler generated members are fine and useful

    void read(void * data, size_t size);
};

// file.cpp:

#include "file.hpp"

class file::impl
{
private:

    impl(impl const &);
    impl & operator=(impl const &);

    // private data

public:

    impl(char const * name, char const * mode) { ... }
    ~impl() { ... }
    void read(void * data, size_t size) { ... }
};

file::file(char const * name, char const * mode): pimpl_(new impl(name, mode))
{
}

void file::read(void * data, size_t size)
{
    pimpl_->read(data, size);
}

The key thing to note here is that the compiler-generated copy constructor, assignment operator, and destructor all have a sensible meaning. As a result, file is CopyConstructible and Assignable, allowing its use in standard containers.

Using abstract classes for implementation hiding

Another widely used C++ idiom for separating interface and implementation is to use abstract base classes and factory functions. The abstract classes are sometimes called "interfaces" and the pattern is known as "interface-based programming". Again, shared_ptr can be used as the return type of the factory functions:

// X.hpp:

class X
{
public:

    virtual void f() = 0;
    virtual void g() = 0;

protected:

    ~X() {}
};

shared_ptr<X> createX();

-- X.cpp:

class X_impl: public X
{
private:

    X_impl(X_impl const &);
    X_impl & operator=(X_impl const &);

public:

    virtual void f()
    {
      // ...
    }

    virtual void g()
    {
      // ...
    }
};

shared_ptr<X> createX()
{
    shared_ptr<X> px(new X_impl);
    return px;
}

A key property of shared_ptr is that the allocation, construction, deallocation, and destruction details are captured at the point of construction, inside the factory function.
Note: The protected and nonvirtual destructor in the example above. The client code cannot, and does not need to, delete a pointer to X; the shared_ptr<X> instance returned from createX will correctly call ~X_impl.

Preventing delete px.get()

It is often desirable to prevent client code from deleting a pointer that is being managed by shared_ptr. The previous technique showed one possible approach, using a protected destructor. Another alternative is to use a private deleter:

class X
{
private:

    ~X();

    class deleter;
    friend class deleter;

    class deleter
    {
    public:

        void operator()(X * p) { delete p; }
    };

public:

    static shared_ptr<X> create()
    {
        shared_ptr<X> px(new X, X::deleter());
        return px;
    }
};

Using a shared_ptr to hold a pointer to an array

A shared_ptr can be used to hold a pointer to an array allocated with new[]:

shared_ptr<X> px(new X[1], checked_array_deleter<X>());

Note:That shared_array is often preferable, if this is an option. It has an array-specific interface, without operator* and operator->, and does not allow pointer conversions.

Encapsulating allocation details, wrapping factory functions

shared_ptr can be used in creating C++ wrappers over existing C style library interfaces that return raw pointers from their factory functions to encapsulate allocation details. As an example, consider this interface, where CreateX might allocate X from its own private heap, ~X may be inaccessible, or X may be incomplete:

X * CreateX();
void DestroyX(X *);

The only way to reliably destroy a pointer returned by CreateX is to call DestroyX.

Here is how a shared_ptr-based wrapper may look like:

shared_ptr<X> createX()
{
    shared_ptr<X> px(CreateX(), DestroyX);
    return px;
}

Client code that calls CreateX still does not need to know how the object has been allocated, but now the destruction is automatic.

Using a shared_ptr to hold a pointer to a statically allocated object

Sometimes it is desirable to create a shared_ptr to an already existing object, so that the shared_ptr does not attempt to destroy the object when there are no more references left. As an example, the factory function:

shared_ptr<X> createX();

in certain situations may need to return a pointer to a statically allocated X instance.

The solution is to use a custom deleter that does nothing:

struct null_deleter
{
    void operator()(void const *) const
    {
    }
};

static X x;

shared_ptr<X> createX()
{
    shared_ptr<X> px(&x, null_deleter());
    return px;
}

The same technique works for any object known to outlive the pointer.

Using a shared_ptr to hold a pointer to a COM Object

Background: COM objects have an embedded reference count and two member functions that manipulate it. AddRef() increments the count. Release() decrements the count and destroys itself when the count drops to zero.

It is possible to hold a pointer to a COM object in a shared_ptr:

shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p)
{
    p->AddRef();
    shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release));
    return pw;
}

Note: That shared_ptr copies created from pw will not "register" in the embedded count of the COM object; they will share the single reference created in make_shared_from_COM. Weak pointers created from pw will be invalidated when the last shared_ptr is destroyed, regardless of whether the COM object itself is still alive.

As explained in the mem_fn documentation, #define BOOST_MEM_FN_ENABLE_STDCALL must be done first.rst.

Using a shared_ptr to hold a pointer to an object with an embedded reference count

This is a generalization of the above technique. The example assumes that the object implements the two functions required by intrusive_ptr , intrusive_ptr_add_ref and intrusive_ptr_release:

template<class T> struct intrusive_deleter
{
    void operator()(T * p)
    {
        if(p) intrusive_ptr_release(p);
    }
};

shared_ptr<X> make_shared_from_intrusive(X * p)
{
    if(p) intrusive_ptr_add_ref(p);
    shared_ptr<X> px(p, intrusive_deleter<X>());
    return px;
}

Using a shared_ptr to hold another shared ownership smart pointer

One of the design goals of shared_ptr is to be used in library interfaces. It is possible to encounter a situation where a library takes a shared_ptr argument, but the object at hand is being managed by a different reference counted or linked smart pointer.

It is possible to exploit shared_ptr's custom deleter feature to wrap this existing smart pointer behind a shared_ptr facade:

template<class P> struct smart_pointer_deleter
{
private:

    P p_;

public:

    smart_pointer_deleter(P const & p): p_(p)
    {
    }

    void operator()(void const *)
    {
        p_.reset();
    }

    P const & get() const
    {
        return p_;
    }
};

shared_ptr<X> make_shared_from_another(another_ptr<X> qx)
{
    shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx));
    return px;
}

One subtle point is that deleters are not allowed to throw exceptions, and the above example assumes that p_.reset() doesn't throw. If this is not the case, p_.reset() should be wrapped in a try {} catch(...) {} block that ignores exceptions. In the (usually unlikely) event when an exception is thrown and ignored, p_ will be released when the lifetime of the deleter ends. This happens when all references, including weak pointers, are destroyed or reset.

Another twist is that it is possible, given the above shared_ptr instance, to recover the original smart pointer, using get_deleter :

void extract_another_from_shared(shared_ptr<X> px)
{
    typedef smart_pointer_deleter< another_ptr<X> > deleter;

    if(deleter const * pd = get_deleter<deleter>(px))
    {
        another_ptr<X> qx = pd->get();
    }
    else
    {
        // not one of ours
    }
}

Obtaining a shared_ptr from a raw pointer

Sometimes it is necessary to obtain a shared_ptr given a raw pointer to an object that is already managed by another shared_ptr instance. For example:

void f(X * p)
{
    shared_ptr<X> px(???);
}

Inside f, create a shared_ptr to *p.

In the general case, this problem has no solution. One approach is to modify f to take a shared_ptr, if possible:

void f(shared_ptr<X> px);

The same transformation can be used for nonvirtual member functions, to convert the implicit this:

void X::f(int m);

would become a free function with a shared_ptr first argument:

void f(shared_ptr<X> this_, int m);

If f cannot be changed, but X uses intrusive counting, use make_shared_from_intrusive described above. Or, if it's known that the shared_ptr created in f will never outlive the object, use a null deleter.

Obtaining a shared_ptr (weak_ptr) to thisis in a constructor

Some designs require objects to register themselves on construction with a central authority. When the registration routines take a shared_ptr, this leads to the question how could a constructor obtain a shared_ptr to this:

class X X
{
public:

    X()
    {
        shared_ptr<X> this_(???);
    }
};

In the general case, the problem cannot be solved. The X instance being constructed can be an automatic variable or a static variable; it can be created on the heap:

shared_ptr<X> px(new X);

but at construction time, px does not exist yet, and it is impossible to create another shared_ptr instance that shares ownership with it.

Depending on context, if the inner shared_ptr this_ doesn't need to keep the object alive, use a null_deleter as explained here and here. If X is supposed to always live on the heap, and be managed by a shared_ptr, use a static factory function:

class X
{
private:

    X() { ... }

public:

    static shared_ptr<X> create()
    {
        shared_ptr<X> px(new X);
        // use px as 'this_'
        return px;
    }
};

Obtaining a shared_ptr to this

Sometimes it is needed to obtain a shared_ptr from this in a virtual member function under the assumption that this is already managed by a shared_ptr. The transformations described in the previous technique cannot be applied.

A typical example:

class X
{
public:

    virtual void f() = 0;

protected:

    ~X() {}
};

class Y
{
public:

    virtual shared_ptr<X> getX() = 0;

protected:

    ~Y() {}
};

// --

class impl: public X, public Y
{
public:

    impl() { ... }

    virtual void f() { ... }

    virtual shared_ptr<X> getX()
    {
        shared_ptr<X> px(???);
        return px;
    }
};

The solution is to keep a weak pointer to this as a member in impl:

class impl: public X, public Y
{
private:

    weak_ptr<impl> weak_this;

    impl(impl const &);
    impl & operator=(impl const &);

    impl() { ... }

public:

    static shared_ptr<impl> create()
    {
        shared_ptr<impl> pi(new impl);
        pi->weak_this = pi;
        return pi;
    }

    virtual void f() { ... }

    virtual shared_ptr<X> getX()
    {
        shared_ptr<X> px(weak_this);
        return px;
    }
};

The library now includes a helper class template enable_shared_from_this that can be used to encapsulate the solution:

class impl: public X, public Y, public enable_shared_from_this<impl>
{
public:

    impl(impl const &);
    impl & operator=(impl const &);

public:

    virtual void f() { ... }

    virtual shared_ptr<X> getX()
    {
        return shared_from_this();
    }
}

Note: There is no need to manually initialize the weak_ptr member in enable_shared_from_this . Constructing a shared_ptr to impl takes care of that.

Using shared_ptr as a smart counted handle

Some library interfaces use opaque handles, a variation of the incomplete class technique described above. An example:

typedef void * HANDLE;
HANDLE CreateProcess();
void CloseHandle(HANDLE);

Instead of a raw pointer, it is possible to use shared_ptr as the handle and get reference counting and automatic resource management for free:

typedef shared_ptr<void> handle;

handle createProcess()
{
    shared_ptr<void> pv(CreateProcess(), CloseHandle);
    return pv;
}

Using shared_ptr to execute code on block exit

shared_ptr<void> can automatically execute cleanup code when control leaves a scope.

  • Executing f(p), where p is a pointer:

    shared_ptr<void> guard(p, f);

  • Executing arbitrary code: f(x, y):

    shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y));

For a more thorough treatment, see the article "Simplify Your Exception-Safe Code" by Andrei Alexandrescu and Petru Marginean, available online at http://www.cuj.com/experts/1812/alexandr.htm?topic=experts.

Using shared_ptr<void> to hold an arbitrary object

shared_ptr<void> can act as a generic object pointer similar to void*. When a shared_ptr<void> instance constructed as:

    shared_ptr<void> pv(new X);

is destroyed, it will correctly dispose of the X object by executing ~X.

This property can be used in much the same manner as a raw void* is used to temporarily strip type information from an object pointer. A shared_ptr<void> can later be cast back to the correct type by using static_pointer_cast .

Associating arbitrary data with heterogeneous shared_ptr instances

shared_ptr and weak_ptr support operator< comparisons required by standard associative containers such as std::map. This can be used to non-intrusively associate arbitrary data with objects managed by shared_ptr:

typedef int Data;

std::map< shared_ptr<void>, Data > userData;
// or std::map< weak_ptr<void>, Data > userData; to not affect the lifetime

shared_ptr<X> px(new X);
shared_ptr<int> pi(new int(3));

userData[px] = 42;
userData[pi] = 91;

Using shared_ptr as a CopyConstructible mutex lock

Sometimes it's necessary to return a mutex lock from a function, and a noncopyable lock cannot be returned by value. It is possible to use shared_ptr as a mutex lock:

class mutex
{
public:

    void lock();
    void unlock();
};

shared_ptr<mutex> lock(mutex & m)
{
    m.lock();
    return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}

Better yet, the shared_ptr instance acting as a lock can be encapsulated in a dedicated shared_lock class:

class shared_lock
{
private:

    shared_ptr<void> pv;

public:

    template<class Mutex> explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {}
};

shared_lock can now be used as:

    shared_lock lock(m);

Note: That shared_lock is not templated on the mutex type, because of shared_ptr<void>'s ability to hide type information.

Using shared_ptr to wrap member function calls

shared_ptr implements the ownership semantics required from the Wrap/CallProxy scheme described in Bjarne Stroustrup's article "Wrapping C++ Member Function Calls" (available online at http://www.research.att.com/~bs/wrapper.pdf). An implementation is given below:

template<class T> class pointer
{
private:

    T * p_;

public:

    explicit pointer(T * p): p_(p)
    {
    }

    shared_ptr<T> operator->() const
    {
        p_->prefix();
        return shared_ptr<T>(p_, mem_fn(&T::suffix));
    };   }
};

class X
{
private:

    void prefix();
    void suffix();
    friend class pointer<X>;

public:

    void f();
    void g();
};

int main()
{
    X x;

    pointer<X> px(&x);

    px->f();
    px->g();
}

Delayed deallocation

In some situations, a single px.reset() can trigger an expensive deallocation in a performance-critical region:

class X; // ~X is expensive

class Y
{
    shared_ptr<X> px;

public:

    void f()
    {
        px.reset();
    }
};

The solution is to postpone the potential deallocation by moving px to a dedicated free list that can be periodically emptied when performance and response times are not an issue:

vector< shared_ptr<void> > free_list;

class Y
{
    shared_ptr<X> px;

public:

    void f()
    {
        free_list.push_back(px);
        px.reset();
    }
};

// periodically invoke free_list.clear() when convenient

Another variation is to move the free list logic to the construction point by using a delayed deleter:

struct delayed_deleter
{
    template<class T> void operator()(T * p)
    {
        try
        {
            shared_ptr<void> pv(p);
            free_list.push_back(pv);
        }
        catch(...)
        {
        }
    }
};

Weak pointers to objects not managed by a shared_ptr

Make the object hold a shared_ptr to itself, using a null_deleter:

class X
{
private:

    shared_ptr<X> this_;
    int i_;

public:

    explicit X(int i): this_(this, null_deleter()), i_(i)
    {
    }

    // repeat in all constructors (including the copy constructor!)

    X(X const & rhs): this_(this, null_deleter()), i_(rhs.i_)
    {
    }

    // do not forget to not assign this_ in the copy assignment

    X & operator=(X const & rhs)
    {
        i_ = rhs.i_;
    }

    weak_ptr<X> get_weak_ptr() const { return this_; }
};

When the object's lifetime ends, X::this_ will be destroyed, and all weak pointers will automatically expire.

Copyright © 2003 Peter Dimov. Distributed under the Boost Software License, Version 1.0. See accompanying file License.html or copy at http://www.boost.org/License.html.


Testsuite

Acceptance tests

Negative tests

Acceptance tests

Test | Type | Description | If failing
function_test.cpp | run | Test the capabilities of the boost::function class template. | The boost::function class template may not be usable on your compiler. However, the library may still be usable via the boost::functionN class templates.
function_n_test.cpp | run | Test the capabilities of the boost::functionN class templates. |
allocator_test.cpp | run | Test the use of custom allocators. | Allocators are ignored by the implementation.
stateless_test.cpp | run | Test the optimization of stateless function objects in the Boost.Function library. | The exception-safety and performance guarantees given for stateless function objects may not be met by the implementation.
lambda_test.cpp | run | Test the interaction between Boost.Function and Boost.Lambda. | Either Boost.Lambda does not work on the platform, or Boost.Function cannot safely be applied without the use of boost::unlambda.
contains_test.cpp | run | Test the operation of the target member function and the equality operators. |
function_30.cpp | compile | Test the generation of a Boost.Function function object adaptor accepting 30 arguments. | The Boost.Function library may work for function object adaptors of up to 10 parameters, but will be unable to generate adaptors for an arbitrary number of parameters. Failure often indicates an error in the compiler's preprocessor.
function_arith_cxx98.cpp | run | Test the first tutorial example. |
function_arith_portable.cpp | run | Test the first tutorial example. |
sum_avg_cxx98.cpp | run | Test the second tutorial example. |
sum_avg_portable.cpp | run | Test the second tutorial example. |
mem_fun_cxx98.cpp | run | Test member function example from tutorial. |
mem_fun_portable.cpp | run | Test member function example from tutorial. |
std_bind_cxx98.cpp | run | Test standard binders example from tutorial. |
std_bind_portable.cpp | run | Test standard binders example from tutorial. |
function_ref_cxx98.cpp | run | Test boost::ref example from tutorial. |
function_ref_portable.cpp | run | Test boost::ref example from tutorial. |

Negative tests

Test | Type | Description | If failing
function_test_fail1.cpp | compile-fail | Test the (incorrect!) use of comparisons between Boost.Function function objects. | Intuitive (but incorrect!) code may compile and will give meaningless results.
function_test_fail2.cpp | compile-fail | Test the use of an incompatible function object with Boost.Function | Incorrect code may compile (with potentially unexpected results).
Copyright © 2001-2004 Douglas Gregor


TreeView

S60 Boost Library API Reference

o + File List

| \ * display.inl

\ + Directories

  \ + api_ref

    \ + epoc32

      \ + include

        \ + Files

          \ * display.inl


Tutorial

Basic Usage

Free functions

Member functions

References to Function Objects

Comparing Boost.Function function objects

Boost.Function has two syntactical forms: the preferred form and the portable form. The preferred form fits more closely with the C++ language and reduces the number of separate template parameters that need to be considered, often improving readability; however, the preferred form is not supported on all platforms due to compiler bugs. The compatible form will work on all compilers supported by Boost.Function. Consult the table below to determine which syntactic form to use for the compiler.

Preferred syntax Portable syntax
  • GNU C++ 2.95.x, 3.0.x, 3.1.x
  • Comeau C++ 4.2.45.2
  • SGI MIPSpro 7.3.0
  • Intel C++ 5.0, 6.0
  • Compaq's cxx 6.2
  • Microsoft Visual C++ 7.1
  • Any compiler supporting the preferred syntax
  • Microsoft Visual C++ 6.0, 7.0
  • Borland C++ 5.5.1
  • Sun WorkShop 6 update 2 C++ 5.3
  • Metrowerks CodeWarrior 8.1

If the compiler does not appear in this list, please try the preferred syntax and report the results to the Boost list so that the table up-to-date.

Basic Usage

A function wrapper is defined simply by instantiating the function class template with the desired return type and argument types, formulated as a C++ function type. Any number of arguments may be supplied, up to some implementation-defined limit (10 is the default maximum). The following declares a function object wrapper f that takes two int parameters and returns a float:

Preferred syntax Portable syntax
boost::function<float (int x, int y)> f;

boost::function2<float, int, int> f;

By default, function object wrappers are empty, so create a function object to assign to f:

struct int_div {
  float operator()(int x, int y) const { return ((float)x)/y; };
};

f = int_div();

 Use
f to execute
the underlying function object
int_div:

std::cout << f(5, 3) << std::endl;

 Assign any compatible function object to f. If
int_div had been declared to take two
long operands, the implicit
conversions would have been applied to the arguments without any user
interference. The only limit on the types of arguments is that they be
CopyConstructible, so we can even use references and arrays:

Preferred syntax

boost::function<void (int values[], int n, int& sum, float& avg)> sum_avg;

Portable syntax

boost::function4<void, int*, int, int&, float&> sum_avg;

void do_sum_avg(int values[], int n, int& sum, float& avg)
{
  sum = 0;
  for (int i = 0; i < n; i++)
    sum += values[i];
  avg = (float)sum / n;
}

sum_avg = &do_sum_avg;

 Invoking a function object wrapper that does not actually
contain a function object is a precondition violation, much like
trying to call through a null function pointer, and will throw a
 bad_function_call exception).
Check for an
empty function object wrapper by using it in a boolean context (it evaluates true if the wrapper is not empty) or compare it against 0. For instance:

if (f)
  std::cout << f(5, 3) << std::endl;
else
  std::cout << "f has no target, so it is unsafe to call" << std::endl;

 Alternatively,

empty()
method will return whether or not the wrapper is empty.

 Finally, clear out a function target by assigning it to 0 or by calling the

clear()
member function, e.g.,

f = 0;

Free functions

 Free function pointers can be considered singleton function objects with const function call operators, and can therefore be directly used with the function object wrappers:

float mul_ints(int x, int y) { return ((float)x) * y; }

f = &mul_ints;

 Note: The &  is necessary
only when usingMicrosoft Visual C++ version 6.

Member functions

 In many systems, callbacks often call to member functions of a
particular object. This is often referred to as "argument binding",
and is beyond the scope of Boost.Function. The use of member functions
directly, however, is supported, so the following code is valid:

struct X {
  int foo(int);
};

Preferred syntax
Portable syntax

boost::function<int (X*, int)> f;

f = &X::foo;

X x;
f(&x, 5);

boost::function2<int, X*, int> f;

f = &X::foo;

X x;
f(&x, 5);

 Several libraries exist that support argument binding. Three such libraries are summarized below:

  • Bind: This library allows binding of
  arguments for any function object. It is lightweight and very
  portable.
    •

  •

    The C++ Standard library. Using
  std::bind1st and
std::mem_fun together one can bind
  the object of a pointer-to-member function for use with
  Boost.Function:

Preferred syntax
Portable syntax

      boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

      boost::function1<int, int> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

    •

  • The Lambda library:this library provides a powerful composition mechanism to construct function objects that uses very natural C++ syntax. Lambda requires a compiler that is reasonably conformant to the C++ standard.

    •

References to Function Objects

 In some cases it is
  expensive (or semantically incorrect) to have Boost.Function clone a
  function object. In such cases, it is possible to request that
  Boost.Function keep only a reference to the actual function
  object. This is done using the ref
  and
cref functions to wrap a
  reference to a function object:

Preferred syntax
Portable syntax

stateful_type a_function_object;
boost::function<int (int)> f;
f = boost::ref(a_function_object);

boost::function<int (int)> f2(f);

stateful_type a_function_object;
boost::function1<int, int> f;
f = boost::ref(a_function_object);

boost::function1<int, int> f2(f);

 Here,
f will not make a copy
of a_function_object, nor will
f2 when it is targeted to
f's reference to
a_function_object. Additionally, when
using references to function objects, Boost.Function will not throw
exceptions during assignment or construction.

Comparing Boost.Function function objects

Function object wrappers can be compared via ==
  or != against any function object that can be stored
  within the wrapper. If the function object wrapper contains a
  function object of that type, it will be compared against the given
  function object (which must be either be
  EqualityComparable or have an overloaded boost::function_equal). For instance:

int compute_with_X(X*, int);

f = &X::foo;
assert(f == &X::foo);
assert(&compute_with_X != f);

When comparing against an instance of

   reference_wrapper, the address
   of the object in the

   reference_wrapper is compared
   against the address of the object stored by the function object
   wrapper:

a_stateful_object so1, so2;
f = boost::ref(so1);
assert(f == boost::ref(so1));
assert(f == so1); // Only if a_stateful_object is EqualityComparable
assert(f != boost::ref(so2));

Copyright © 2001-2004 Douglas Gregor


Class template unwrap_reference

boost::unwrap_reference — Find the type in a reference_wrapper .

Synopsis

template<typename T>
class unwrap_reference {
public:
  // types
  typedef unspecified type;
};

Description

The typedef type is T::type if T is a reference_wrapper , T otherwise.

Copyright © 1999, 2000 Jaakko Järvi
Copyright © 2001, 2002 Peter Dimov
Copyright © 2002 David Abrahams


Header boost/utility.hpp Documentation

Header boost/utility.hpp

The entire contents of the header <boost/utility.hpp> are in namespace boost.

Class templates supporting the base-from-member idiom

Function templates checked_delete() and checked_array_delete()

Function templates next() and prior()

Class noncopyable

Function template addressof()

Class template result_of

Other utilities not part of utility.hpp

Function templates checked_delete() and checked_array_delete()

See separate documentation.

Function templates next() and prior()

Certain data types, such as the C++ Standard Library's forward and bidirectional iterators, do not provide addition and subtraction via operator+() or operator-(). This means that non-modifying computation of the next or prior value requires a temporary, even though operator++() or operator--() is provided. It also means that writing code like itr+1 inside a template restricts the iterator category to random access iterators.

The next() and prior() functions provide a simple way around these problems:

template <class T>
T next(T x) { return ++x; }

template <class T, class Distance>
T next(T x, Distance n)
{
    std::advance(x, n);
    return x;
}

template <class T>
T prior(T x) { return --x; }

template <class T, class Distance>
T prior(T x, Distance n)
{
    std::advance(x, -n);
    return x;
}

Usage is simple:

const std::list<T>::iterator p = get_some_iterator();
const std::list<T>::iterator prev = boost::prior(p);
const std::list<T>::iterator next = boost::next(prev, 2);

The distance from the given iterator should be supplied as an absolute value. For example, the iterator four iterators prior to the given iterator p may be obtained by prior(p, 4).

Contributed by Dave Abrahams. Two-argument versions by Daniel Walker.

Class noncopyable

Class noncopyable is a base class. Derive own class from noncopyable to prohibit copy construction and copy assignment.

Some objects, particularly those which hold complex resources like files or network connections, have no sensible copy semantics. Sometimes there are possible copy semantics, but these would be of very limited usefulness and be very difficult to implement correctly. When implementing a class that doesn't need to be copied just yet and the user would not want to take the time to write the appropriate functions. Deriving from noncopyable will prevent the otherwise implicitly-generated functions (which don't have the proper semantics) from becoming a trap for other programmers.

The traditional way to deal with these is to declare a private copy constructor and copy assignment, and then document why this is done. But deriving from noncopyable is simpler and clearer, and doesn't require additional documentation.

The program noncopyable_test.cpp can be used to verify class noncopyable works as expected. It has have been run successfully under GCC 2.95, Metrowerks CodeWarrior 5.0, and Microsoft Visual C++ 6.0 sp 3.

Contributed by Dave Abrahams.

Example

// inside one of your own headers ...
#include <boost/utility.hpp>

class ResourceLadenFileSystem : boost::noncopyable {
...

Rationale

Class noncopyable has protected constructor and destructor members to emphasize that it is to be used only as a base class. Dave Abrahams notes concern about the effect on compiler optimization of adding (even trivial inline) destructor declarations. Dave says "Probably this concern is misplaced, because noncopyable will be used mostly for classes which own resources and thus have non-trivial destruction semantics."

Function template addressof()

Function addressof() returns the address of an object.

template <typename T> inline T*                addressof(T& v);
template <typename T> inline const T*          addressof(const T& v);
template <typename T> inline volatile T*       addressof(volatile T& v);
template <typename T> inline const volatile T* addressof(const volatile T& v);

C++ allows programmers to replace the unary operator&() class member used to get the address of an object. Getting the real address of an object requires casting tricks to avoid invoking the overloaded operator&(). Function addressof() provides a wrapper around the necessary code to make it easy to get an object's real address.

The program addressof_test.cpp can be used to verify that addressof() works as expected.

Contributed by Brad King based on ideas from discussion with Doug Gregor.

Example

#include <boost/utility.hpp>

struct useless_type {};
class nonaddressable {
  useless_type operator&() const;
};

void f() {
  nonaddressable x;
  nonaddressable* xp = boost::addressof(x);
  // nonaddressable* xpe = &x; /* error */
}

Class template result_of

The class template result_of helps determine the type of a call expression. Given an lvalue f of type F and lvalues t1, t2, ..., tN of types T1, T2, ..., TN, respectively, the type result_of<F(T1, T2, ..., TN)>::type defines the result type of the expression f(t1, t2, ...,tN). The implementation permits the type F to be a function pointer, function reference, member function pointer, or class type. When F is a class type with a member type result_type, result_of<F(T1, T2, ..., TN)> is F::result_type. Otherwise, result_of<F(T1, T2, ..., TN)> is F::result<F(T1, T2, ..., TN)>::type when N > 0 or void when N = 0. For additional information about result_of, see the current draft of the C++ Library TR, N1647 , or the result_of proposal .

Class template result_of resides in the header <boost/utility/result_of.hpp>. By default, N may be any value between 0 and 10. To change the upper limit, define the macro BOOST_RESULT_OF_NUM_ARGS to the maximum value for N.

This implementation of result_of requires class template partial specialization, the ability to parse function types properly, and support for SFINAE. If result_of is not supported by your compiler, including the header boost/utility/result_of.hpp will define the macro BOOST_NO_RESULT_OF. Contributed by Doug Gregor.

Class templates for the Base-from-Member Idiom

See separate documentation.

© Copyright boost.org 1999-2003. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


weak_ptr

weak_ptr class template

Introduction
Synopsis
Members
Free Functions
Frequently Asked Questions

Introduction

The weak_ptr class template stores a "weak reference" to an object that's already managed by a shared_ptr. To access the object, a weak_ptr is converted to a shared_ptr using the shared_ptr constructor or the member function lock. When the last shared_ptr to the object goes away and the object is deleted, the attempt to obtain a shared_ptr from the weak_ptr instances that refer to the deleted object will fail: the constructor will throw an exception of type boost::bad_weak_ptr, and weak_ptr::lock will return an empty shared_ptr.

Every weak_ptr meets the CopyConstructible and Assignable requirements of the C++ Standard Library, and so can be used in standard library containers. Comparison operators are supplied so that weak_ptr works with the standard library's associative containers. weak_ptr operations never throw exceptions.

The class template is parameterized on T, the type of the object pointed to.

When compared to shared_ptr, weak_ptr provides a very limited subset of operations since accessing its stored pointer is often dangerous in multithreaded programs, and sometimes unsafe even within a single thread (that is, it may invoke undefined behavior.) For example, if a  weak_ptr has a get member function that returns a raw pointer, and consider this piece of code:

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);

// some time later

if(int * r = q.get())
{
    // use *r
}

Imagine that after the if, but immediately before r is used, another thread executes the statement p.reset(). Now r is a dangling pointer.

The solution to this problem is to create a temporary shared_ptr from q:

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);

// some time later

if(shared_ptr<int> r = q.lock())
{
    // use *r
}

Now r holds a reference to the object that was pointed by q. Even if p.reset() is executed in another thread, the object will stay alive until r goes out of scope or is reset. By obtaining a shared_ptr to the object, it is effectively locked against destruction.

Synopsis

namespace boost {

  template<class T> class weak_ptr {

    public:
      typedef T element_type;

      weak_ptr();

      template<class Y> weak_ptr(shared_ptr<Y> const & r);
      weak_ptr(weak_ptr const & r);
      template<class Y> weak_ptr(weak_ptr<Y> const & r);

      ~weak_ptr();

      weak_ptr & operator=(weak_ptr const & r);
      template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r);
      template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r);

      long use_count() const;
      bool expired() const;
      shared_ptr<T> lock() const;

      void reset();
      void swap(weak_ptr<T> & b);
  };

  template<class T, class U>
    bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);

  template<class T>
    void swap(weak_ptr<T> & a, weak_ptr<T> & b);
}

Members

element_type

typedef T element_type;

Provides the type of the template parameter T.

constructors

weak_ptr();

Effects: Constructs an empty weak_ptr.

Postconditions: use_count() == 0

Throws: nothing

template<class Y> weak_ptr(shared_ptr<Y> const & r);
weak_ptr(weak_ptr const & r);
template<class Y> weak_ptr(weak_ptr<Y> const & r);

Effects: If r is empty, constructs an empty weak_ptr; otherwise, constructs a weak_ptr that shares ownership with r as if by storing a copy of the pointer stored in r.

Postconditions: use_count() == r.use_count()

Throws: nothing

destructor

~weak_ptr();

Effects: Destroys this weak_ptr but has no effect on the object its stored pointer points to.

Throws: nothing

assignment

weak_ptr & operator=(weak_ptr const & r);
template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r);
template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r);

Effects: Equivalent to weak_ptr(r).swap(*this).

Throws: nothing

Notes: The implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary.

use_count

long use_count() const;

Returns: 0 if *this is empty; otherwise, the number of shared_ptr objects that share ownership with *this.

Throws: nothing.

Notes: use_count() is not necessarily efficient. Use only for debugging and testing purposes, not for production code.

expired

bool expired() const;

Returns: use_count() == 0

Throws: nothing

Notes: expired() may be faster than use_count().

lock

shared_ptr<T> lock() const;

Returns: expired()? shared_ptr<T>(): shared_ptr<T>(*this)

Throws: nothing

reset

void reset();

Effects: Equivalent to weak_ptr().swap(*this).

swap

void swap(weak_ptr & b);

Effects: Exchanges the contents of the two smart pointers.

Throws: nothing.

Free Functions

comparison

template<class T, class U>
  bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);

Returns: an unspecified value such that

  • operator< is a strict weak ordering as described in section 25.3 [lib.alg.sorting] of the C++ standard;

  • under the equivalence relation defined by operator<, !(a < b) && !(b < a), two weak_ptr instances are equivalent if and only if they share ownership or are both empty.

Throws: nothing

Notes: Allows weak_ptr objects to be used as keys in associative containers.

swap

template<class T>
  void swap(weak_ptr<T> & a, weak_ptr<T> & b)

Effects: Equivalent to a.swap(b).

Throws: nothing

Notes: Matches the interface of std::swap. Provided as an aid to generic programming.

Frequently Asked Questions

Can an object create a weak_ptr to itself in its constructor?

No. A weak_ptr can only be created from a shared_ptr, and at object construction time no shared_ptr to the object exists yet. If created, a temporary shared_ptr to this would go out of scope at the end of the constructor, and all weak_ptr instances would instantly expire.

The solution is to make the constructor private, and supply a factory function that returns a shared_ptr:

class X
{
private:

    X();

public:

    static shared_ptr<X> create()
    {
        shared_ptr<X> px(new X);
        // create weak pointers from px here
        return px;
    }
};
Copyright 1999 Greg Colvin and Beman Dawes. Copyright 2002 Darin Adler. Copyright 2002-2005 Peter Dimov. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


weak_ptr

Name

weak_ptr: Class template stores a "weak reference" to an object that is already managed by a shared_ptr.

Synopsis

namespace boost {

  template<class T> class weak_ptr {

    public:
      typedef T element_type;

      weak_ptr();

      template<class Y> weak_ptr(shared_ptr<Y> const & r);
      weak_ptr(weak_ptr const & r);
      template<class Y> weak_ptr(weak_ptr<Y> const & r);

      ~weak_ptr();

      weak_ptr & operator=(weak_ptr const & r);
      template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r);
      template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r);

      long use_count() const;
      bool expired() const;
      shared_ptr<T> lock() const;

      void reset();
      void swap(weak_ptr<T> & b);
  };

  template<class T, class U>
    bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);

  template<class T>
    void swap(weak_ptr<T> & a, weak_ptr<T> & b);
}

Detailed Description

Click here for more details.

Limitation

None

Feedback

For additional information or queries on this page send feedback.

© 2006-2007 Nokia

Top

Legal Disclaimer Please note, that by using some of the links in this document you will leave the Nokia website and the site you enter is not operated by Nokia. If you choose to access such sites, you should take precautions for security or content. By using the site, you shall review and agree to the site's rules of use before using the site. You agree and acknowledge that Nokia has no control over the service and will not assume any liability or responsibility for the availability of the site, nor for any aspects of the service.