C++ 通用回调实现
Posted
技术标签:
【中文标题】C++ 通用回调实现【英文标题】:C++ generic callback implementation 【发布时间】:2021-05-25 12:40:08 【问题描述】:我有一个代码,它以 XML 的形式从 flash player 获取消息,将它们解析为函数和参数,并为该函数调用注册的回调。 我要替换的那段代码做得很好(几乎)通用回调机制: code for the generic callback implementation of flashSDK (ASInterface.inl).
问题在于这段代码是为 flash 编写的,我想替换 flash 并使用具有相同接口的其他服务。这种回调机制是否有任何标准实现(std?boost?其他开源的东西?)?
此代码实现了通用回调机制,您可以在映射中注册具有多个参数和类型的函数:
void SomethingHappened(int a, int b) print a + b;
void SomethingElseHappened(string abcd) print abcd;
callbacks["SomethingHappened"] = &SomethingHappened;
callbacks["SomethingElseHappened"] = &SomethingElseHappened;
然后搜索它并使用参数数组调用:
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
HRESULT result = itCallback->second.Call(arguments, returnValue);
完整用法示例:
//init callbacks
std::map<std::wstring, Callback> callbacks;
void SomethingHappened(int a, int b) print a + b;
void SomethingElseHappened(string abcd) print abcd;
callbacks[functionName] = &SomethingHappened;
void MessageArrived(string xmlInput)
string functionName = parseFunctionName(xmlInput);
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
//parse arguments
std::vector<std::wstring> args;
_Args::split(xml, args);
ASValue::Array arguments;
for (size_t i = 0, s = args.size(); i < s; ++i)
ASValue arg; arg.FromXML(args[i]);
arguments.push_back(arg);
ASValue returnValue;
//***this is where the magic happens: call the function***
HRESULT result = itCallback->second.Call(arguments, returnValue);
return result;
【问题讨论】:
看来你想要std::function<HRESULT(ASValue::Array, ASValue&)>
。
@Jarod42 不,因为我已经拥有以下功能:SomethingHappened、SomethingElseHappened 等等,它们有不同的声明
你需要一个包装器来创建std::function
,不是吗?
什么是ASValue
?某种std::variant<int, string, double, ASValue::Array, ...>
?
我不熟悉 std::variant。虽然 ASValue 保存基本类型数据。请参阅此链接:github.com/cpzhang/bud/blob/…
【参考方案1】:
你可以实现类似的东西。
包含 std::function<R(Args...)>
对象的对象映射(此处为 GenericCallback),使用 std::any
或 std::variant
进行类型擦除。
您需要小心调用函数回调的方式。
例如我必须给它一个 std::string("hello world")
而不是简单的 C 字符串,否则 std::any_cast
会抛出(因为 function<string(const char*)>
不是 function<string(string)>
)。
#include <algorithm>
#include <any>
#include <functional>
#include <iostream>
#include <string>
#include <map>
#include <memory>
struct Caller
virtual ~Caller() = default;
virtual std::any call(const std::vector<std::any>& args) = 0;
;
template<typename R, typename... A>
struct Caller_: Caller
template <size_t... Is>
auto make_tuple_impl(const std::vector<std::any>& anyArgs, std::index_sequence<Is...> )
return std::make_tuple(std::any_cast<std::decay_t<decltype(std::get<Is>(args))>>(anyArgs.at(Is))...);
template <size_t N>
auto make_tuple(const std::vector<std::any>& anyArgs)
return make_tuple_impl(anyArgs, std::make_index_sequence<N> );
std::any call(const std::vector<std::any>& anyArgs) override
args = make_tuple<sizeof...(A)>(anyArgs);
ret = std::apply(func, args);
return ret;
;
Caller_(std::function<R(A...)>& func_)
: func(func_)
std::function<R(A...)>& func;
std::tuple<A...> args;
R ret;
;
struct GenericCallback
template <class R, class... A>
GenericCallback& operator=(std::function<R(A...)>&& func_)
func = std::move(func_);
caller = std::make_unique<Caller_<R, A...>>(std::any_cast<std::function<R(A...)>&>(func));
return *this;
template <class Func>
GenericCallback& operator=(Func&& func_)
return *this = std::function(std::forward<Func>(func_));
std::any callAny(const std::vector<std::any>& args)
return caller->call(args);
template <class R, class... Args>
R call(Args&&... args)
auto& f = std::any_cast<std::function<R(Args...)>&>(func);
return f(std::forward<Args>(args)...);
std::any func;
std::unique_ptr<Caller> caller;
;
using namespace std;
//Global functions
int sub(int a, int b) return a - b;
std::function mul = [](int a, int b) return a*b;;
std::string sortString(std::string str)
std::sort(str.begin(), str.end());
return str;
int main()
std::map<std::string, GenericCallback> callbacks;
// Adding our callbacks
callbacks["add"] = [](int a, int b) return a + b; ;
callbacks["sub"] = sub;
callbacks["mul"] = std::move(mul);
callbacks["sortStr"] = sortString;
// Calling them (hardcoded params)
std::cout << callbacks["add"].call<int>(2, 3) << std::endl;
std::cout << callbacks["sub"].call<int>(4, 2) << std::endl;
std::cout << callbacks["mul"].call<int>(5, 6) << std::endl;
std::cout << callbacks["sortStr"].call<std::string>(std::string("hello world")) << std::endl;
// Calling "add" (vector of any params)
std::vector<std::any> args = 1, 2 ;
std::any result = callbacks["add"].callAny(args);
std::cout << "result=" << std::any_cast<int>(result) << std::endl;
return 0;
https://godbolt.org/z/h63job
【讨论】:
但是在您的调用中,您正在硬编码它的接口回调[“add”].callargs
后面的类型是什么(这意味着您打算将其设为vector<any>
或类似的类型?)那么将会有一些额外的工作在你的论据包上做,是的。就像使用 std::holds_alternative<T>(anyVar)
检查它们的类型,使用 std::anyCast<T&>(anyVar)
强制转换它们并将它们传递给您注册的函数一样。
我在编译时确实知道 arg 背后的类型,但 callbacks[str].call 的使用应该以与 sortStr 和任何其他函数签名相同的方式完成......看看此代码:github.com/cpzhang/bud/blob/…
看起来他们所有的回调签名都是一样的:std::any func(std::vector<std::any> args, std::any)
,或者与其变体类型ASValue
的等价物。这使他们的工作变得更加容易,但这不是您在原始问题中所问的。你想注册void SomethingHappened(int a, int b) print a + b;
之类的东西。
请注意 std::any
/std::variant
是 C++17,而 OP 标签是 C++11。 (所以必须使用 boost::any
代替或等效)。【参考方案2】:
您可能需要一个围绕 std::function
的包装器,例如:
template <typename T> struct Tag;
// Convert ASValue to expected type,
// Possibly throw for invalid arguments.
bool Convert(Tag<Bool>, AsValue val) return (Boolean)val;
int Convert(Tag<int>, AsValue val) return (Number)val;
// ...
struct Callback
private:
template <std::size_t ... Is, typename Ret, typename ... Ts>
static Ret call_impl(Ret(* func)(Ts...), std::index_sequence<Is...>)
if (arr.size() != sizeof...(Is)) throw std::invalid_argument;
return func(Convert(tag<Ts>, arr[Is])...);
public:
template <typename Ret, typename ... Ts>
Callback(Ret(* func)(Ts...)) : Call[func](ASValue::Array arr, ASValue& ret)
try
ret = Callback::call_impl(func, std::make_index_sequence<sizeof(...(Ts)>());
return S_OK;
catch (...)
return E_INVALIDARG;
std::function<HRESULT(ASValue::Array, ASValue&)> Call;
;
std::index_sequence
是 C++14,但您可能会在 SO 上找到实现。
【讨论】:
以上是关于C++ 通用回调实现的主要内容,如果未能解决你的问题,请参考以下文章