C++ 通用回调实现

时间:2021-02-23 08:45:40

标签: c++ c++11 templates flash boost

我有一个代码,它以 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;
    }
}

2 个答案:

答案 0 :(得分: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 上找到实现。

答案 1 :(得分:1)

你可以实现类似的东西。
对象映射(此处为 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;
}

https://godbolt.org/z/h63job

相关问题