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

时间:2017-07-18 10:34:13

标签: c++ functor

我一直在努力遵循这些建议:

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

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

但努力让它编译。<​​/ p>

我有一个委托定义:

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

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

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

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

foo.DoSomethingThatCallsback(SomeDelegate());

我收到编译错误:

  

无法将参数1从'SomeDelegate'转换为'void(__ cdecl *)(Core :: SomeType *)'

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

我尝试过使用这样的模板:

template <typename Callback>
void DoSomethingThatCallsback(Callback callback)

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

我最终正在寻找更多的OOP方法来诉诸函数指针或转向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());

3 个答案:

答案 0 :(得分:0)

第一种方法不起作用,因为你告诉你的函数在传递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;
};

为了提供一个非常清晰的解决方案,我仍然错过了背景。除了存储一些回调实例之外,我不太确定你想要实现什么。我希望上述内容可以帮助你。

答案 1 :(得分:0)

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

std::function<ReturnType(Parameters)>能够封装任何行为类似函数的参数Parameters并返回类型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

答案 2 :(得分:0)

据我所知,从你的问题和对答案的评论中,你想创建一个容器来删除监听器的类型,并在需要时通过回调方法调用它们。当然,您不希望出于未知原因使用标准库,因此lambdas和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类中。如果您想要存储属于不同类型的回调,可能的解决方案是键入 - 擦除原始类型并将它们放在void *框中。然后,您可以在需要时通过添加额外的间接层(静态模板函数proto)来获取类型并执行正确的调用。

正如你所看到的,它只是工作,你可以将不同的类型附加到MapReader(请注意,你必须保证听众的生命周期克服了发射器之一)。您为此付出的代价是跳过一个中间函数,该函数在执行调用之前将void *强制转换为正确的类型。