DaveStance
DaveStance

Reputation: 421

Varying the Parameter List of an Function Based on Template Parameter?

I am trying to create a template class that executes a user-specified N-ary function with arguments of type C. To do so, I need some way of specifying the type of this function based on the template parameters. The following code illustrates my problem:

template <typename C, size_t N>
class NaryDispatch {

    typedef typename std::function<void(/* N parameters of type C& */)> NaryFn;

    public:
        NaryDispatch(NaryFn f) : m_function(std::forward<NaryFn>(f)) {}

    private:
        NaryFn m_function;
};

I have been unable to find a way to build the std::function type with a signature of the appropriate arity. I am using C++11 and Boost::MPL extensively so solutions involving either are more than welcome. I have tried to use SFINAE/template parameter deduction on the constructor parameter as follows:

template <
    class... Args,
    typename std::enable_if<sizeof...(Args) == N, C>::type = 0
>
NaryDispatch(std::function<void(Args&...)> fn) : m_function(std::forward<???>(fn)) {}

As you can see, the issue here is that because I have been unable to determine the type the function will take given the template parameters C and N, I'm unable to determine the type of the class member where the function should be stored.

To simplify my intent a bit, for template parameters C and N, the class constructor should accept (and store in a private member) an std::function that returns void and accepts N parameters of type C&. For example, the following should compile:

NaryDispatch<int, 3> disp([](int a, int b, int c) {});

Thanks in advance for any insights you might offer.

Upvotes: 0

Views: 117

Answers (2)

Casey
Casey

Reputation: 42554

Your next problem will be "How do I pass N parameters to the contained std::function?" I think you could simplify substantially by using a dispatcher class template that works with any old list of parameter types:

template <typename...Args>
class Dispatcher {
    typedef typename std::function<void(Args...)> Fn;

    public:
        Dispatcher(Fn f) : m_function(std::move(f)) {}

        void operator()(Args...args) {
          m_function(std::forward<Args>(args)...);
        }

    private:
        Fn m_function;
};

along with a bit of metaprogramming to calculate the proper Dispatcher specialization to handle N parameters of type C&:

template <typename C, size_t N, typename...Args>
struct NaryDispatch_ {
  using type = typename NaryDispatch_<C, N-1, Args..., C&>::type;
};
template <typename C, typename...Args>
struct NaryDispatch_<C, 0, Args...> {
  using type = Dispatcher<Args...>;
};

template <typename C, size_t N>
using NaryDispatch = typename NaryDispatch_<C, N>::type;

DEMO AT IDEONE

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 477228

This shouldn't be too hard. Let's start with the top level:

template <typename C, std::size_t N>
struct NaryDispatch
{
    // details, see below

    using f_type = typename function_maker<C &, N>::type;

    template <typename F>
    NaryDispatch(F && f) : fn_(std::forward<F>(f)) {}

    f_type fn_;
};

Now we just need to implement the trait function_maker:

template <typename T, std::size_t K, typename ...Args>
struct function_maker
{
    using type = typename function_maker<T, K - 1, T, Args...>::type;
};

template <typename T, typename ...Args>
struct function_maker<T, 0, Args...>
{
    using type = std::function<void(Args...)>;
};

Finally, you might also want to provide some kind of constrained call function. Perhaps like this:

template <typename ...Args,
          typename = typename std::enable_if<sizeof...(Args) == N>::type>
void run(Args &&... args)
{
    fn_(std::forward<Args>(args)...);
}

Upvotes: 4

Related Questions