Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Basic Usage

(For the source of the examples in this section see basic.cpp)

The main class in the library is any. An any can store objects that meet whatever requirements we specify. These requirements are passed to any as an MPL sequence.

[Note] Note

The MPL sequence combines multiple concepts. In the rare case when we only want a single concept, it doesn't need to be wrapped in an MPL sequence.

any<mpl::vector<copy_constructible<>, typeid_<>, relaxed> > x(10);
int i = any_cast<int>(x); // i == 10

copy_constructible is a builtin concept that allows us to copy and destroy the object. typeid_ provides run-time type information so that we can use any_cast. relaxed enables various useful defaults. Without relaxed, any supports exactly what you specify and nothing else. In particular, it allows default construction and assignment of any.

Now, this example doesn't do very much. x is approximately equivalent to a boost::any. We can make it more interesting by adding some operators, such as operator++ and operator<<.

any<
    mpl::vector<
        copy_constructible<>,
        typeid_<>,
        incrementable<>,
        ostreamable<>
    >
> x(10);
++x;
std::cout << x << std::endl; // prints 11

The library provides concepts for most C++ operators, but this obviously won't cover all use cases; we often need to define our own requirements. Let's take the push_back member, defined by several STL containers.

BOOST_TYPE_ERASURE_MEMBER((has_push_back), push_back, 1)

void append_many(any<has_push_back<void(int)>, _self&> container) {
    for(int i = 0; i < 10; ++i)
        container.push_back(i);
}

We use the macro BOOST_TYPE_ERASURE_MEMBER to define a concept called has_push_back. The second parameter is the name of the member function and the last macro parameter indicates the number of arguments which is 1 since push_back is unary. When we use has_push_back, we have to tell it the signature of the function, void(int). This means that the type we store in the any has to have a member that looks like:

void push_back(int);

Thus, we could call append_many with std::vector<int>, std::list<int>, or std::vector<long> (because int is convertible to long), but not std::list<std::string> or std::set<int>.

Also, note that append_many has to operate directly on its argument. It cannot make a copy. To handle this we use _self& as the second argument of any. _self is a placeholder. By using _self&, we indicate that the any stores a reference to an external object instead of allocating its own object.

There's actually another placeholder here. The second parameter of has_push_back defaults to _self. If we wanted to define a const member function, we would have to change it to const _self, as shown below.

BOOST_TYPE_ERASURE_MEMBER((has_empty), empty, 0)
bool is_empty(any<has_empty<bool(), const _self>, const _self&> x) {
    return x.empty();
}

For free functions, we can use the macro BOOST_TYPE_ERASURE_FREE.

BOOST_TYPE_ERASURE_FREE((has_getline), getline, 2)
std::vector<std::string> read_lines(any<has_getline<bool(_self&, std::string&)>, _self&> stream)
{
    std::vector<std::string> result;
    std::string tmp;
    while(getline(stream, tmp))
        result.push_back(tmp);
    return result;
}

The use of has_getline is very similar to has_push_back above. The difference is that the placeholder _self is passed in the function signature instead of as a separate argument.

The placeholder doesn't have to be the first argument. We could just as easily make it the second argument.

void read_line(any<has_getline<bool(std::istream&, _self&)>, _self&> str)
{
    getline(std::cin, str);
}


PrevUpHomeNext