Can BOOST_FOREACH be customized for a pointer type?

91 Views Asked by At

I'm writing C++98 (sorry), but working with a C library, which has many objects stored in data structures of the form:

struct c_container
{
    size_t len;
    int data[1];
};

struct c_container *make_container(size_t n)
{
    if (n == 0)
        return NULL;
    struct c_container *rv = (struct c_container *)malloc(sizeof(rv->len) + n*sizeof(rv->data));
    rv->len = n;
    return rv;
}

I'd like to do C++-style iteration using BOOST_FOREACH, but this doesn't work. (The "old style" of manually calling the range_begin and range_end functions does work).

inline int *range_begin(c_container *c)
{
    return c ? &c->data[0] : NULL;
}
inline int *range_end(c_container *c)
{
    return c ? &c->data[c->len] : NULL;
}
inline const int *range_begin(const c_container *c)
{
    return c ? &c->data[0] : NULL;
}
inline const int *range_end(const c_container *c)
{
    return c ? &c->data[c->len] : NULL;
}

namespace boost
{
    template<>
    struct range_mutable_iterator<c_container *>
    {
        typedef int *type;
    };
    template<>
    struct range_const_iterator<c_container *>
    {
        typedef const int *type;
    };
}

int main()
{
    c_container *coll = make_container(3);
    coll->data[0] = 1;
    coll->data[1] = 42;
    coll->data[2] = -1;

    BOOST_FOREACH(int i, coll)
    {
        std::cout << i << std::endl;
    }
}

This is all that should be necessary, according to http://www.boost.org/doc/libs/1_65_1/doc/html/foreach/extensibility.html (and I've tested it with classes)

However, that example uses a class, whereas I'm using a pointer to a class. Based on my investigation, it appears to be using the codepath that is only intended for const char * and const wchar_t *:

In file included from boost-foreach.cpp:6:0:
/usr/include/boost/foreach.hpp: In function ‘bool boost::foreach_detail_::done(const boost::foreach_detail_::auto_any_base&, const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<T*, C>*) [with T = c_container, C = mpl_::bool_<false>, const boost::foreach_detail_::auto_any_base& = const boost::foreach_detail_::auto_any_base&]’:
boost-foreach.cpp:65:5:   instantiated from here
/usr/include/boost/foreach.hpp:749:57: error: no match for ‘operator!’ in ‘!* boost::foreach_detail_::auto_any_cast [with T = c_container*, C = mpl_::bool_<false>, typename boost::mpl::if_<C, const T, T>::type = c_container*, const boost::foreach_detail_::auto_any_base& = const boost::foreach_detail_::auto_any_base&](((const boost::foreach_detail_::auto_any_base&)((const boost::foreach_detail_::auto_any_base*)cur)))’
/usr/include/boost/foreach.hpp:749:57: note: candidate is: operator!(bool) <built-in>

Is there some additional boost trait to specialize or something?

1

There are 1 best solutions below

1
TobiMcNamobi On

It seems to be difficult to define the range functions for pointer types. But you can define them for c_container directly. The code looks like this:

#include <cstdlib>
#include <iostream>
#include <boost/foreach.hpp>

struct c_container
{
    size_t len;
    int data[1];
};

struct c_container *make_container(size_t n)
{
    if (n == 0)
        return NULL;
    struct c_container *rv = (struct c_container *)malloc(sizeof(rv->len) + n * sizeof(rv->data));
    rv->len = n;
    return rv;
}

inline int *range_begin(c_container &c)
{
    return c.len > 0 ? &c.data[0] : NULL;
}
inline int *range_end(c_container &c)
{
    return c.len > 0 ? &c.data[c.len] : NULL;
}
inline const int *range_begin(const c_container &c)
{
    return c.len > 0 ? &c.data[0] : NULL;
}
inline const int *range_end(const c_container &c)
{
    return c.len > 0 ? &c.data[c.len] : NULL;
}

namespace boost
{
    template<>
    struct range_mutable_iterator<c_container>
    {
        typedef int *type;
    };
    template<>
    struct range_const_iterator<c_container>
    {
        typedef const int *type;
    };
}

#define MY_FOREACH(x, y) BOOST_FOREACH(x, *y)

int main()
{
    c_container *coll = make_container(3);
    coll->data[0] = 1;
    coll->data[1] = 42;
    coll->data[2] = -1;

    //BOOST_FOREACH(int i, *coll)
    MY_FOREACH(int i, coll)
    {
        std::cout << i << std::endl;
    }
}

Note that the BOOST_FOREACH loop does not iterate over a pointer type. As a workaround you may define your own FOREACH that does so as shown in the code above.