Home > Blockchain >  Restrict function template for member functions only
Restrict function template for member functions only

Time:12-28

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  
}

Working demo

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

Demo

  • Related