从指针到成员的映射 [英] Mapping from pointers-to-member

查看:205
本文介绍了从指针到成员的映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(注意:如果这种感觉像是XY问题,请滚动到分隔符下方以查看我是如何解决这个问题的)

我正在寻找一种方式来存储指针到成员函数(不同类型),并比较它们的平等。我需要存储从指针到成员函数到任意对象的映射,然后搜索此映射。它不必是一个关联容器,线性搜索很好。另请注意,指针只用作映射键,它们从不取消引用。

I am looking for a way to store pointers-to-member-functions (of different types) and compare them for equality. I need to store a mapping from pointer-to-member-function to an arbitrary object, and then search this mapping. It doesn't have to be an associative container, a linear search is fine. Also note that the pointers serve as mapping keys only, they are never dereferenced.

我当前的方法是:当构建映射时,我 reinterpret_cast 到一个已知类型( void(MyClass :: *)())的入局指针到成员,并将其插入映射。这样的东西(错误检查为简洁省略):

My current approach is this: when building the mapping, I reinterpret_cast the incoming pointer-to-member to one well-known type (void (MyClass::*)()) and insert it into the mapping. Something like this (error checking omitted for brevity):

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

然后在查找时,我执行相同的转换和搜索: p>

Then on lookup, I perform the same cast and search by equality:

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

但是,我不确定这将总是工作。虽然我相信它不能产生假阴性(两个相等的指针被报告为不同),我恐怕它可能产生假阴性(两个指针,原来是不同类型可以比较相等当投射到常见类型)。所以我的问题是,是的情况?或者我可以安全地使用这样的比较吗?

However, I am not sure that this will always work. While I believe it cannot produce false negatives (two equal pointers being reported as distinct), I am afraid it might produce false negatives (two pointers which were originally of different type could compare equal when cast to the "common" type). So my question is, is that the case? Or am I safe in using comparisons like this?

我知道我在这里危险地靠近UB地区。然而,我不介意一个解决方案使用的行为,没有定义的标准,但已知的工作在gcc和MSVC(我的两个目标编译器)。

I know I am treading dangerously close to UB territory here. However, I don't mind a solution which works using behaviour which is not defined by the standard, but known to work in gcc and MSVC (my two target compilers).

所以,问题是:是一个通用类型安全的比较?或者我会更好地将存储的指针转换为传入类型进行比较(像这样):

So, the question is: is the comparison in a common type safe? Or would I be better off casting the stored pointer to the incoming type for the comparison (like this):

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

或者这两者都不能在实践中工作,

Or will neither of these work in practice and I'm out of luck?

我对指针到成员的上述属性感兴趣,无论是根据我的实际任务,我对语言的理解。仍然,从完整性的意义(如果有人知道更好的方式),这里是我如何到达原来的问题。

I am interested in the above properties of pointers-to-member, both in light of my actual task and to deepen my understanding of the language. Still, out of a sense of completeness (and in case somebody knows a better way), here is how I arrived at the original question.

我正在建立一个实用程序用于帮助单元测试 Qt4 信号(测试发出正确的信号)的框架。我的想法是创建一个类 CallChecker 将存储验证器(包括 std :: function 对象)能够运行它们。然后测试将创建一个从中派生的类;该类将定义将运行相应验证器的槽。这里有一个使用的概念(简化):

I'm building a utility framework for helping unit-testing Qt4 signals (testing that the proper signals are emitted). My idea was to create a class CallChecker that would store validators (wrapped std::function objects) for slots, and be able to run them. The test would then create a class derived from this; that class would define slots which would run the corresponding validators. Here's an idea of usage (simplified):

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

我有一个工作实现(gcc on ideone)其中 CallChecker 使用 std :: vector ,其中指针to-member转换为公共函数类型。在编译器标志( / vmg )之后,我也在MSVC中工作。

I have a working implementation (gcc on ideone) where CallChecker uses a std::vector of pairs, with the pointers-to-member cast to a common function type. After some fiddling with compiler flags (/vmg), I got this working in MSVC as well.

可以建议比通过指针查找成员更好的解决方案,我会很高兴听到它。我的目标是易于使用的类实现测试插槽:我真的希望这些插槽是简单的一线。

If you can suggest a better solution than lookup by pointer to member, I'll be happy to hear it. My goal is ease of use in the class implementing the test slots: I really want these slots to be simple one-liners. Using a textual representation of the slot signature (what Qt uses internally) is not really an option, as it's too susceptible to typos.

推荐答案

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

这篇关于从指针到成员的映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆