5

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)

6
  • Why not simply template<typename Fun> void register_function(Fun fct)? Commented Jul 7, 2015 at 18:50
  • I've update my question with a little bit more code. Inside the lambdas, I may call std::bind or directly use the member function pointer on a new object instance. This is why i really need to access the ReturnType and the Params typenames. Commented Jul 7, 2015 at 18:50
  • Is that your whole codebase? lol really Commented Jul 7, 2015 at 18:54
  • @LightnessRacesinOrbit Full code here if you are interested github.com/Cylix/cpp_reflection/blob/master/includes/… Commented Jul 7, 2015 at 18:56
  • Can you elaborate on "/* do something */"? Commented Jul 7, 2015 at 18:56

1 Answer 1

4

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)...);
        };

        // ...
    }
};
Sign up to request clarification or add additional context in comments.

4 Comments

@T.C. Didn't know that. I guess still want to explicitly check which class it's a member function pointer of.
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.
@SimonNinon Can you use C++14? Can just do auto f = [fct](auto&&... params) { return (Type{}.*fct.second)(std::ref(params)...); };
@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<RetVal(Params...)> as an std::function which inherits from an empty class function_base. This way, function_containers are stored in a generic std::vector<function_base>. 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.