【问题标题】:Container of Functions with different Parameters具有不同参数的函数容器
【发布时间】:2012-12-01 11:28:21
【问题描述】:

有没有办法在向量中存储具有不同参数的 void 函数?参数的数量总是一个,只是类型不同。参数类型可以是int 等默认类型,example* 等指向我自己的对象的指针或任何其他类型。

我现在所做的是使用带有 void 指针作为参数的函数向量。所以我可以通过一切。但我想摆脱所有功能中的回投。

unordered_map<string, function<void(void*)> > List;

void Callback(string Name, function<void(void*)> Function)
{
    List[Name].push_back(&Function);
}

Callback("event", [](void* x){
    int value = *(int*)x;
    cout << value << endl;
});

这里有一个例子来说明我想要什么。请注意我喜欢的模板语法。因此,我需要将所有功能存储在一个容器中。

vector<function<void(...)> > List; // need something other than a std vector

template <typename T>
void Callback(string Name, function<void(T)> Function)
{
    List[Name].push_back(&Function);
}

Callback<int>([](int x){
    cout << x << endl;
});

此应用程序与性能相关,因为它是实时渲染引擎的重要组成部分。

编辑:我解决了存储没有参数的函数的问题,所以这不再是问题的一部分,是什么让问题更加清晰和直接。

【问题讨论】:

  • 在调用现场,你怎么知道使用哪种参数类型?
  • 现在,它也是一个空指针。但我也想将其模板化。
  • 有趣的信息是您如何知道使用哪种类型,而不是您当前如何表示它。假设您有vector&lt;Foo&gt; v,其中Foo 是正确的类型。你想打电话给v[3](x)。你想要x是什么类型的?
  • 所有回调函数都有一个约束条件,即没有参数或与事件提供的参数相同。例如,事件WindowResize 为新的高度和宽度提供Vector2i。所有注册的回调必须没有或只有一个Vector2i 参数。但正如我所说,我的实际实现使用 void 指针,所以只有程序员知道类型。
  • 因此程序员知道WindowResize 具有Vector2i 等类型的数据,并且他希望仅使用该语言的静态类型安全结构来表达此知识。为什么他将所有可能类型的所有回调集中在同一个向量中?通过这样做,他丢掉了知识。为什么不将接受int 的回调与接受Vector2i 等不同类型的不同变量的回调分开?然后每个事件类型都会知道在哪里可以找到相关的回调。

标签: c++ function templates pointers containers


【解决方案1】:

如果可以传递给函数的参数类型有限,那么一种选择是使用类似boost::variant

typedef boost::variant<
    std::function<void()>,
    std::function<void(int)>,
    std::function<void(long)>,
    std::function<void(std::string const&)>
> my_functions;
typedef std::vector<my_functions> functions_list;

然后你可以将你的回调直接插入到容器中。

【讨论】:

  • 对不起,我需要传递孔类,而不仅仅是默认类型。我针对这个事实更新了我的问题。
  • 对不起,我没有得到您的评论,请您解释一下!
  • 你的介绍句不适用。参数类型不限int,float,string,...我需要传递自己类的对象。
  • 我说如果类型列表有限,你可以使用它,但不限于这个集合,这只是一个示例,你可以用任何其他类型替换这个类型,你的变体可能有 50 种不同类型。请阅读boost::variant的文档。
  • 所以你可以使用boost::any而不是void*,使用它你可以有安全的演员,并确保你正在调用具有正确参数的函数
【解决方案2】:

正如 n.m 在评论中指出的那样,问题在于您如何使用 价值。形式上,C++ 允许您将任何指针转换为 (非成员)函数到 void (*)(void) 并返回到它的 原始类型,不损失价值—void (*)(void)可以 被认为是指向函数的指针的一种void*。和 实际上,此类转换的运行时间成本为零。但 为了使用该功能,在某些时候,您必须知道 原始类型,以便将指针转换回原来的类型。

您没有给出您的用例,但通常情况涉及 回调,其中回调的注册有void (*)( void* )(或void (*)( void const* )),回调 将void* 转换为正确的类型,并调用成员 对它起作用。在这种情况下,使用void* 作为泛型 论据是正确的(并且可能是唯一的)解决方案。

当然,这是 C 解决方案,通常应该只是 当使用回调的接口是使用定义的时使用 C API(例如pthread_create 等函数)。在 C++ 中, 解决方案是注册对象,这些对象派生自相同的 抽象基类,并实现特定的纯虚 功能。

【讨论】:

  • 事件系统内部可以使用 void 指针。但我想要一种摆脱转换回参数的方法。因此我想要模板。
  • 嗯,将函数包装在模板中是相当容易的。问题是,通常,如果您使用这个习语,那是因为接口是使用 C API 设计的(否则会有更好的解决方案)。要将指向函数的指针传递给 C API,函数必须是 extern "C",模板不能是 extern "C"
  • 你能举个例子吗?顺便说一句,我不依赖任何 C API。
【解决方案3】:

我开发了一个基于空指针的事件系统,它现在可以工作了。它使用模板来传递和接收数据。支持没有参数或任何类型的一个参数的函数。由于它使用 void 指针来存储回调函数,我想它与使用 boost 框架中的 any 类型的解决方案相比非常快。

#include <string>
#include <vector>
#include <unordered_map>
#include <functional> 
#include <memory>

using namespace std;

class ManagerEvent
{
    typedef unordered_map<string, unordered_map<int, vector<pair<void*, bool> > > > Events;
public:
    void Listen(string Name, function<void()> Function)
    {
        Listen(Name, 0, Function);
    }
    void Listen(string Name, int State, function<void()> Function)
    {
        List[Name][State].push_back(make_pair(new function<void()>(Function), false));
    }
    template <typename T>
    void Listen(string Name, function<void(T)> Function)
    {
        Listen<T>(Name, 0, Function);
    }
    template <typename T>
    void Listen(string Name, int State, function<void(T)> Function)
    {
        List[Name][State].push_back(make_pair(new function<void(T)>(Function), true));
    }
    void Fire(string Name)
    {
        Fire(Name, 0);
    }
    void Fire(string Name, int State)
    {
        auto Functions = List[Name][State];

        for (auto i = Functions.begin(); i != Functions.end(); ++i)
        {
            if(i->second) continue;
            else          (*(function<void()>*)(i->first))();
        }
    }
    void FireRange(string Name, int From, int To)
    {
        for(int i = From; i <= To; ++i) Fire(Name, i);
    }
    template <typename T>
    void Fire(string Name, T Data)
    {
        Fire(Name, 0, Data);
    }
    template <typename T>
    void Fire(string Name, int State, T Data)
    {
        auto Functions = List[Name][State];

        for (auto i = Functions.begin(); i != Functions.end(); ++i)
        {
            if(i->second) (*(function<void(T)>*)i->first)(Data);
            else          (*(function<void()>*)i->first)();
        }
    }
private:
    Events List;
};

这是我想到的,而且效果很好。但是,请随时提出改进建议或将代码用于您自己的项目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-18
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    • 2021-03-05
    • 2018-11-16
    • 1970-01-01
    相关资源
    最近更新 更多