Rikib1999
Rikib1999

Reputation: 209

C++ template recursion undefined type

I have templates for instructions like add, store, jump, return and variable. Then I created static_list template for storing these instructions. I want to execute them one by one. I use enable_if_else and comparing member instruction for checking what type the current instruction is and branch accordingly. However, I cant continue after the first check, if it is a variable type instruction. The execute_v and execute_s are working when it is like this:

template<int index, class ...Commands>
struct execute_s <index, static_list<Commands ...>> {
    static constexpr int value = enable_if_else_v < static_list_get<index, static_list<Commands...>>::type::instruction == 1,
                                    1,
                                    0>::result ;
};

It returns 1 or 0 depending on what type of instruction I put as first into execute_v. This works correctly. However when I want to recursively continue onto next command, it wont work, it looks like this:

template<int index, class ...Commands>
struct execute_s <index, static_list<Commands ...>> {
    static constexpr int value = enable_if_else_v < static_list_get<index, static_list<Commands...>>::type::instruction == 1,
                                    execute_s<index + 1, Commands...>::value,
                                    0>::result ;
};

The problem is with execute_s<index + 1, Commands...>::value as it seems it does not stop when instruction is not variable type, but I dont know why is it so, because it should stop then. Maybe I am wrongly unpacking Commands? How can I achieve that I can push forward next index with all the commands to the next level of recursion? These are the templates:

template<size_t VariableId, int InitValue>
struct variable {
    static constexpr size_t variableId = VariableId;
    static constexpr int value = InitValue;
    static constexpr int instruction = 1;
};

template<size_t DestinationVariableId, size_t SourceVariableId>
struct store {
    static constexpr size_t destinationVariableId = DestinationVariableId;
    static constexpr size_t sourceVariableId = SourceVariableId;
    static constexpr int instruction = 2;
};

template<size_t DestinationVariableId, size_t SourceVariableId>
struct add {
    static constexpr size_t destinationVariableId = DestinationVariableId;
    static constexpr size_t sourceVariableId = SourceVariableId;
    static constexpr int instruction = 3;
};

template<size_t VariableId, size_t CommandNumber>
struct jump_if_not_zero {
    static constexpr size_t variableId = VariableId;
    static constexpr size_t commandNumber = CommandNumber;
    static constexpr int instruction = 4;
};

template<size_t VariableId>
struct ret {
    static constexpr size_t variableId = VariableId;
    static constexpr int instruction = 5;
};

//-------------enable_if_else_v-------------
template<bool Cond, int TrueValue, int FalseValue>
struct enable_if_else_v;

template<int TrueValue, int FalseValue>
struct enable_if_else_v<true, TrueValue, FalseValue> {
    static constexpr int result = TrueValue;
};

template<int TrueValue, int FalseValue>
struct enable_if_else_v<false, TrueValue, FalseValue> {
    static constexpr int result = FalseValue;
};

//-------------static_list_get-------------
template<size_t Idx, class List>
struct static_list_get;

template<size_t Idx, class FirstElement, class ...Elements>
struct static_list_get<Idx, static_list<FirstElement, Elements ...>>
    : static_list_get<Idx - 1, static_list<Elements ...>> {
};

template<class FirstElement, class ...Elements>
struct static_list_get<1, static_list<FirstElement, Elements ...>> {
    using type = FirstElement;
};

//-------------execute_s-------------
template<int index, class ...Commands>
struct execute_s;

template<int index, class ...Commands>
struct execute_s <index, static_list<Commands ...>> {
    static constexpr int value = enable_if_else_v < static_list_get<index, static_list<Commands...>>::type::instruction == 1,
                                    execute_s<index + 1, Commands...>::value,
                                    0>::result ;
};

template<class ...Commands>
constexpr int execute_v = execute_s<1, static_list<Commands...>>::value;

And this is main function example:

int main() {
    constexpr size_t PREV = 0;
    constexpr size_t CURR = 1;
    constexpr size_t OLD_CURR = 2;
    constexpr size_t N = 3;
    constexpr size_t MINUS1 = 4;
    
    constexpr int fib_n = execute_v<
        variable<PREV, 0>,              // 1: PREV = 0
        variable<CURR, 1>,              // 2: CURR = 1
        variable<OLD_CURR, 0>,          // 3: OLD_CURR = 0
        variable<N, 10>,                // 4: N = 10
        variable<MINUS1, -1>,           // 5: MINUS1 = -1

        jump_if_not_zero<N, 8>,         // 6: if (N > 0) goto 8
        ret<PREV>,                      // 7: return 0

        store<OLD_CURR, CURR>,          // 8: OLD_CURR = CURR
        add<CURR, PREV>,                // 9: CURR += PREV
        store<PREV, OLD_CURR>,          // 10: PREV = OLD_CURR
        add<N, MINUS1>,                 // 11: N += -1
        jump_if_not_zero<N, 8>,         // 12: if (N > 0) goto 8
        ret<PREV>                       // 13: return PREV
    >;
    static_assert(fib_n == 55, "Wrong result");
}

Compile errors: use of undefined type 'execute_s<2, variable<0, 0>, variable<1, 1>, variable<2, 0>...(all commands)..., ret<0>>'

I tried to pack Commands to list like this: execute_s<index + 1, static_list<Commands...>>::value, but it was not working, it does not stop the recursion.

Upvotes: 1

Views: 102

Answers (1)

super
super

Reputation: 12928

Your example is a bit incomplete, but I'll try to give you the general idea with SFINAE.

template <bool condition, int index, typename list>
struct get_next_command_or_zero;

template <int index, typename list>
struct get_next_command_or_zero<false, index, list> {
    static constexpr int value = 0;
}

template <int index, typename... Commands>
struct get_next_command_or_zero<true, index, static_list<Commands...>> {
    static constexpr int value = execute_s<index + 1, Commands...>::value; // I think you example is wrong here since, execure_s is expecting a static_list?
}

Here we use sfinae to pick the right partial specialization first, so the lookup of execure_s<...>::value is only done when that path is chosen and otherwise discarded and left unevaluated.

Now in execute_s you can use this like

static constexpr int value = get_next_command_or_zero<static_list_get<index, static_list<Commands...>>::type::instruction == 1, index, static_list<Commands...>>::value;

Upvotes: 2

Related Questions