mitchk
mitchk

Reputation: 23

Error when trying to expand template parameter pack

I'm trying to use variadic templates to store the parameter types to a member function. The way I'm trying to achieve this is by associating each type with a key, then storing this key in an std::vector. The code for creating this key is as follows

template <typename T>
class ClassInfo {
public:
    inline static void const* GetClassKey() {
        static char key;
        return &key;
    }
};

Then I use the following code to try to store the keys in an std::vector

class WrappedMemberFunction {
    void *function_pointer; // Holds the member function pointer
    void const* class_type; // Class type key
    void const* return_type; // Return type key
    std::vector<void const*> parameter_types; // Parameter type keys

    void StoreArguments() {}
    template <typename Arg, typename... Args>
    void StoreArguments() {
        parameter_types.push_back(ClassInfo<Arg>::GetClassKey());
        StoreArguments<Args...>(); // Error here: No matching member function for call to 'StoreArguments'
    }


public:
    template <typename Class, typename ReturnType, typename... Args>
    WrappedMemberFunction(ReturnType (Class::*member_pointer)(Args...)) {
        // Store member pointer as regular old void pointer
        function_pointer = (void*&)member_pointer;

        // Store class type
        class_type = ClassInfo<Class>::GetClassKey();

        // Store return type
        return_type = ClassInfo<Class>::GetClassKey();

        // Store parameter types
        StoreArguments<Args...>();
    }
};

What I'm getting stuck on is the variadic recursion needed to store each class key. I am getting an error on the line indicated above, which is the recursive step in trying to expand the parameter pack. What am I doing wrong here?

Upvotes: 2

Views: 120

Answers (1)

Barry
Barry

Reputation: 302852

You have:

// function that is not a template
void StoreArguments() {}

// function template that takes N+1 types
template <typename Arg, typename... Args>
void StoreArguments() {
    parameter_types.push_back(ClassInfo<Arg>::GetClassKey());

    // call function template that takes N types
    StoreArguments<Args...>();
}

Hopefully the comments I added make this clear... you're recursing from a function template taking N+1 types to a function template taking N types. The base case there is a function template taking 0 types. You don't have that, you have a nullary function - which won't be considered.

Your approaches are either to lift your types into values, so your base case actually is a nullary function:

template <class T> struct tag { using type = T; };

void StoreArgumentsImpl() { }

template <typename Arg, typename... Tags>
void StoreArgumentsImpl(tag<Arg>, Tags... tags) {
    parameter_types.push_back(ClassInfo<Arg>::GetClassKey());

    StoreArgumentsImpl(tags...);
}

template <typename... Args>
void StoreArguments() {
    StoreArgumentsImpl(tag<Args>{}...);
}

Or do everything in a single function with the expander trick:

template <typename... Args>
void StoreArguments() {
    using expander = int[];
    (void)expander{0,
        (void(
           parameter_types.push_back(ClassInfo<Args>::GetClassKey())
        ), 0)...
    };
}

Or, in C++17 (can't wait), with fold expressions:

template <typename... Args>
void StoreArguments() {
    (parameter_types.push_back(ClassInfo<Args>::GetClassKey()), ...);
}

Or, also in C++17, with if constexpr (though this will not work with no arguments):

template <typename Arg, typename... Args>
void StoreArguments() {
    parameter_types.push_back(ClassInfo<Args>::GetClassKey());

    if constexpr(sizeof...(Args) > 0) {
        StoreArguments<Args...>();
    }
}

Upvotes: 1

Related Questions