6

I have the following code base:

template <typename Type>
class SomeClass {
public:
    template <typename ReturnType, typename... Params>
    void register_function(const std::pair<std::string, ReturnType (Type::*)(Params...)> fct) {
        auto f = [fct](Params... params) -> ReturnType { return (Type().*fct.second)(std::ref(params)...); }
        // ...
    }
};

This works when I pass a pointer to a member-function (non-const). However, if I want to pass a pointer to a const member-function, it results in a compile error and I must duplicate the above function to get this code:

template <typename Type>
class SomeClass {
public:
    template <typename ReturnType, typename... Params>
    void register_function(const std::pair<std::string, ReturnType (Type::*)(Params...)> fct) {
        auto f = [fct](Params... params) -> ReturnType { return (Type().*fct.second)(std::ref(params)...); }
        // ...
    }

    template <typename ReturnType, typename... Params>
    void register_function(const std::pair<std::string, ReturnType (Type::*)(Params...) const> fct) {
        auto f = [fct](Params... params) -> ReturnType { return (Type().*fct.second)(std::ref(params)...); }
        // ...
    }
};

Now, I can pass both const-member-functions and non-const-member-functions. But, now, the code is duplicate and maintainability is reduced.

Is there a way to merge these two functions into a function taking both const-member-functions and non-const-member-functions?

Important note: I must really take a pointer function as parameter (no std::function).

Edit: I've added a little bit more code. Inside the functions, I build a closure matching the member function signature (same return types and params). This closure will be stored and used later for making reflection (more here)

Simon Ninon
  • 2,371
  • 26
  • 43

1 Answers1

5

You could write a type trait, based on which will tell you if some MF is a pointer-to-member function on Type:

template <typename C, typename T>
struct is_pointer_to_member_helper : std::false_type { };

template <typename C, typename T>
struct is_pointer_to_member_helper<C, T C::*> : std::is_function<T> { };

template <typename C, typename T>
struct is_pointer_to_member : is_pointer_to_member_helper<C,
                                  std::remove_cv_t<T>
                              > { };

And use it to ensure that you only get one of those:

template <typename Type>
class SomeClass {
public:
    template <typename MF>
    std::enable_if_t<is_pointer_to_member<Type, MF>::value>
    register_function(const std::pair<std::string, MF> fct) 
    {
        auto f = [fct](auto&&... params) {
            return (Type{}.*fct.second)(std::forward<decltype(params)>(params)...);
        };

        // ...
    }
};
Barry
  • 286,269
  • 29
  • 621
  • 977
  • @T.C. Didn't know that. I guess still want to explicitly check which class it's a member function pointer of. – Barry Jul 07 '15 at 18:48
  • Interesting answer. However, I need to access the ReturnType and the Params... typenames. I've updated my question with a little bit more code: I use lambdas in my code where I may call std::bind or directly use the function pointer on a new Type instance. – Simon Ninon Jul 07 '15 at 18:51
  • @SimonNinon Can you use C++14? Can just do `auto f = [fct](auto&&... params) { return (Type{}.*fct.second)(std::ref(params)...); };` – Barry Jul 07 '15 at 19:11
  • @Barry Tell me if I'm wrong, but it means that Params types will be deduced at compile time when the lambda will be called? The problem is that the lambda is stored in a templated class function_container as an std::function which inherits from an empty class function_base. This way, function_containers are stored in a generic std::vector. Then, at runtime, when user want to make reflection, function_base is dynamically cast to function_container<>. That's why I don't think that Params types can be deduced at compile time. – Simon Ninon Jul 07 '15 at 19:31