QVariant的访客模式(无需手动型式测试和铸造)
QVariant's Visitor pattern (without manual type testing and casting)
Qt的QVariant类有任何现有的(和方便的)访问者模式实现吗?
如果没有,是否有可能实现类似于boost::apply_visitor()
的东西,即最小化关于测试类型和转换的重复?
我想实现以下目标:
/* I have a QVariant that can contain anything, including user types */
QVariant variant;
/* But in my Visitor I'm interested only in ints and QStrings (for the sake of the example) */
struct Visitor
{
void operator()(int i) { /* do something with int */ }
void operator()(QString s) { /* ...or QString */ }
};
/* The question is: */
/* Can this be implemented in a generic way (without resorting to particular template parameters)? */
template <typename VisitorT>
void visit(QVariant variant, VisitorT visitor)
{
if (variant.canConvert<int>()) {
visitor(variant.value<int>());
} else if (variant.canConvert<QString>()) {
visitor(variant.value<QString>());
} /* and so on (if needed for other types)... */
}
/* So that later I can use it like that */
visit(variant, Visitor());
编辑1:QVariant::canConvert<T>()
可能不是上面最好的解决方案,但问题是:类型映射(QMetaType
和typename T
之间)可以自动实现吗?
编辑2:"访客函子"或"访客函数"对我来说并不重要。重要的是我想避免测试类型和类型转换(如果可能的话)。
Introspectable Visitor
您可以利用moc生成的自省信息。将访问者声明为Q_GADGET
。这将向访问者添加一个静态staticMetaObject
成员,其中包含关于那里的可调用方法的信息。
// https://github.com/KubaO/stackoverflown/tree/master/questions/variant-convert-38071414
#include <QtCore>
struct Foo {
int a;
Foo() = default;
explicit Foo(int a) : a(a) {}
};
QDebug operator<<(QDebug debug, const Foo & f) {
return debug << f.a;
}
Q_DECLARE_METATYPE(Foo)
struct Visitor
{
Q_GADGET
Q_INVOKABLE void visit(int i) { qDebug() << "got int" << i; }
Q_INVOKABLE void visit(const QString & s) { qDebug() << "got string" << s; }
Q_INVOKABLE void visit(const Foo & f) { qDebug() << "got foo" << f; }
};
Qt拥有传递不透明类型作为可调用方法参数所需的所有信息:
template <typename V>
bool visit(const QVariant & variant, const V & visitor) {
auto & metaObject = V::staticMetaObject;
for (int i = 0; i < metaObject.methodCount(); ++i) {
auto method = metaObject.method(i);
if (method.parameterCount() != 1)
continue;
auto arg0Type = method.parameterType(0);
if (variant.type() != (QVariant::Type)arg0Type)
continue;
QGenericArgument arg0{variant.typeName(), variant.constData()};
if (method.invokeOnGadget((void*)&visitor, arg0))
return true;
}
return false;
}
也许这就是你想要的:
int main() {
visit(QVariant{1}, Visitor{});
visit(QVariant{QStringLiteral("foo")}, Visitor{});
visit(QVariant::fromValue(Foo{10}), Visitor{});
}
#include "main.moc"
本例结束。
Non-Introspectable访客
你可以分解为一个类型和条件代码执行的转换:
void visitor(const QVariant & val) {
withConversion(val, [](int v){
qDebug() << "got an int" << v;
})
|| withConversion(val, [](const QString & s){
qDebug() << "got a string" << s;
});
}
int main() {
visitor(QVariant{1});
visitor(QVariant{QStringLiteral("foo")});
}
withConversion
函数推导出可调用对象的参数类型,如果变量是匹配的类型,则调用该可调用对象:
#include <QtCore>
#include <type_traits>
template <typename T>
struct func_traits : public func_traits<decltype(&T::operator())> {};
template <typename C, typename Ret, typename... Args>
struct func_traits<Ret(C::*)(Args...) const> {
using result_type = Ret;
template <std::size_t i>
struct arg {
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename F> bool withConversion(const QVariant & val, F && fun) {
using traits = func_traits<typename std::decay<F>::type>;
using arg0_t = typename std::decay<typename traits::template arg<0>::type>::type;
if (val.type() == (QVariant::Type)qMetaTypeId<arg0_t>()) {
fun(val.value<arg0_t>());
return true;
}
return false;
}
相关文章:
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- QGraphicsPolygonItem在拖动时未更新QPolygonF坐标
- 无法编译 rtmidi 测试 cmidiin.cpp 文件, 非法指令
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 尽管测试成功,CppUnit测试核心仍被丢弃.为什么
- 数据成员SFINAE的C++17测试:gcc vs clang
- 错误处理.将系统错误代码映射到泛型
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 为什么二进制搜索在我的测试中不起作用
- 从父数组测试用例构造二叉树失败
- 试图对缓存进行跨步测试,但程序并没有结束
- 有什么好的方法可以让系统调用代理允许在单元测试中进行模拟
- OpenGL在启用深度测试时不会丢弃我的碎片
- 为测试目标创建具有不同源文件夹的文件
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 在子目录中使用target_sources()命令时用于单元测试(qtest)的项目结构
- VC++本机单元测试,找不到调试符号
- 链表的泛型函数remove()与成员函数remove)
- 换位表导致测试失败(但在游戏中运行良好)
- 编译宏以测试uint64_t和无符号长整型之间的差异