ssb
ssb

Reputation: 7502

Template specialization and Function overloading in C++

I am trying to build a static wrapper function that will be able to call member and and non-member function pointers.

What I have come up with is as follows -

template <typename Derived>
struct CallAny {
  template<typename F> using ReturnType = typename std::function<typename std::remove_pointer<F>::type>::result_type;

  template<typename F, bool IsMember, typename... Args>
  static ReturnType<F> function_wrapper(Derived* obj, F func, Args... args);

  template<typename F, typename... Args>
  static ReturnType<F> function_wrapper<F, true, Args...>(Derived *obj, F func, Args... args) {
    return obj->*func(args...);
  }

  template<typename F, typename... Args>
  static ReturnType<F> function_wrapper<F, false, Args...>(Derived *obj, F func, Args... args) {
    return func(args...);
  }

};

struct CallAnyDerived : public CallAny<CallAnyDerived> {}; 

Here F is a function pointer type that has to be called with arguments given in Args.... F might be a C-style function pointer or a member of class Derived which inherits from CallAny.

When I try to compile this code, I get an error saying -

XXXXXXXXXXX: error: function template partial specialization ‘fucntion_wrapper<F, true, Args ...>’ is not allowed
   static ReturnType<F> function_wrapper<F, true, Args...>(Derived *obj, F func, Args... args) {
                                                                                             ^
XXXXXXXXXXX: error: function template partial specialization ‘fucntion_wrapper<F, false, Args ...>’ is not allowed
   static ReturnType<F> function_wrapper<F, false, Args...>(Derived *obj, F func, Args... args) {
                                                                                              ^

How do I fix this?

Upvotes: 1

Views: 116

Answers (2)

Ryan Haining
Ryan Haining

Reputation: 36802

In general you cannot partially specialize a function. The way around that is to create a helper struct with something like a static R call(...), and partially specialize the helper struct instead.

In this case you don't need template specialization at all, you can use overloading

template <typename Derived>
struct CallAny {
  template<typename F> using ReturnType = typename std::function<typename std::remove_pointer<F>::type>::result_type;

  // takes pointer to member function
  template<typename R, typename... Ts, typename... Args>
  static R function_wrapper(Derived* obj, R (Derived::*func)(Ts...), Args... args) {
    return obj->*func(args...);
  }

  // takes everything else
  template<typename F, typename... Args>
  static ReturnType<F> function_wrapper(Derived *obj, F func, Args... args) {
    return func(args...);
  }

};

Upvotes: 4

Jarod42
Jarod42

Reputation: 217255

You may use SFINAE:

template <typename Derived>
struct CallAny {
  template<typename F, typename... Args>
  static
  auto
  function_wrapper(Derived* obj, F func, Args&&... args)
  -> std::enable_if_t<
         std::is_member_function_pointer<F>::value,
         decltype((obj->func)(std::forward<Args>(args)...))>
  {
      return (obj->func)(std::forward<Args>(args)...);
  }

  template<typename F, typename... Args>
  static
  auto
  function_wrapper(Derived* obj, F func, Args&&... args)
  -> std::enable_if_t<
         !std::is_member_function_pointer<F>::value,
         decltype(func)(/*obj,*/std::forward<Args>(args)...))>
  {
      return func(/*obj,*/std::forward<Args>(args)...);
  }

};

or tag dispatching:

template <typename Derived>
struct CallAny {
  template<typename F, typename... Args>
  static
  auto
  function_wrapper(Derived* obj, F func, Args&&... args)
  {
      return impl(std::is_member_function_pointer<F>{}, obj, func, std::forward<Args>(args)...);
  }

  template<typename F, typename... Args>
  static
  auto
  impl(std::true_type, Derived* obj, F func, Args&&... args)
  -> decltype((obj->func)(std::forward<Args>(args)...))
  {
      return (obj->func)(std::forward<Args>(args)...);
  }

  template<typename F, typename... Args>
  static
  auto
  impl(std::true_type, Derived* obj, F func, Args&&... args)
  -> decltype(func)(/*obj,*/std::forward<Args>(args)...))>
  {
      return func(/*obj,*/std::forward<Args>(args)...);
  }
};

Upvotes: 1

Related Questions