簡體   English   中英

如何散列和比較指向成員函數的指針?

[英]How to hash and compare a pointer-to-member-function?

我如何散列(std::tr1::hash 或 boost::hash)一個 C++ 成員函數指針?

例子:

我有幾個 bool (Class::*functionPointer)() (非靜態)指向類 Class 的幾個不同方法,我需要散列那些指向成員函數的指針。

我怎樣才能做到這一點?

另外我如何比較(std::less)那些成員函數指針,以便我可以將它們存儲在 std::set 中?

所有 C++ 對象,包括指向成員函數的指針,都在內存中表示為字符數組。 所以你可以試試:

bool (Class::*fn_ptr)() = &Class::whatever;
const char *ptrptr = static_cast<const char*>(static_cast<const void*>(&fn_ptr));

現在將ptrptr視為指向一個(sizeof(bool (Class::*)()))字節數組,並散列或比較這些字節。 如果您願意,可以使用unsigned char而不是char

這保證沒有誤報——在 C++03 中,指向成員函數的指針是 POD,這意味着它們可以使用 memcpy 進行復制。 這意味着如果具有相同的字節對字節值,則它們是相同的。

問題是成員函數指針的存儲表示可能包括不參與值的位 - 因此對於指向同一成員函數的不同指針,它們不一定相同。 或者編譯器可能出於某種晦澀的原因,有不止一種指向同一類的同一函數的方法,這些方法在字節上是不相等的。 無論哪種方式,你都可能得到假陰性。 您必須研究成員函數指針在您的實現中實際是如何工作的。 它必須以某種方式為成員函數指針實現operator== ,如果您能找出方法,那么您可能會找出順序和散列函數。

這可能很困難:成員函數指針很笨拙,並且根據指向的函數類型(虛擬的、繼承的),存儲可能包含不同數量的非參與“松弛空間”。 因此,您可能必須與編譯器的實現細節進行大量交互。 本文可能會幫助您入門: http : //www.codeproject.com/KB/cpp/FastDelegate.aspx

更簡潔的替代方法可能是對數組進行線性搜索,以便“規范化”所有函數指針,然后根據數組中該函數指針的“規范”實例的位置進行比較和散列。 取決於你的性能要求是什么。 並且即使有要求,類(及其派生類)是否具有如此多的函數,以至於線性搜索需要那么長時間?

typedef bool (Class::*func)();
vector<func> canon;

size_t getIndexOf(func fn_ptr) {
    vector<func>::iterator it = find(canon.begin(), canon.end(), fn_ptr);
    if (it != canon.end()) return it - canon.begin();
    canon.push_back(func);
    return canon.size() - 1;
}

如果您的成員函數指針是唯一的,這在大多數基於回調的訂閱的情況下都是正確的,那么您可以使用帶有type_index的勾,該唯一性由您程序中的類型(即Class::Method )的唯一性保證,並且適合存放在unordered_map ,即

struct MyEvent {

    using fn_t = std::function<void(MyEvent &)>;
    using map_t = std::unordered_map<std::type_index, fn_t>;


    template <typename Handler>
    void subscribe(Object& obj, Handler&& handler) {
        fn_t fn = [&, handler = std::move(handler)](MyEvent& event) {
            (obj.*handler)(event);
        }
        std::type_index index = typeid(Handler);
        subscribers.emplace(std::move(index), std::move(fn));
    }

    void fire() {
        for(auto& pair: subscribers) {
            auto& fn = pair.second;
            fn(*this);
        }
    }

    map_t subscribers;
}

以及訂閱和火災事件示例:

MyEvent event;
MyObject obj = ...;
event.subscribe(obj, &MyObject::on_event );
...
event.fire();

所以,上面的例子給你類/方法唯一性,如果你需要對象/方法唯一性,那么你應該有一個結構,它提供組合散列,假設有std::hash<MyObject>並且已經有std::hash<std::type_index>用於成員函數指針。

我無法像之前的答案中描述的那樣轉換指針(在 Microsoft 編譯器 2010 中),但這對我有用:

static string fmptostr(int atype::*opt)
  {
      char buf[sizeof(opt)];
      memcpy(&buf,&opt,sizeof(opt));
      return string(buf,sizeof(opt));
  }

關於指針的按位標識,它可以是按位的,因此看起來是否使用了適當的編譯器開關。 至少對於使用 #pragma pointers_to_members 和 switch.../vmg 的 Microsoft 編譯器來說,這是正確的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM