Search code examples
c++boostboost-fusion

What is the proper use of boost::fusion::push_back?


// ... snipped includes for iostream and fusion ...
namespace fusion = boost::fusion;

class Base
{
protected: int x;
public: Base() : x(0) {}
    void chug() { 
        x++;
        cout << "I'm a base.. x is now " << x << endl;
    }
};

class Alpha : public Base
{
public:
    void chug() { 
        x += 2;
        cout << "Hi, I'm an alpha, x is now " << x << endl;
    }
};

class Bravo : public Base
{
public:
    void chug() { 
        x += 3;
        cout << "Hello, I'm a bravo; x is now " << x << endl; 
    }
};

struct chug {
    template<typename T>
    void operator()(T& t) const
    {
        t->chug();
    }
};

int main()
{
    typedef fusion::vector<Base*, Alpha*, Bravo*, Base*> Stuff;
    Stuff stuff(new Base, new Alpha, new Bravo, new Base);

    fusion::for_each(stuff, chug());     // Mutates each element in stuff as expected

    /* Output:
       I'm a base.. x is now 1
       Hi, I'm an alpha, x is now 2
       Hello, I'm a bravo; x is now 3
       I'm a base.. x is now 1
    */

    cout << endl;

    // If I don't put 'const' in front of Stuff...
    typedef fusion::result_of::push_back<const Stuff, Alpha*>::type NewStuff;

    // ... then this complains because it wants stuff to be const:
    NewStuff newStuff = fusion::push_back(stuff, new Alpha);

    // ... But since stuff is now const, I can no longer mutate its elements :(
    fusion::for_each(newStuff, chug());

    return 0;
};

How do I get for_each(newStuff, chug()) to work?

(Note: I'm only assuming from the overly brief documentation on boost::fusion that I am supposed to create a new vector every time I call push_back.)


Solution

  • (Note: I'm only assuming from the overly brief documentation on boost::fusion that I am supposed to create a new vector every time I call push_back.)

    You're not creating a new vector. push_back returns a lazily evaluated view on the extended sequence. If you want to create a new vector, then e.g. typedef NewStuff as

    typedef fusion::vector<Base*, Alpha*, Bravo*, Base*, Alpha*> NewStuff;
    

    Your program works then.

    Btw, fusion is a very functional design. I think it would be more fusion-like if you'd store actual objects rather than pointers and used transform. The chug logic would then be moved out of the classes into the struct chug which had appropriate operator()'s for each type. No new vectors would have to be created then, you could work with lazily evaluated views.