键入擦除成员函数指针的"right"方法是什么?
What's the "right" way to type-erase a member function pointer?
我正在编写一个测试装置,它涉及确保在适当的时间调用某些回调(实际上是Qt信号,但为了我的问题,这无关紧要(。为了帮助解决这个问题,我创建了一个帮助程序类,用于记录回调(信号(何时触发到列表中。
此列表需要能够记录触发的回调(信号(。我也不希望专门为此目的创建新的枚举。我的想法是将信号的地址记录为类型擦除指针,以便我可以根据信号的地址检查记录。
为了让自己更轻松一点,我将信号类型记录为:
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;
};
这从使用点"隐藏"了类型擦除,所以我以后可以写:
QCOMPARE(event.type, &SomeObject::someMethod);
。无需用演员阵容将其弄乱。
然而,海湾合作委员会并不高兴:
warning: cast between incompatible pointer to member types from ‘void (SomeObject::*)(...)’ to ‘void (SomeObject::*)()’ [-Wcast-function-type]
有没有办法让海湾合作委员会高兴,而无需诉诸诊断#pragma
来简单地关闭警告?有没有其他"更好"的方法来实现这种特殊的类型擦除?(请注意,我不需要调用SignalType
封装的成员;我只需要能够测试平等性。
叹息。应该搜索警告消息,而不是我正在尝试做什么。从技术上讲,我想这是 gcc 中不兼容函数类型之间的强制转换的副本,但这只询问如何摆脱警告,并且不清楚代码试图完成什么。因此,为了我可以在这里学到一些有用的东西,请专注于是否有其他"更干净"的方法来实现我的目标,而不是只是将其作为重复项关闭并说"无法修复"。
这是一个解决方案,其中包含一个函数指针,该指针可以比较相同类型的两个值,同时还充当运行时检查两个类型是否相同的std::type_info
。它将函数指针存储在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);
}
};
相关文章:
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 键入擦除成员函数指针的"right"方法是什么?
- 是否有任何方法可用于更改 MFC(C++) 中 CListBox 的属性"right align text"?
- "Right" C++ 中从大端二进制文件中检索 int 的方法
- 在 c++11 中,迭代向量的最现代、最"right"的方法是什么
- 返回std::right方法
- 在 std::中使用 GUID 作为键的"right"方法是什么hash_map
- 读取带有C++流的文件"right"方法是什么?
- 在C++中避免别名(例如,将容器的元素添加到自身时)的"right"方法是什么?