Mapping from pointers-to-member

只谈情不闲聊 提交于 2019-12-04 13:13:49

If you first check that the typeid of both sides are the same, you can then use a type-erased function to cast both sides to the same type and compare in that type. (This is strictly necessary by the standard, as even if you can round-trip via a well-known type, there is no guarantee by the standard that comparisons in that type will have the same behaviour as comparisons in the original type.) Here's a sketch:

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

As I said in the comments, there is a way to unit test that a qt signal is emitted. You need to use QSignalSpy and link to QTestLib.

As they say in their documentation :

QSignalSpy can connect to any signal of any object and records its emission. QSignalSpy itself is a list of QVariant lists. Each emission of the signal will append one item to the list, containing the arguments of the signal.

You can also read their examples, but here is one of my unit tests that use google test :

class TestSomeControls : public testing::Test
{
public:

    TestSomeControls() :
        obj(),
        ctrl1Dis( &obj, SIGNAL(DisableControl1(bool)) ),
        ctrl2Dis( &obj, SIGNAL(DisableControl2(bool)) )
    {
    }

    model::SomeControls obj;

    QSignalSpy ctrl1Dis;
    QSignalSpy ctrl2Dis;
};

TEST_F( TestSomeControls, OnControl1Clicked_untilControl1Disabled )
{
    for ( int i = 0; i < 5; ++ i )
    {
        obj.OnControl1Clicked();
        ASSERT_EQ( ctrl1Dis.count(), 0 );
    }

    obj.OnControl1Clicked();

    ASSERT_EQ( ctrl1Dis.count(), 1 );
    ASSERT_EQ( ctrl1Dis.takeFirst().at(0).toBool(), true );
}

Compare anything to anything.

#include <utility>
#include <memory>
#include <iostream>

struct Base
{
  virtual bool operator== (const Base& other) const = 0;
  virtual ~Base() {}
};

template <class T>
struct Holder : Base
{
  Holder(T t) : t(t) {}
  bool operator== (const Base& other) const
  {
    const Holder<T>* h = dynamic_cast<const Holder<T>*>(&other);
    return (h && h->t == t);
  }
  private:
  T t;
};

struct Any
{
  template<class T>
    Any(T t) : p(std::make_shared<Holder<T>>(t)) {}
  bool operator== (const Any& other) const
  {
    return *p == *other.p;
  }
  private:
  std::shared_ptr<Base> p;
};

int main ()
{
  std::cout << (Any(2) == Any(2));
  std::cout << (Any(2) == Any(3));
  std::cout << (Any(2) == Any("foo"));
  std::cout << (Any("foo") == Any("foo"));
  std::cout << (Any("foo") == Any("bar"));
}

Implementation of operator< is deferred to the reader.

Important note Two pointers-to-member of different types will always compile unequal in this implementation, but it is possible that they will be equal in direct comparison after coercion to a common type. I.e &Foo::x and &Bar::x can be the same if Foo derives from Bar. Such behaviour cannot be easily added here.

This is a narrow answer to the narrow question.

The standard states by implication and also in a footnote that a pointer to member cannot be converted to void*. The likely rationale is that a pointer to member could require more bytes of storage than a void*. Your compiler should forbid the reinterpret cast, and even it if does not you run a real risk of clashes. You can test on your target compilers, but the risk remains.

The standard will permit you to convert a 'pointer to member of X of type T1' to 'pointer to member of Y of type T2' when T1 and T2 are both function types. In other words, your strategy is permitted as long as the common type is a pointer to member function. I think this is what you intended. S5.2.10/10 in N3337. It does not however guarantee that two such pointers will compare equal, in the way that it does for pointers to objects. For example, if the implementation includes an encoded 'this' pointer, it just won't work.

The standard will permit you to store the pointer to member in a union. You can provide a char[] member that is likely to be long enough, and you can use an assert on sizeof to make sure that it is. Provided it's a 'standard layout' type, accessing the value through the char[] should have guaranteed behaviour. Personally, I would try this just to find out how big those pointers actually are! But the problem about possible non-canonical values remains.

My third suggestion is that you use the typeid of the pointer-to-member-function instead of the pointer itself. Typeid can be applied to any expression -- if it's good enough for reinterpret_cast it's good enough for typeid -- and the resultant value should be unique to the type, not the instance.

After that I'm out of ideas. You might have to redefine/renegotiate the problem in a quest for other solutions.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!