我如何散列(std::tr1::hash 或 boost::hash)一个 c++ 指向成员函数的指针?
例子:
我有几个 bool (Class::*functionPointer)() (不是静态的)指向类 Class 的几个不同方法,我需要散列那些指向成员函数的指针。
我怎样才能做到这一点?
另外,我如何比较(std::less)那些成员函数指针,以便将它们存储在 std::set 中?
我如何散列(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;
}
我无法按照先前答案中的描述转换指针(在 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
如果您的成员函数指针是唯一的,在大多数情况下对于基于回调的订阅都是如此,那么您可以使用带有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>
有一个成员函数指针。