C++是否可以实现指向不同参数列表的函数指针
Is it possible for C++ to implement function pointers point to different parameter lists?
我最近写了一篇关于类成员函数回调函数的文章。我需要保存回调对象和函数指针,然后调用函数指针,并在需要回调的地方填写适当的参数。
我一开始是typedef void (AAA::*Function)(int a, int b);
的一种形式,但当我需要支持成员函数的不同参数列表时,我显然需要一种动态的方式来实现它
class AAA
{
public:
int add(int a, int b)
{
return (a + b);
}
};
class BBB
{
public:
void setValue(std::string value)
{
this->value = value;
}
private:
std::string value;
};
class CCC
{
public:
void bind(??? p) // Binding objects and callback functions.
{
this->p = p;
}
template <class... Args>
auto callback(Args&&... args) // Autofill parameter list.
{
return this->p(std::forward<Args>(args)...);
}
private:
??? p; // How is this function pointer implemented?
};
int main()
{
AAA aaa;
BBB bbb;
CCC ccc;
ccc.bind(???(aaa, &AAA::add));
int number = ccc.callback(5, 6);
ccc.bind(???(bbb, &BBB::setValue));
ccc.callback("Hello");
system("pause");
return 0;
}
我不知道如何实现函数指针"???"。
您基本上要求拥有完全动态类型化和检查过的函数调用。
要进行完全动态的函数调用,基本上必须抛出C++函数调用系统。
这是个坏主意,但我会告诉你怎么做。
一个动态可调用对象大致如下:
using dynamic_function = std::function< std::any( std::vector<std::any> ) >
在哪里使用
struct nothing_t {};
当我们想要返回CCD_ 2时。
然后,您编写一个机器,它接受一个对象和一个特定的签名,并将其封装起来。
template<class R, class...Args, class F>
struct dynamic_function_maker {
template<std::size_t...Is>
dynamic_function operator()(std::index_sequence<Is...>, F&& f)const {
return [f=std::forward<F>(f)](std::vector<std::any> args)->std::any {
if (sizeof...(Is) != args.size())
throw std::invalid_argument("Wrong number of arguments");
if constexpr( std::is_same< std::invoke_result_t<F const&, Args... >, void >{} )
{
f( std::any_cast<Args>(args[Is])... );
return nothing_t{};
}
else
{
return f( std::any_cast<Args>(args[Is])... );
}
};
}
dynamic_function operator()(F&& f)const {
return (*this)(std::make_index_sequence<sizeof...(Args)>{}, std::forward<F>(f));
}
};
template<class R, class...Args, class F>
dynamic_function make_dynamic_function(F f){
return dynamic_function_maker<R,Args...,F>{}(std::forward<F>(f));
}
接下来,您将需要推导函数指针等的签名:
template<class R, class...Args>
dynamic_function make_dynamic_function(R(*f)(Args...)){
return dynamic_function_maker<R,Args...,F>{}(std::forward<F>(f));
}
template<class Tclass R, class...Args>
dynamic_function make_dynamic_function(T* t, R(T::*f)(Args...)){
return dynamic_function_maker<R,Args...,F>{}(
[t,f](auto&&...args)->decltype(auto){return (t->*f)(decltype(args)(args)...);}
);
}
然后在修复了上面的拼写错误之后,你应该能够解决你原来的问题。
同样,作为一个能够真正编写和理解上述代码的人,我强烈建议您不要使用它。它是脆弱和危险的。
几乎从来没有一个好的理由将回调存储在你不知道要用什么参数调用它的地方。
对于要调用它的每一组参数,都应该有不同类型和实例的CCC
。当人们问这个问题时,他们问错了99/100次。
C++是一种类型安全的语言。这意味着你不能完全按照你在问题中概述的去做。指向采用特定参数的函数的指针与指向采用不同参数的函数指针的类型不同。这是C++的基础。
std::bind
可以用于将不同类型的类型擦除为同一类型,但最后会得到一个类型,该类型只能使用匹配的参数集(如果有的话)调用。不可能调用带有实际参数的"底层"绑定函数。这是因为std::bind
的全部目的是让它们消失,无法访问。这就是std::bind
的作用。
在保持C++类型安全的边界和约束的情况下,您只有有限的一组选项来实现这一点。
-
以某种方式使用
void *
。事实上,不要。不要那样做。这只会引起更多的问题和头痛。 -
有一个单独的回调列表和类,每个列表对应一组采用特定参数的回调。在调用回调时,您必须知道要传递什么参数。因此,只需从适当的列表中获取回调即可。
-
使用
std::variant
。类型安全的std::variant
仅为C++17(但boost有一个类似的模板,基本上是等效的,可用于较旧的C++版本)。所有回调都接受一个std::variant
参数,一个可能的参数集的变体(指定为其中的std::tuple
,或某个类/结构实例)。如果每个回调接收到包含错误参数值的std::variant
,则必须决定该怎么办。
或者,std::variant
可以是不同std::function
类型的变体,从而将类型检查的责任转移到调用者身上,而不是每次回调。
归根结底,C++从根本上说是一种类型安全的语言;这正是人们选择使用C++而不是不具有相同类型安全性的不同语言的原因之一。
但作为一种类型安全的语言,这意味着在处理不同类型时有一定的局限性。具体来说:你不能。C++中的所有内容始终是,而且必须是单一类型。
- 概念中的cv限定符需要表达式参数列表
- 在没有参数列表的情况下使用模板名称"Event"无效,模板问题
- 错误 没有与参数列表匹配的重载函数"getline"实例
- std::vector 没有重载函数的实例与参数列表匹配
- 模板参数列表中的 false 在模板初始化期间计算为什么?
- C++模板/别名 - 模板参数列表中参数 1 处的类型/值不匹配
- 构造函数/函数声明参数列表中的统一初始化
- 模板化检查是否存在带有参数列表的类成员函数?
- 如何将类成员方法的参数列表自动填充写入可变参数?
- 带有整数的变量参数列表
- 转发变量参数列表以模拟 std::thread
- 错误:"模板<类_Tp,类_Dp>类 std::unique_ptr"的模板参数列表中参数 1 的类型/值不匹配
- C++-将具有引用的长参数列表重构为结构
- 缺少别名模板C++参数列表
- C++如果两个模板函数都与参数列表匹配,将调用哪个模板
- "extern"声明以及带有和不带参数列表的类模板实例的后续定义
- 使用显式模板参数列表和 [temp.arg.explicit]/3 的函数调用的演绎失败
- 没有函数模板的实例与我不知道为什么的参数列表匹配
- 我可以使用宏自动构建参数列表吗?
- 类成员函数参数列表是否可以依赖于模板参数?