Question

I'm having some troubles understanding MPL placeholders.
Could someone please explain me why this code fails to compile?

I would expect the number 0, 1 & 2 to be printed but it seems the placeholder doesn't get replaced with the actual type when the compiler tries to determine the type of the default template parameter of Wrapper.

#include <iostream>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>

template <typename T> struct Traits;

template<> struct Traits<int>  { typedef boost::mpl::int_<0> type; };
template<> struct Traits<char> { typedef boost::mpl::int_<1> type; };
template<> struct Traits<bool> { typedef boost::mpl::int_<2> type; };

template <typename T, typename Type=typename Traits<T>::type > struct Wrapper
{
    Wrapper() { std::cout << "Value: " << Type::value << std::endl; }

    T value;
};

int main()
{
    typedef boost::mpl::inherit_linearly<
                boost::mpl::vector<int, char, bool>,
                boost::mpl::inherit<boost::mpl::_1, Wrapper<boost::mpl::_2> > >::type Object;

    Object obj;

    return 0;
}

This is the error from gcc-4.1.2 (I know... old compiler at work)

# g++4 -I ../boost test.cpp -o test
test.cpp: In function 'int main()':
test.cpp:24: error: invalid use of undefined type 'struct Traits<mpl_::arg<2> >'
test.cpp:7: error: declaration of 'struct Traits<mpl_::arg<2> >'
test.cpp:24: error: template argument 2 is invalid
test.cpp:24: error: template argument 2 is invalid
test.cpp:24: error: template argument 2 is invalid
test.cpp:24: error: expected initializer before 'Object'
test.cpp:26: error: 'Object' was not declared in this scope
test.cpp:26: error: expected `;' before 'obj'

EDIT: After Acorbe's answer below I've made a variant of my example program to show why his proposed solution does not fit my needs. This clarifies what I'm after. In this case I would expect the text TYPE_A, TYPE_B, TYPE_A to be printed. The g++ error is the same.

#include <iostream>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>

enum WrapperType { TYPE_A, TYPE_B };

template <typename T> struct Traits;

template<> struct Traits<int>  { typedef boost::mpl::int_<TYPE_A> type; };
template<> struct Traits<char> { typedef boost::mpl::int_<TYPE_B> type; };
template<> struct Traits<bool> { typedef boost::mpl::int_<TYPE_A> type; };

template <typename T, typename Type=typename Traits<T>::type > struct Wrapper;

template <typename T>
struct Wrapper<T, boost::mpl::int_<TYPE_A> >
{
    Wrapper() : value (0) { std::cout << "TYPE_A" << std::endl; }

    T value;
};

template <typename T>
struct Wrapper<T, boost::mpl::int_<TYPE_B> >
{
    Wrapper() { std::cout << "TYPE_B" << std::endl; }

    T value;
};

int main()
{
    typedef boost::mpl::inherit_linearly<
                boost::mpl::vector<int, char, bool>,
                boost::mpl::inherit<boost::mpl::_1, Wrapper<boost::mpl::_2> > >::type Object;

    Object obj;

    return 0;
}

This is the error from gcc-4.5.4

# g++ -I ../boost test.cpp -o test
test.cpp: In functie ‘int main()’:
test.cpp:37:79: fout: invalid use of incomplete type ‘struct Traits<mpl_::arg<2> >’
test.cpp:9:34: fout: declaration of ‘struct Traits<mpl_::arg<2> >’
test.cpp:37:79: fout: template argument 2 is invalid
test.cpp:37:81: fout: template argument 2 is invalid
test.cpp:37:83: fout: template argument 2 is invalid
test.cpp:37:91: fout: expected initializer before ‘Object’
test.cpp:39:9: fout: ‘Object’ was not declared in this scope
test.cpp:39:16: fout: expected ‘;’ before ‘obj’

This is (partially) the error from clang++-3.1:

test.cpp:15:50: error: implicit instantiation of undefined template 'Traits<mpl_::arg<2> >'
    template <typename T, typename Type=typename Traits<T>::type > struct Wrapper;
                                                 ^
test.cpp:37:57: note: in instantiation of default argument for 'Wrapper<mpl_::arg<2> >' required here
                    boost::mpl::inherit<boost::mpl::_1, Wrapper<boost::mpl::_2> > >::type Object;
                                                        ^~~~~~~~~~~~~~~~~~~~~~~
test.cpp:9:34: note: template is declared here
    template <typename T> struct Traits;
Was it helpful?

Solution 2

Allright... I seem to have cracked it...
As often, an extra level of abstraction did the trick.

I created a metafunction class (genWrapper) which is passed to mpl::apply1 together with the placeholder. The metafunction in there would then return the expected wrapper type.

Here is the resulting program (the 2nd version).
Thanks to all for the help and pointers.

#include <iostream>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>

enum WrapperType { TYPE_A, TYPE_B };

template <typename T> struct Traits;

template<> struct Traits<int>  { typedef boost::mpl::int_<TYPE_A> type; };
template<> struct Traits<char> { typedef boost::mpl::int_<TYPE_B> type; };
template<> struct Traits<bool> { typedef boost::mpl::int_<TYPE_A> type; };

template <typename T, typename Type = typename Traits<T>::type> struct Wrapper;

template <typename T>
struct Wrapper<T, boost::mpl::int_<TYPE_A> >
{
    Wrapper() : value (0) { std::cout << "TYPE_A" << std::endl; }

    T value;
};

template <typename T>
struct Wrapper<T, boost::mpl::int_<TYPE_B> >
{
    Wrapper() { std::cout << "TYPE_B" << std::endl; }

    T value;
};

struct genWrapper
{
    template <typename T>
    struct apply
    {
        typedef Wrapper<T> type;
    };
};

int main()
{
    typedef boost::mpl::inherit_linearly<
                boost::mpl::vector<int, char, bool>,
                boost::mpl::inherit<boost::mpl::_1, boost::mpl::apply1<genWrapper, boost::mpl::_2> > >::type Object;

    Object obj;

    return 0;
}

This is the resulting output:

# g++4 -I ../boost test.cpp -o test
# ./test
TYPE_A
TYPE_B
TYPE_A

OTHER TIPS

I made a little change in your code splitting the typedef induced by the default template parameter in two simpler typedefs.

Consider this:

template < typename T , typename Super_Type = Traits<T> > struct Wrapper
{ 
     typedef typename Super_Type::type Type;
     Wrapper() { std::cout << "Value: " << Type::value << std::endl; }

     T value;
};

This way it compiles. My suspect is that your orginal expression (although correct) is somehow too complicated to be correctly expanded by the compiler.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top