I am working in C 11 and have the following code that compiles. But the problem is that the function func
in the below example can also be called with a std::function
, lambda, pointer to a function etc.
Instead, I want that func
should only be called by a pointer to a non-static member function of any class. That is, I want to restrict this function only member function pointers.
template <typename Callable, typename... Args> void func(Callable&& callable, Args&&... args)
{
}
struct Test
{
int someMember(int x)
{
return x;
}
};
void g(int, int, int)
{
}
int main()
{
func(g, 1, 1, 1); //this works currently but it should be rejected in the modified program
func([](int){}, 42); //this works currently but it should be rejected in the modified program
Test test;
func(&Test::someMember, test, 1);// this works currently and should work in the modified version
}
As we can see in the above program, all of the calls to func
works. But I want that only the call func(&Test::someMember, test, 1);
should work and the other two calls should be rejected.
So how can I achieve this. Maybe there is a way to use SFINAE or some other metaprogramming technique.
CodePudding user response:
Maybe there is a way to use SFINAE or some other metaprogramming technique.
That would do it, since we have std::is_member_function_pointer
.
template <typename Callable, typename... Args>
typename std::enable_if<std::is_member_function_pointer<Callable>::value, void>::type
func(Callable callable, Args&&... args)
{
}
If the predicate is false, enable_if produces no type, and our template has no return type, making the function non-viable.
The change to pass by value is because it makes to controlling condition simpler, and because we are only passing pointers to members (fairly cheap to copy).
CodePudding user response:
This can be done by setting up the template parameters in such a way that only pointers to member function are accepted(as shown below). In particular, we can have 4 template parameters corresponding to the class, member function parameters, object of that class and finally the arguments passed to that member function.
template<typename className, typename... Param,typename Ret, typename... Args>
void func(Ret (className::*ptrFunc)(Param... param),className& Object, Args... args)
{
(Object.*ptrFunc)(args...); //std::invoke(ptrFunc, Object, args...) in C 17
}
int main()
{
Test test;
func(&Test::someMember, test, 1);//only this form works now
//other calls will all fails now as you want
//func(g, 1, 1, 1); //error now as needed
//func([](int){}, 42); //error now as needed
}
CodePudding user response:
I think a static_assert
is the perfect tool for the situation. No need to change the signature of func
, and the error message can be whatever you want, so it's clearer than, for example, a substitution failure.
template <typename Callable, typename... Args>
void func(Callable callable, Args&&... args)
{
static_assert(std::is_member_function_pointer<Callable>::value, "callable must be a member function");
}