9

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

例子:

我有几个 bool (Class::*functionPointer)() (不是静态的)指向类 Class 的几个不同方法,我需要散列那些指向成员函数的指针。

我怎样才能做到这一点?

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

4

3 回答 3

14

所有 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;
}
于 2009-08-25T14:25:58.090 回答
1

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

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

关于指针的按位标识,它可以是按位的,因此看起来是否使用了适当的编译器开关。至少这对于 Microsoft 编译器是正确的,例如使用 #pragma pointers_to_members 和 switch.../vmg

于 2011-09-08T14:14:28.683 回答
0

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

于 2019-05-22T18:54:00.580 回答