1

I was working on porting some Windows c++ code to linux, which involves template specialization and boost::mpl. My problem is that (1) cannot get the class template partial specialization compile (due to argument dependence), (2) or if template can be get away from, another meta programming technique is needed to loop over a sequence of compile-time constant integers.
I hope the code snippet could be clearer than my description.

#include <vector>
#include <boost/mpl/at.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/vector.hpp>

class State_Idle{};
class State_WaitingToTurnOn{};
class State_WaitingToTurnOff{};

typedef boost::mpl::vector <
    State_Idle,
    State_WaitingToTurnOn,
    State_WaitingToTurnOff
> ::type StateListX;

#ifdef __linux__

template <class StateList>class MosStateMachine;
typedef std::vector<void *> StateTable;

#if 0
// Failed attempt NO.1
// Of course it fails, as there is no partial specialization of function template
template <class StateList, int N>
static void FillTable(StateTable& table)
{
    table.at(N) = (void*)(&MosStateMachine<StateList>::template PerformStateTransition
        <typename boost::mpl::at < StateList, boost::mpl::int_<N> > ::type >);
    FillTable<StateList, N + 1>(table);
}

template <class StateList>
void FillTable <StateList, boost::mpl::size<StateList>::value>(StateTable& table){}
// error: function template partial specialization ‘FillTable<StateList, boost::mpl::size<Sequence>::value>’ is not allowed

template <class StateList>
static StateTable GenerateTable()
{
    StateTable table(boost::mpl::size<StateList>::value);
    FillTable<StateList, 0>(table);
    return table;
}

#endif

#if 1
// Failed attempt NO.2
// Leverage class template partial specialization
// It fails because the "specialized template argument" is dependent on another argument 
// (boost::mpl::size<StateList>::value  to  StateList)
template <class StateList, int N>
struct Bar
{
    void operator()(StateTable& table)
    {
        table.at(N) = &MosStateMachine<StateList>::template PerformStateTransition
            < typename boost::mpl::at < StateList, boost::mpl::int_<N> > ::type > ;
        Bar<StateList, N + 1> b;
        b(table);
    }
};

// error: template argument ‘boost::mpl::size<Sequence>::value’ involves template parameter(s)
template <class StateList>
struct Bar < StateList, boost::mpl::size<StateList>::value >
{
    void operator()(StateTable& table){}
};

template <class StateList>
static StateTable GenerateTable()
{
    StateTable table(boost::mpl::size<StateList>::value);
    Bar<StateList, 0> b;
    b(table);
    return table;
}
#endif

#endif // !__linux__

template <class StateList>
class MosStateMachine
{
public:
    explicit MosStateMachine(){}
    ~MosStateMachine(){}
    template <class State> void PerformStateTransition(){}

#ifndef __linux__
    // Windows version. It works with MSVC. But does not conform to C++ standard and g++. 
    // Specialization should be out of this class. Then I had failed attempts NO.1 and 2 as above. 
    struct detail
    {
        typedef std::vector<void (MosStateMachine::*)()> StateTable;
        static const int NumberOfStates = boost::mpl::size<StateList>::value;

        template <int N>
        static void FillTable(StateTable& table)
        {
            // The following line of code is the very essential that all these is about. 
            // Need to do this over a sequence of compile time integer constant
            table.at(N) = &MosStateMachine::PerformStateTransition <
                typename boost::mpl::at<StateList, boost::mpl::int_<N> >::type > ;
            FillTable<N + 1>(table);
        }

        template <> static void FillTable<detail::NumberOfStates>(StateTable& table){}

        static StateTable GenerateTable()
        {
            StateTable table(NumberOfStates);
            FillTable<0>(table);
            return table;
        }
    };
#endif // !__linux__

    void PerformStateTransitionById(int stateId)
    {
#ifndef __linux__
        static typename std::vector<void (MosStateMachine::*)()> lookupTable = detail::GenerateTable();
#else
        static typename std::vector<void (MosStateMachine::*)()> lookupTable = GenerateTable<StateList>();
#endif // !__linux__

        (this->*(lookupTable.at(stateId)))();
    }
};

int main(int argc, char* argv[])
{
    MosStateMachine<StateListX>();
    return 0;
}
nzabcd
  • 11
  • 3
  • 2
    http://stackoverflow.com/help/mcve – πάντα ῥεῖ Jul 23 '15 at 22:10
  • There's this very nifty keyword `constexpr` which was added to C++ (available in C++11, even more powerful in C++14). I suggest you research it and see whether it solves your problem. – Ben Voigt Jul 23 '15 at 22:27
  • " cannot get the partial specialization template (function or class) compile, " - there is no such thing as function partial specialization – M.M Jul 24 '15 at 01:23
  • Excuse for my working. Partial function template specialization is not allowed. That's why I tried class template to do that after, which failed as well. I am trying to figure out if constexpr as @BenVoigt menstioned work or not. – nzabcd Jul 24 '15 at 02:31
  • So, do I understand correctly that you have an mpl vector of types and you want to generate a table of void pointers to instantiations of each of those types? That is what your example seems to attempt. Your question is about something else. – dhavenith Jul 24 '15 at 06:12
  • [This answer](http://stackoverflow.com/a/22486607/2417774) seems to solve your exact problem (You could also use `boost::mpl::int_` instead of `std::integral_constant`). [Your program now at least compiles](http://coliru.stacked-crooked.com/a/f3af7adbcc480e8b) although I think that what you want to achieve would be easier using `mpl::range_c` and `mpl::for_each`. – llonesmiz Jul 26 '15 at 05:42
  • [An approach](http://coliru.stacked-crooked.com/a/e2f9b2c6ae05e5df) using `range_c` and `for_each`. – llonesmiz Jul 26 '15 at 06:06
  • I think @cv_and_he 2nd solution is really what I want. No template specialization. Appreciated. – nzabcd Jul 27 '15 at 21:56

0 Answers0