2017-07-18 52 views
1

我一直努力遵循一些这方面的建议代表:如何实现使用仿函数和运算符()

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) 

但我得到一个类似的错误。

我最终寻找更多的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); 
} 

注意这考虑到ptrToSomeTypeSomeType*类型。而Callback类型等于SomeDelegate类型,就像您在问题中定义它的类型,或者提供接受SomeType*作为其唯一参数的operator()的任何其他类型。

更新

我觉得有没有真正的理由在这里使用的模板,因为你现在跟我提供。

首先,在我之前的回答中,我没有意识到这些回调实例要存储在一个列表中。这完全改变了这种情况。

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

struct MapOpenedDelegate { 
public: 

    void operator()(Map *openedMap) { 

    } 
}; 

class MapReader { 
public: 

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

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

为了提供一个完美清晰的解决方案,我仍然错过了上下文。我不太清楚除了存储一些回调实例以外,你想实现什么。我确实希望以上方式可以帮助你。

+0

当我完全按照您的概述添加模板规范时,我会得到相同的错误。我必须做一些不寻常的事情。 –

+0

有没有机会发布更多的代码,更多的上下文?我相信我们在这里错过了一些东西。 – arjen1988

+0

Thanks @Arjen我用更多的具体代码更新了这个问题。注意,所有这些都包含在一个通用名称空间中,如果这很重要的话。 –

0

它看起来对我来说,std::function可以解决所有你的问题:

一个std::function<ReturnType(Parameters)>能够封装任何表现如同一个带参数Parameters功能和返回ReturnType类型,尤其是仿函数,lambda表达式和函数指针。

因此用这样一个std ::函数替换函数指针应该能够做到这一点。

编辑:(!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

+0

我忘了提及STL目前不是这个项目的一个选项。 –

+0

我添加了一些实现细节,如果你想自己做;)请注意,这可能不会工作没有动态多态性。 –

0

据我从你的问题和意见的答案了解,您希望创建一个擦除的侦听器类型的容器,并呼吁他们通过回调方法需要时。当然,你不想因为不明原因而使用标准库,所以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类。为了存储属于不同类型的回调,可能的解决方案是键入 - 擦除原始类型并将其放入void *框中。然后,您可以通过添加额外的间接层(静态模板函数proto)在需要时返回类型,并执行正确的调用。

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