Search code examples
c++boosttemplate-meta-programminggeneric-programmingboost-hana

Initialize boost::hana::tuple with one argument


Let's assume we have something like this: An Interface for some class Foo ( 'FooInterface' ) and a container class Bar that contains derived classes from 'FooInterface'.

Now I forward a typelist of the types of the derived classes ( 'FooOne', 'FooTwo' ) to the container class and it stores an instance of them in a 'boost::hana::tuple' subsequent to a small type computation ( 'FooTuple' ).

Now how do I initialize the tuple elements with a dereferenced this-pointer, depending on the size of 'FooList' ?

MCVE (Wandbox)

#include <iostream>

#include <boost/hana.hpp>

namespace hana = boost::hana;

template <typename FooList>
class Bar;

template <typename FooList>
class FooInterface
{
public:
    FooInterface(Bar<FooList>& bar) {}

public:
    virtual void foo() = 0;
};

class FooOne;
class FooTwo;

using MyFooList = decltype(hana::tuple_t<FooOne, FooTwo>);

class FooOne final
    : public FooInterface<MyFooList>
{
public:
    FooOne(Bar<MyFooList>& bar) 
        : FooInterface(bar)
    {}

public:
    void foo() override
    {
        std::cout << "FooOne!\n";
    }
};

class FooTwo final
    : public FooInterface<MyFooList>
{
public:
    FooTwo(Bar<MyFooList>& bar) 
        : FooInterface(bar)
    {}

public:
    void foo() override
    {
        std::cout << "FooTwo!\n";
    }
};

template <typename FooList>
class Bar
{
public:
    using FooTuple = typename decltype(hana::unpack(FooList(), hana::template_<hana::tuple>))::type;

    FooTuple foos{ *this, *this };
};

int main() 
{
   Bar<MyFooList> b;
   b.foos[hana::int_c<0>].foo();
   b.foos[hana::int_c<1>].foo();
}

Output :

FooOne!
FooTwo!

Solution

  • hana::replicate is your friend.

    template <typename FooList>
    class Bar {
        ...
    
        using FooTuple = ...;
        FooTuple foos;
    
        Bar() : foos(hana::replicate<hana::tuple_tag>(*this, hana::size_c<N>)) {}
    };
    

    Now, you have to be careful cause that'll make a copy of each *this when creating a tuple in replicate. If you want references instead, use reference_wrapper like this:

    foos(hana::replicate<hana::tuple_tag>(std::ref(*this), hana::size_c<N>))
    

    and then make sure that the constructor of each thing in FooTuple can be constructed from a reference_wrapper (which is the case if they take a reference).