c – 从指针到成员的映射
作者:互联网
(注意:如果这感觉像是X-Y问题,请在分隔符下面滚动以了解我是如何得出这个问题的)
我正在寻找一种方法来存储指向成员函数(不同类型)的指针,并比较它们是否相等.我需要存储从指针到成员函数到任意对象的映射,然后搜索此映射.它不必是关联容器,线性搜索就可以了.另请注意,指针仅用作映射键,它们永远不会被解除引用.
我目前的方法是:在构建映射时,我将传入的指向成员的指针重新解释为一个众所周知的类型(void(MyClass :: *)())并将其插入到映射中.像这样的东西(为简洁省略了错误检查):
template <class R, class... A)
void CallChecker::insert(R (MyClass::*key)(A...), Object value)
{
mapping.push_back(std::make_pair(reinterpret_cast<void (MyClass::*)()>(key), value));
}
然后在查找时,我执行相同的强制转换并按等式搜索:
template <class R, class... A)
Object CallChecker::retrieve(R (MyClass::*key)(A...)) const
{
auto k = reinterpret_cast<void (MyClass::*)()>(key);
auto it = std::find_if(begin(mapping), end(mapping), [k](auto x) { return x.first == k; });
return it->second;
}
但是,我不确定这总是有效.虽然我认为它不能产生假阴性(两个相等的指针被报告为不同的),但我担心它可能会产生假阴性(原来不同类型的两个指针在投射到“普通”类型时可以比较相等).所以我的问题是,是这样吗?或者我可以安全地使用这样的比较?
我知道我在这里危险地靠近UB地区.但是,我不介意使用行为的解决方案,该行为不是由标准定义的,但已知在gcc和MSVC(我的两个目标编译器)中工作.
所以,问题是:普通类型的比较安全吗?或者我会更好地将存储的指针转换为传入类型进行比较(如下所示):
template <class R, class... A)
Object CallChecker::retrieve(R (MyClass::*key)(A...)) const
{
auto it = std::find_if(begin(mapping), end(mapping), [key](auto x) { return reinterpret_cast<R (MyClass::*)(A...)>(x.first) == key; });
return it->second;
}
或者这些都不会在实践中发挥作用而且我运气不好?
我对指向成员的指针的上述属性感兴趣,既考虑到我的实际任务,又加深了对语言的理解.尽管如此,出于一种完整感(如果有人知道更好的方式),这就是我对原始问题的看法.
我正在构建一个实用程序框架,用于帮助对Qt4信号进行单元测试(测试是否发出了正确的信号).我的想法是创建一个类CallChecker,它将存储槽的验证器(包装std :: function对象),并能够运行它们.然后测试将创建一个派生自此的类;该类将定义将运行相应验证器的槽.这是一个使用的想法(简化):
class MyTester : public QObject, public CallChecker
{
Q_OBJECT
public slots:
void slot1(int i, char c) { CallChecker::checkCall(&MyTester::slot1, i, c); }
void slot2(bool b) { CallChecker::checkCall(&MyTester::slot2, b); }
};
void testRunner()
{
MyTester t;
connectToTestedSignals(t);
t.addCheck(&MyTester::slot1, [](int i, char c) { return i == 7; });
}
我有一个working implementation(在ideone上的gcc),其中CallChecker使用std :: vector对,其中指针成员转换为通用函数类型.在使用编译器标志(/ vmg)之后,我也在MSVC中工作了.
如果您可以建议一个更好的解决方案,而不是通过指向成员的查找,我会很高兴听到它.我的目标是在实现测试槽的类中易于使用:我真的希望这些插槽是简单的单行.使用插槽签名的文本表示(Qt在内部使用的内容)实际上并不是一种选择,因为它太容易受到错别字的影响.
解决方法:
如果您首先检查双方的typeid是否相同,则可以使用类型擦除函数将两侧转换为相同类型并在该类型中进行比较. (这是标准所必需的,因为即使您可以通过一种众所周知的类型往返,也不能保证该类型的比较与原始类型的比较具有相同的行为.)这里是草图:
struct any_pmf_compare {
std::type_index ti;
void (any_pmf_compare::*pmf)();
bool (*comp)(const any_pmf_compare &, const any_pmf_compare &);
template<typename F>
any_pmf_compare(F f):
ti(typeid(F)),
pmf(reinterpret_cast<void (any_pmf_compare::*)()>(f)),
comp([](const any_pmf_compare &self, const any_pmf_compare &other) {
return reinterpret_cast<F>(self.pmf) == reinterpret_cast<F>(other.pmf);
})
{
}
};
bool operator==(const any_pmf_compare &lhs, const any_pmf_compare &rhs) {
return lhs.ti == rhs.ti && lhs.comp(lhs, rhs);
}
标签:pointer-to-member,c,c11 来源: https://codeday.me/bug/20190825/1716955.html