I am writing a test fixture which involves ensuring certain callbacks are called at appropriate times (actually Qt signals, but it shouldn't matter for the sake of my problem). To help with this, I created a helper class that records when a callback (signal) fires into a list.
This list needs to be able to record which callback (signal) fired. I would also prefer to not need to create a new enumeration specifically for this purpose. My idea was to instead record the address of the signal as a type-erased pointer so I can check the record against the address of the signal.
To make things a little easier on myself, I record the signal type as a:
template <typename Object>
class SignalType
{
public:
SignalType() = default;
SignalType(SignalType const&) = default;
SignalType(SignalType&&) = default;
template <typename R, typename... Args>
SignalType(R (Object::*member)(Args...))
: member{reinterpret_cast<void (Object::*)()>(member)} {}
template <typename R, typename... Args>
bool operator==(R (Object::*other)(Args...)) const
{ return this->member == reinterpret_cast<void (Object::*)()>(other); }
private:
void (Object::*member)() = nullptr;
};
This "hides" the type erasure from the point of use, so I can later just write:
QCOMPARE(event.type, &SomeObject::someMethod);
...without needing to clutter that with a cast.
However, GCC is unhappy:
warning: cast between incompatible pointer to member types from ‘void (SomeObject::*)(...)’ to ‘void (SomeObject::*)()’ [-Wcast-function-type]
Is there a way to make GCC happy without resorting to diagnostic #pragma
s to simply shut up the warning? Is there some other, "better" way to achieve this particular flavor of type-erasure? (Note that I don't need to ever call the member that SignalType
encapsulates; I just need to be able to test for equality.)
Sigh. Should search on the warning message, not what I'm trying to do. Technically I guess this is a duplicate of Cast Between Incompatible Function Types in gcc , however that only asks how to get rid of the warning, and isn't clear what the code is trying to accomplish. So, in order that I might learn something useful here, please focus on if there is some other, "cleaner" way to accomplish my goal rather than just closing this as a duplicate and saying "it can't be fixed".
Here is a solution that holds a function pointer that can compare two values of the same type whilst also acting as a std::type_info
checking at run time if two types are the same. It stores the function pointer in a char[]
.
#include <new>
template<typename Object>
class SignalType
{
public:
SignalType() = default;
SignalType(SignalType const&) = default;
SignalType& operator=(SignalType const&) = default;
template<typename R, typename... Args>
SignalType(R (Object::*member)(Args...)) noexcept
: comparator(&compare_members_from_void_ptr<R, Args...>) {
using member_ptr_type = R(Object::*)(Args...);
static_assert(sizeof(member_ptr_type) <= sizeof(void(Object::*)()), "Member pointer type too large?");
static_assert(alignof(member_ptr_type) <= alignof(void(Object::*)()), "Member pointer align too large?");
// Don't need to destruct since it has a trivial destructor
new (member_storage) member_ptr_type(member);
}
bool operator==(const SignalType& other) const {
if (!comparator) return !other.comparator; // Check both empty
// Same comparator implies same type
return comparator == other.comparator && comparator(member_storage, other.member_storage);
}
bool operator!=(const SignalType& other) const {
return !(*this == other);
}
// Return true if these contain pointers to members of the same type
bool is_same_type_as(const SignalType& other) const {
return comparator == other.comparator;
}
// true if holding a typed pointer (could still be nullptr)
explicit operator bool() const {
return comparator;
}
// Check if holding an `R(Object::*)(Args...)`
template<typename R, typename... Args>
bool is_type() const noexcept {
return comparator && comparator == &compare_members_from_void_ptr<R, Args...>;
}
// Returns the held function pointer if it is of type R(Object::*)(Args...), else nullptr
template<typename R, typename... Args>
R(Object::* get() const noexcept)(Args...) {
return is_type<R, Args...>() ? *static_cast<R(Object::**)(Args...)>(static_cast<void*>(member_storage)) : nullptr;
}
private:
alignas(void(Object::*)()) char member_storage[sizeof(void(Object::*)())];
bool (*comparator)(const void*, const void*) = nullptr;
template<typename R, typename... Args>
static bool compare_members_from_void_ptr(const void* a, const void* b) noexcept {
return *static_cast<R(Object::*const *)(Args...)>(a) == *static_cast<R(Object::*const *)(Args...)>(b);
}
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.