如何散列和比较指向成员函数的指针?

发布于 2024-08-02 15:22:54 字数 234 浏览 1 评论 0原文

我如何散列(std::tr1::hash 或 boost::hash)c++ 成员函数指针?

示例:

我有几个 bool (Class::*functionPointer)() (非静态)指向类 Class 的几个不同方法,我需要对这些指向成员函数的指针进行哈希处理。

我怎样才能做到这一点?

另外,我如何比较(std::less)这些成员函数指针,以便将它们存储在 std::set 中?

How can i hash (std::tr1::hash or boost::hash) a c++ pointer-to-member-function?

Example:

I have several bool (Class::*functionPointer)() (not static) that point to several diferent methods of the class Class and i need to hash those pointer-to-member-function.

How can i do that?

Also how can i compare (std::less) those member function pointers so i can store them in a std::set?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深爱不及久伴 2024-08-09 15:22:54

所有 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;
}

All C++ objects, including pointers to member functions, are represented in memory as an array of chars. So you could try:

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

Now treat ptrptr as pointing to an array of (sizeof(bool (Class::*)())) bytes, and hash or compare those bytes. You can use unsigned char instead of char if you prefer.

This guarantees no false positives - in C++03, pointers to member functions are POD, which means among other things that they can be copied using memcpy. This implies that if have the same byte-for-byte values, then they are the same.

The problem is that the storage representation of member function pointers could include bits which do not participate in the value - so they will not necessarily be the same for different pointers to the same member function. Or the compiler might, for some obscure reason, have more than one way of pointing to the same function of the same class, which are not byte-wise equal. Either way you can get false negatives. You'll have to look into how member function pointers actually work on your implementation. It must implement operator== for member function pointers somehow, and if you can find out how then you can probably figure out an order and a hash function.

That's potentially hard: member function pointers are awkward, and the storage is likely to include different amounts of non-participating "slack space" according to what kind of function is pointed to (virtual, inherited). So you'll probably have to interact quite significantly with your compiler's implementation details. This article might help get you started: http://www.codeproject.com/KB/cpp/FastDelegate.aspx

A cleaner alternative might be to do a linear search through an array in order to "canonicalise" all your function pointers, then compare and hash based on the position of the "canonical" instance of that function pointer in your array. Depends what your performance requirements are. And even if there are requirements, does the class (and its derived classes) have so many functions that the linear search will take that long?

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;
}
凡尘雨 2024-08-09 15:22:54

我无法转换指针(在 Microsoft 编译器 2010 中),如前面的答案中所述,但这对我有用:

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

关于指针的按位标识,它可以是按位的,因此看起来是否使用了适当的编译器开关。至少对于微软编译器来说是这样的
使用#pragmapointers_to_members
和一个开关.../vmg

I could not cast the pointer (in Microsoft compiler 2010)as described in previous answer but this works for me:

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

About bitwise identity of the pointer, it can be bitwise so it seems if appropriate compiler switches are used. At least this is true for Microsoft compiler E.g
using #pragma pointers_to_members
and a switch.../vmg

﹏雨一样淡蓝的深情 2024-08-09 15:22:54

如果您的成员函数指针是唯一的(在大多数情况下对于基于回调的订阅都是如此),那么您可以使用带有 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 并且已经有 std::hash 用于成员函数指针。

If your member function pointer is unique, which is true in most of cases for callback-based subscriptions, then you can use the tick with type_index, which uniqueness is guaranteed by uniqueness of type (i.e. Class::Method) in your program, and it is suitable to be stored in unordered_map, i.e.

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;
}

And the subscription and fire event example:

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

So, example above gives you class/method uniqueness, and if you need object/method uniqueness, then you should have an struct, which provides combined hash, assuming that there is std::hash<MyObject> and there is already std::hash<std::type_index> for a member function pointer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文