Search code examples
c++boostc++14boost-mplboost-fusion

MPL replace without casting the fusion container


I have this class

struct MyChildrenNeedsSpace : HaveChildren<MyChildrenNeedsSpace>
{
    typedef childrenListType<string, string, string, string> context;

    const   context children;

    MyChildrenNeedsSpace() : children("this", "sentence", "needs", "spaces")
    {
        std::cout << endl << "The children type is:"  << endl << typeid(children).name() << endl;
    }
};

it uses CRTP to allow the HaveChildren class to access its children member variable.

The childrenListType is a class that inherits from boost::fusion::vector.

I would like to programmatically make the children member variable contain one space class between each string.

So if I enter:

<string,string>

children becomes:

<string, space,string>

If I enter:

<string,string,string>

it becomes

<string,space,string,space,string>

etc.

I am using boost fusion so it have to be at compile time.

I have tried to do it like this:

struct MyChildrenNeedsSpaceWithReplacer : HaveChildren<MyChildrenNeedsSpaceWithReplacer>
{
    typedef childrenListType<string, string, string, string> context;
    typedef boost::mpl::replace< context, string, stringAndSpace >::type replacedContext;

    const   replacedContext children;

    MyChildrenNeedsSpaceWithReplacer() : children( "this" ,"sentence" , "needs" , "spaces")
    {
        std::cout << endl << "The children type is:" << endl <<typeid(children).name() << endl;
    }
};

But then the MPL:replace changes the type of the container from my own class, that inherits from boost::fusion::vector, to a boost::fusion::vector4 and that ruins my streaming overloaders.

You may notice that instead of having <string,space> I replace each string with a stringAndSpace.

Having <string,space> would be best- but the other way was easier for me.

So- to summarise:

typedef boost::mpl::replace< context, string, stringAndSpace >::type replacedContext;

casts my container type- can you help to make a function that can at compile time define a type be the following class

struct childrenListType : public  boost::fusion::vector<CHILDREN_TYPES...>

with template arguments of one space between every string I input?

I have posted the full source at: http://ideone.com/XxYTOt

Their compilers typeinfo says that without the mpl replace the children type is: 16childrenListTypeIISsSsSsSsEE

and with : N5boost6fusion7vector4I14stringAndSpaceS2_S2_S2_EE

You can also see the stream overloads fails because it outputs the parenthesis around:

(this  sentence  needs  spaces )

Solution

  • With C++14, I fail to see the need for boost here - or maybe I'm missing one of your requirements? The following will add spaces between the types with plain C++14:

    struct space {};
    template<typename, typename=void> struct spacer;
    
    template<template<typename...> class T>
    struct spacer<T<>> { using type = T<>; };
    
    template<template<typename...> class T,typename T1, typename... Ts>
    struct spacer<T<T1,Ts...>>
        : spacer<T<T1,Ts...>, std::make_index_sequence<2*sizeof...(Ts)+1>> {};
    
    template<template<typename...> class T,typename... Ts, std::size_t... Ns>
    struct spacer<T<Ts...>, std::index_sequence<Ns...>>
    {
        using tuple = std::tuple<Ts...>;
        using type =
            T<std::conditional_t<(Ns%2)==0,std::tuple_element_t<Ns/2,tuple>,space>...>;
    };
    
    template<typename T> using spacer_t = typename spacer<T>::type;
    

    Live example

    The above preserves your outer container type, so in your case if you pass in a boost::fusion::vector<...>, this is also what you'll get as a result (just with additional spaces).