问题描述
我有一个代码,它以 XML 的形式从 Flash Player 获取消息,将它们解析为函数和参数,并为该函数调用注册的回调。 我想替换的这段代码是做得很好的(几乎)通用回调机制: code for the generic callback implementation of flashSDK (ASInterface.inl)。
问题在于此代码是为闪存编写的,我想更换闪存并使用其他具有相同界面的服务。这种回调机制是否有任何标准实现(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
进行包装,例如:
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 上找到实现。
你可以实现类似的东西。
对象映射(此处为 GenericCallback)包含用 std::function<R(Args...)>
或 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;
}