我一直在努力遵循这些建议:
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());
答案 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 *
强制转换为正确的类型。