2017-07-18 34 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 *轉換爲正確的類型。

相關問題