How about pass the member function pointer to the foo
and using a helper trait we retrieve the types of class, return, and arguments!
Provide a trait something like:
template<typename Class> struct class_traits final {};
template<typename ReType, typename Class, typename... Args>
struct class_traits<ReType(Class::*)(Args...)> final
{
using class_type = Class;
using ret_type = ReType;
};
// traits helpers
template<typename MemFunctionPtr> using class_type = typename class_traits<MemFunctionPtr>::class_type;
template<typename MemFunctionPtr> using ret_type = typename class_traits<MemFunctionPtr>::ret_type;
Now in the foo
#include <functional> // std::invoke
template <typename MemFuncType, typename... Args>
void foo(MemFuncType&& func, Args&&... args)
{
// class type can be retrieved!
class_type<MemFuncType> obj{};
// types other than void!!
ret_type<MemFuncType> res = std::invoke(func, obj, std::forward<Args>(args)...);
// do something with |function_ptr|
}
Now the main-part of the story. The above will only resolve the non-const
, non-volatile
, and non-noexcept
, etc versions of the member function pointers. Exactly what you asked is missing!
For the rest, we need to provide different traits. Inspired from this post, we can do it by macro, which will do the boilerplate duplications of the traits for us:
template<typename Class> struct class_traits final {};
#define CREATE_CLASS_TRAITS(...) \
template<typename ReType, typename Class, typename... Args> \
struct class_traits<ReType(Class::*)(Args...)__VA_ARGS__> final \
{ \
using class_type = Class; \
using ret_type = ReType; \
}
CREATE_CLASS_TRAITS();
CREATE_CLASS_TRAITS(const);
CREATE_CLASS_TRAITS(volatile);
CREATE_CLASS_TRAITS(const volatile);
CREATE_CLASS_TRAITS(&);
CREATE_CLASS_TRAITS(const&);
CREATE_CLASS_TRAITS(volatile&);
CREATE_CLASS_TRAITS(const volatile&);
CREATE_CLASS_TRAITS(&&);
CREATE_CLASS_TRAITS(const&&);
CREATE_CLASS_TRAITS(volatile&&);
CREATE_CLASS_TRAITS(const volatile&&);
CREATE_CLASS_TRAITS(noexcept);
CREATE_CLASS_TRAITS(const noexcept);
CREATE_CLASS_TRAITS(volatile noexcept);
CREATE_CLASS_TRAITS(const volatile noexcept);
CREATE_CLASS_TRAITS(&noexcept);
CREATE_CLASS_TRAITS(const& noexcept);
CREATE_CLASS_TRAITS(volatile& noexcept);
CREATE_CLASS_TRAITS(const volatile& noexcept);
CREATE_CLASS_TRAITS(&& noexcept);
CREATE_CLASS_TRAITS(const&& noexcept);
CREATE_CLASS_TRAITS(volatile&& noexcept);
CREATE_CLASS_TRAITS(const volatile&& noexcept);
#undef CREATE_CLASS_TRAITS
// traits helpers
template<typename MemFunctionPtr> using class_type = typename class_traits<MemFunctionPtr>::class_type;
template<typename MemFunctionPtr> using ret_type = typename class_traits<MemFunctionPtr>::ret_type;
Now we can:
template <typename MemFuncType, typename... Args>
void foo(MemFuncType&& func, Args&&... args)
{
class_type<MemFuncType> obj{};
using Type = decltype(std::invoke(func, obj, std::forward<Args>(args)...));
static_assert(std::is_same_v<ret_type<MemFuncType>, Type>, "are not same");
std::invoke(func, obj, std::forward<Args>(args)...);
// do something with |function_ptr|
}
class MyClass
{
public:
void func() { std::cout << "MyClass::void func()\n"; }
void func1() const { std::cout << "MyClass::void func1() const\n"; }
void func2() const noexcept{ std::cout << "MyClass::void func2() const noexcept\n"; }
int func3() const { std::cout << "MyClass::func3() const\n"; return {}; }
int func4(int a, double b) const { std::cout << "MyClass::func3() const"<< a << " " << b << "\n"; return {}; }
};
int main()
{
foo(&MyClass::func);
foo(&MyClass::func1);
foo(&MyClass::func2);
foo(&MyClass::func3);
foo(&MyClass::func4, 1, 2.);
return 0;
}
(See Live Demo Online)