具有不同参数类型和存储类型的回调

Posted

技术标签:

【中文标题】具有不同参数类型和存储类型的回调【英文标题】:Callbacks with different parameter types and storing the type 【发布时间】:2015-10-22 21:31:45 【问题描述】:

我有一个通信库,它应该根据接收到的命令类型调用回调(或处理程序)。我将处理程序存储在std::map 中(命令作为键,处理程序作为值)。

这些处理程序需要有不同的参数类型。处理程序参数始终只有一个,该参数继承自BaseType。所以我有以下类型定义和处理程序:

class Comm 
    typedef std::function<void(BaseType*)> Handler;
    typedef std::map<Command, Handler> Handlers;

    void HandleHandshake(IntType*); // IntType inherits from BaseType
;

不幸的是,我无法将处理程序 HandleHandshake 存储在 Handlers 类型中。我只能存储具有BaseType* 类型参数的处理程序。我不能使用std::function&lt;void()&gt; 绑定不同的参数,因为我需要访问它的argument_type(否则在调用处理程序时我将无法传递正确的数据类型。)。

有什么方法可以实现我想要实现的目标吗? (存储不同参数类型的处理程序,并存储类型以供进一步使用。)

显示调用处理程序的示例:

// I parsed a command. Handler of this command should take IntType as a parameter.
m_Handlers[cmd](IntType(some_data));
// But, I don't really know that it is IntType, I only have my map of handlers.
// I want something like this:
m_Handlers[cmd](m_Handlers[cmd]::argument_type(some_data));

【问题讨论】:

如果我错了,请纠正我。 1. 当您被命令调用特定命令时,您会收到some_datacmd。 2. some_data 可以是不同的类型(例如 int、float)。 3. 要将其传递给您的“调用命令”方法,您需要使用模板。 顺便说一句。您可以随时使用非常不安全的evilvoid*static_castsome_data,而无需添加一层包装器。附言在我说没有更好的方法之前不要这样做。 系统需要支持多少种不同的参数类型? 5? 10? 100?未知号码(即可以由图书馆的用户扩展)? 系统需要支持多少种不同的消息? 根本问题是您的处理程序映射包含 X 处理程序。您将它们向上转换以适合 function&lt;void(Basetype*)&gt; 签名,这会删除您需要在正确参数中传递的信息。将正确的类型和您的论点联系在一起的唯一方法是您暗示它们匹配。要解决这个问题,要么创建一个 lambda,将 dynamic_casts 转换为正确的类型并继续传递,然后将 static_cast 传递给传递,或者不键入 erase 以开始(并在模板存储中匹配它们)。 【参考方案1】:

您始终可以存储一个执行static_cast 的 lambda。例如:

template <typename T=BaseType, typename F>
void store(Command c, F function)

     Handlers[c] = [function](BaseType* obj)
         function(static_cast<T*>(obj));
     );

【讨论】:

我不认为可以根据需要使用它。我需要在调用处理程序时知道类型,以便为它创建它。 @TomášOndruš 所以你能做什么? 调用意味着请求。阅读它调用处理程序 @TomášOndruš 我还是不明白你想要什么。你能在问题中添加一些例子来澄清吗? 添加了调用处理程序的示例。【参考方案2】:

我以前做过类似的事情。这就是我会做的:

typedef std::function<void(BaseType*)> Handler;
typedef std::map<Command, Handler> Handlers;

然后我会让每个处理程序获取一个指向 BaseType* 的指针,以便我可以将它存储在我的地图中。

void MyIntHandler(BaseType* b)

    // we know what kind of information should be passed into this method
    // so we can just cast to the correct type
    auto data = static_cast<IntType*>(b);

    // do something with data


void MyStringHandler(BaseType* b)

    auto data = static_cast<StringType*>(b);
    // ...

您不能像在 C# 中那样存储类型对象,然后使用它来创建该类型的对象。您可以使用typeinfo 在运行时获取有关特定类型的一些信息。

【讨论】:

所以我不可能有确切类型的 typedef 吗?让我们说一下我的 std::function 的一些包装器。 @TomášOndruš 他是对的,他是错的。如果您知道如何使用 C++,它是一种非常强大的语言。尽管类型不是 C++ 中的对象并且您无法存储它,但您可以通过巧妙地使用模板来实现非常相似的行为。

以上是关于具有不同参数类型和存储类型的回调的主要内容,如果未能解决你的问题,请参考以下文章

具有变量计数和类型参数的函数指针?

14_函数类型和回调函数

respondsToSelector 在选择器中具有不同的参数类型

映射到具有不同数量参数和不同数据类型的函数指针

将表值参数传递给具有不同字段数的存储过程

具有相同名称但参数和返回类型不同的虚拟函数[重复]