如何使用仿函数和 operator() 实现委托

How to implement delegates using functors and operator()

我一直在尝试遵循以下一些建议:

https://softwareengineering.stackexchange.com/a/213635/46534

https://en.wikipedia.org/wiki/Function_object

但是很难让它编译。

我有一个委托定义:

struct SomeDelegate {
    void operator()(SomeType *data) {
        //do some stuff with data
    }
};

然后是接受函数指针的成员函数:

void DoSomethingThatCallsback(void(*callback)(SomeType *) ) {
    callback(ptrToSomeType);
}

然后当我尝试按如下方式使用此成员函数时:

foo.DoSomethingThatCallsback(SomeDelegate());

我得到编译错误:

cannot convert argument 1 from 'SomeDelegate' to 'void (__cdecl *)(Core::SomeType *)'

所有 我一直在阅读的示例表明这是可能的。

我试过使用这样的模板:

template <typename Callback>
void DoSomethingThatCallsback(Callback callback)

但是我得到了类似的错误。

我最终正在寻找一种更面向对象的方法来求助于函数指针或迁移到 C++11。另外,无法使用STL。

更新了更多上下文

struct MapOpenedDelegate {
public:

    void operator()(Map *openedMap) {

    }
};

class MapReader {
public:

    template <typename Callback>
    void RegisterMapOpenedCallback(Callback &callback) {
        _callbacks.Add(callback);
    }

private:
    Rise::List<void(*)(Map *)> _callbacks;
};
...
mapReader.RegisterMapOpenedCallback(MapOpenedDelegate());

第一种方法行不通,因为您在向函数传递 SomeDelegate 实例时告诉函数接受函数指针。

模板方法确实可行,具体取决于模板的主体。

可用的模板示例:

template <typename Callback>
void DoSomethingThatCallsback(Callback& callback) {
  callback(ptrToSomeType);
}

请注意,鉴于 ptrToSomeType 的类型为 SomeType*,此方法有效。并且 Callback 类型等于 SomeDelegate 类型,就像您在问题中定义的那样,或者提供 operator() 接受 SomeType* 作为其唯一参数的任何其他类型.

更新

考虑到您现在提供给我的内容,我认为没有理由在此处使用模板。

首先,在我之前的回答中,我并不知道这些回调实例要存储在列表中。这彻底改变了局面。

我在下面的问题解决方案中假设 RegisterMapOpenedCallback 的调用者拥有回调实例的所有权。调用者通过引用传递它们,MapReader 只存储指向它们的指针。

struct MapOpenedDelegate {
public:

    void operator()(Map *openedMap) {

    }
};

class MapReader {
public:

    void RegisterMapOpenedCallback(MapOpenedDelegate& callback) {
        _callbacks.Add(&callback);
    }

private:
    Rise::List<MapOpenedDelegate*> _callbacks;
};

为了提供一个非常清晰的解决方案,我仍然缺少上下文。除了存储一些回调实例之外,我不太确定你想要实现什么。不过,我希望以上内容能对您有所帮助。

在我看来 std::function 可以解决您所有的问题:

A std::function<ReturnType(Parameters)> 能够封装任何行为类似于具有参数 Parameters 和 return 类型 ReturnType 的函数,尤其是函子、lambda 和函数指针。

因此用 std::function 替换函数指针应该可以解决问题。

编辑:如果您不被允许使用完整的标准库(STL != 标准库),您可能必须使用像

这样的薄包装器自己实现该功能
struct MyDelegate {
    virtual operator()(...);
}

然后使用动态多态性为所有可能的可调用对象实现它:

template <typename Callable>
struct MyDelegateImpl : MyDelegate {
    Callable m_f;
    virtual operator()(...) { m_f(...); }
}

请注意,这只是一个粗略的草图,因此赋值等可能会有更多问题。确保将代表存储在 std::unique_ptr 中,以便您的对象 don't get sliced

据我从你的问题和对答案的评论中了解到,你想创建一个容器来擦除侦听器的类型,并在需要时通过回调方法调用它们。当然,您出于未知原因不想使用标准库,因此 lambda 和 std::function 超出了此处的范围。

到目前为止,还不错。它遵循基于您的示例代码的最小工作片段:

#include<vector>

struct Map {
    // You haven't provided a definition for this class
};

struct ADelegate {
    void operator()(Map *) {
        // ...
    }
};

struct AnotherDelegate {
    void operator()(Map *) {
        // ...
    }
};

class MapReader {
    using fn_type = void(*)(void*, Map *);

    struct Callback {
        void *ptr;
        fn_type fn;
    };

    template<typename T>
    static void proto(void *ptr, Map *map) {
        (*static_cast<T *>(ptr))(map);
    }

public:
    template <typename T>
    void attach(T *ptr) {
        callbacks.push_back({ ptr, &proto<T> });
    }

    void operator()() {
        Map map;

        for(auto &&cb: callbacks) {
            cb.fn(cb.ptr, &map);
        }
    }

private:
    // I used a vector for I don't know how Rise::List is implemented
    std::vector<Callback> callbacks;
};

int main() {
    MapReader mr;

    ADelegate d1;
    AnotherDelegate d2;

    mr.attach(&d1);
    mr.attach(&d2);

    mr();
}

我使用了 std::vector,因为我没有 Rise::List 的实现。我相信您可以轻松地将其替换为您自己的列表类型。
请参阅上面的代码段和 wandbox 上的 运行。

让我们看看它是如何工作的。
几乎所有事情都发生在 MapReader class 内。如果你想存储属于不同类型的回调,一个可能的解决方案是类型擦除原始类型并将它们放在 void * 框中。然后,您可以在需要时通过添加额外的间接层(静态模板函数 proto)取回类型并进行正确的调用。

如您所见,它正常工作,您可以将不同的类型附加到 MapReader(请注意,您必须保证侦听器的生命周期超过发射器之一)。您为此付出的代价是通过中间函数的跳转,该函数在调用之前将 void * 转换为正确的类型。