我希望能够推迟对某个对象的成员函数的调用。因此,我需要能够将成员函数指针和参数存储为数据。 我的方法使用 C++ 模板,将成员函数指针视为类型的一部分。函数指针应该是模板参数,因为我希望能够在编译时通过
static_assert
选择允许哪些成员函数(即仅setter);
我正在使用
gcc (GCC) 14.1.1 20240507
和 C++ 23
。
示例代码:
struct Object {
void set();
void set(int test);
int get();
};
// Utils:
template <typename Function>
struct FunctionTraits;
template <
typename Return,
typename... Args
> struct FunctionTraits
<Return (Object::*)(Args...)>
{
using args_tuple_t = std::tuple<Args...>;
using ret_t = Return;
};
// The member function wrapper:
template <
typename Function,
Function function
>
struct SetterTask
{
Object* obj;
typename FunctionTraits<decltype(function)>::args_tuple_t args;
};
// pseudo constructor
template <
typename Return,
typename... Args
>
auto makeSetter(
Object* obj,
Return (Object::*setter)(Args...),
Args... args
)
{
auto tuple = std::tuple( args... );
using SetterType = Return (Object::*)(Args...); // <-- ERROR
return SetterTask<SetterType,setter>{ // <-- ERROR
.obj = obj,
.args = tuple
};
}
template <
typename Function,
Function function
>
auto run(
SetterTask<Function, function> setter
)
{
return std::apply(setter.function,
std::tuple_cat(
std::make_tuple(setter.obj),
setter.args
)
);
}
测试程序:
void test() {
void (Object::*pFunc)(int) = &Object::set;
Object obj;
auto setter = makeSetter( &obj, pFunc, 10 );
run(setter);
}
这是我得到的错误:
template_test.cpp:10:26: required from here
10 | auto setter = makeSetter( &obj, pFunc, 10 );
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
template_test.h:61:9: error: ‘setter’ is not a valid template argument for type ‘void (Object::*)(int)’
61 | };
| ^
template_test.h:61:9: note: it must be a pointer-to-member of the form ‘&X::Y’
我做错了什么?据我了解,类型都很明确,甚至过于明确。 或者这是一个编译器错误?
这是关键部分:
template <
typename Return,
typename... Args
>
auto makeSetter(
Object* obj,
Return (Object::*setter)(Args...),
Args... args
)
{
auto tuple = std::tuple( args... );
using SetterType = Return (Object::*)(Args...); // <-- ERROR
return SetterTask<SetterType,setter>{ // <-- ERROR
.obj = obj,
.args = tuple
};
}
我也尝试过:
using SetterType = decltype(setter); <-- decltype of pointer
return SetterTask<SetterType,setter>{
...
};
...还有...
using SetterType = Return (Object::*)(Args...);
return SetterTask<SetterType,(SetterType)setter>{ <-- explicitly cast pointer
...
};
正如 PiotrNycz 正确指出的那样,模板“值”参数不能从函数参数设置,也不能从任何运行时值设置。 非常明显,如果您考虑一下 - 模板参数是编译时常量。
这是我的解决方案:
template_test.h:
#include <tuple>
#include <type_traits>
struct Object {
double set();
void set(int test);
void set2(int test);
int get();
};
// Allowed member functions:
template <typename Function, Function function>
struct IsAllowedFunction : std::false_type {};
template <> struct IsAllowedFunction
<double (Object::*)(), (double (Object::*)())&Object::set>
: std::true_type {};
template <> struct IsAllowedFunction
<void (Object::*)(int), (void (Object::*)(int))&Object::set>
: std::true_type {};
template <> struct IsAllowedFunction
<void (Object::*)(int), (void (Object::*)(int))&Object::set2>
: std::true_type {};
// Utils:
template <typename Function>
struct FunctionTraits;
template <
typename Return,
typename... Args
> struct FunctionTraits
<Return (Object::*)(Args...)>
{
using args_tuple_t = std::tuple<Args...>;
using ret_t = Return;
};
// The member function wrapper:
template <
typename Function,
Function function
>
struct SetterTask
{
Object* obj;
typename FunctionTraits<Function>::args_tuple_t args;
};
// pseudo constructor
template <
typename Function,
Function function,
typename... Args
>
auto makeSetter(
Object* obj,
Args... args
)
{
static_assert(
IsAllowedFunction<Function,function>::value,
"Invalid function. Valid functions functionare: 'set', 'set2'"
);
auto tuple = std::tuple( args... );
return SetterTask<Function,function>{
.obj = obj,
.args = tuple
};
}
template <
typename Function,
Function function
>
auto run(
SetterTask<Function, function> setter
)
{
return std::apply(
function,
std::tuple_cat(
std::make_tuple(setter.obj),
setter.args
)
);
}
#define MAKE_SETTER(obj, Type, Function, ... ) \
makeSetter<Type, (Type )&Object::Function>( obj, ## __VA_ARGS__ )
main.cpp:
#include "template_test.h"
#include <iostream>
void test() {
Object obj;
// Example 1: `double set()`:
{
auto setter = MAKE_SETTER(&obj,double (Object::*)(),set);
double ret = run(setter);
std::cout << "function returned: " << ret;
}
// Example 2: `void set(int)`:
{
auto setter = MAKE_SETTER(&obj,void (Object::*)(int),set, 10);
run(setter);
}
// Example 3: `void set(int)`:
{
auto setter = MAKE_SETTER(&obj,void (Object::*)(int),set2, 10);
run(setter);
}
// the following is not allowed
// and raises a compile-time error:
// auto getter = MAKE_SETTER(&obj,double (Object::*)(),get);
}