4

when attempting to get functions' signatures when they are input into templates its fairly easy, just do the following:

template <class OutType, class... ArgTypes>
void foo(OutType (*func)(ArgTypes...));

it is only marginally more complicated to get a non-static member function:

template <class OutType, class MemberOf, class... ArgTypes>
void foo(OutType (MemberOf::*func)(ArgTypes...));

// or

template <class OutType, class MemberOf, class... ArgTypes>
void foo(OutType (MemberOf::*func)(ArgTypes...) const);

but how exactly do you combine the two function declarations above into one when it doesn't matter whether or not the input method is const?

7
  • Depending on what you need template<typename Func> void foo(Func func) is my first goto. Commented Oct 3, 2019 at 18:22
  • @NathanOliver I need all the types in the signature available to me within the function unfortunately. Commented Oct 3, 2019 at 18:29
  • I agree with Nathan, using std::invoke you can support more than just function pointers Commented Oct 3, 2019 at 18:29
  • @MatthewL In that case what about template <class OutType, class... ArgTypes> void foo(std::function<OutType(ArgTypes...)>);? Commented Oct 3, 2019 at 18:30
  • that seems like it would work, but foo(std::function<[whole function signature]>(&bar::do)) feels needlessly verbose, especially with descriptive function/class names Commented Oct 3, 2019 at 18:55

1 Answer 1

4

Unfortunately, the presence or absence of const on a non-static member function is not a feature that can be deduced separately from the function type it appertains to. Therefore, if you want to write a single foo template declaration that is limited to accepting pointers to members (but accepts both const and non-const member functions) then it would have to be:

template <class MemberOf, class F>
void foo(F MemberOf::*func);

For example:

#include <type_traits>

template <class MemberOf, class F>
void foo(F MemberOf::*func) {
    static_assert(std::is_same<F, void(int) const>::value);
}

struct S {
    void bar(int) const {}
};

int main() {
    foo(&S::bar);
}

You cannot have F's argument types deduced at that point. You would have to dispatch to a helper function. (But we cannot deduce all the types at once while also writing a single declaration that accepts both const and non-const. If that's the only thing you'll accept, then sorry, it's not possible.) We can do this like so:

template <class T>
struct remove_mf_const;

template <class R, class... Args>
struct remove_mf_const<R(Args...)> {
    using type = R(Args...);
};

template <class R, class... Args>
struct remove_mf_const<R(Args...) const> {
    using type = R(Args...);
};

template <bool is_const, class F, class OutType, class MemberOf, class... ArgTypes>
void foo_helper(F func, OutType (MemberOf::*)(ArgTypes...)) {
    // now you have all the types you need
}

template <class MemberOf, class F>
void foo(F MemberOf::*func) {
    static_assert(std::is_function<F>::value, "func must be a pointer to member function");
    using NonConstF = typename remove_mf_const<F>::type;
    constexpr bool is_const = !std::is_same<F, NonConstF>::value;
    foo_helper<is_const>(func, (NonConstF MemberOf::*)nullptr);
}

Coliru link

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

Comments

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.