QVariant的访客模式(无手动型式测试和铸造) [英] QVariant's Visitor pattern (without manual type testing and casting)

查看:370
本文介绍了QVariant的访客模式(无手动型式测试和铸造)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Qt的QVariant类是否有任何现有的(和方便的)访问者模式实现?

Does Qt's QVariant class has any existing (and convenient) Visitor pattern implementation?

如果没有,是否可以实现类似 boost :: apply_visitor(),即最小化关于测试类型和转换的重复?

If not, is it possible to achieve something similar to boost::apply_visitor(), i.e. minimize the the duplication in regard to testing the type and casting?

以下行:

/* 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& c $ c>可能不是上面最好的解决方案,但是要点是:可以进行类型映射( QMetaType typename T )自动实现?

Edit 1: QVariant::canConvert<T>() may not be the best solution above, but the point is: can the type mapping (between QMetaType and typename T) be achieved automatically?

编辑2:访客函数或访问者函数对我来说并不重要。

Edit 2: "Visitor functor" or "Visitor function" doesn't really matter for me. What matters is I want to avoid testing the type and casting (if at all possible).

推荐答案

Introspectable Visitor

我们希望避免测试类型和转换(如果可能的话)

您可以利用moc生成的内省信息。声明您的访问者是 Q_GADGET 。这会向访问者添加一个静态 staticMetaObject 成员,其中包含有关可调用方法的信息。

Introspectable Visitor

You could leverage the introspection information generated by moc. Declare your visitor to be Q_GADGET. This adds a single static staticMetaObject member to the visitor, containing the information about the invokable methods there.

// 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有所有必要的信息,通过不透明类型作为可调用方法的参数:

Qt has all the information necessary to pass opaque types around as arguments to invokable methods:

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"

您可以将转换分解为类型和条件代码执行:

You can factor out the conversion to a type and conditional code execution:

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 函数推导出可调用的参数类型,并且如果变体具有匹配类型则调用可调用:

The withConversion function deduces the argument type of the callable and invokes the callable if the variant is of the matching type:

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

查看这个问题更多关于可调用对象中的参数类型扣除。

See this question for more about argument type deduction in callables.

这篇关于QVariant的访客模式(无手动型式测试和铸造)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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