2016-02-29 40 views
0

大师和模板专家qobject_cast,我需要你的帮助......可变参数模板通过父()

我目前正在寻找在检查一个QObject的父层次的解决方案。我有一个自定义QDialog的有以下层次结构(父到子):

QDockWidget 
> CustomDockArea 
    > QMainWindow 
    > QDockWidget 
     > CustomDialog 

内CustomDialog类,我要检查,如果对话的层次结构这个例子匹配,所以我检查,如果这是accomplishable具有可变参数模板,例如:

assert(qobject_cast_parent<QDockWidget*, QMainWindow*, CustomDockArea*, QDockWidget*>(this)); 

,我想出了这样的事情:

template <class T, class... Ts> 
inline T qobject_cast_parent(QObject* root) 
{ 
    if (root) 
    { 
     if (sizeof...(Ts) == 0) 
     { 
      return qobject_cast<T>(root->parent()); 
     } 
     else 
     { 
      return qobject_cast_parent<Ts...>(root->parent()); 
     } 
    } 
    else 
    { 
     return nullptr; 
    } 
} 

然而,有几个问题:我需要的PA的最后一个参数rameter包作为函数的返回类型,在我们的例子中是QDockWidget *。我可以将第一个参数作为返回类型,但这会使模板调用有点麻烦。但是,即使解决了这个问题,我认为参数包的“展开”方式仍然存在问题,现在我有点不确定我的模板方法对于原始问题是否可行。也许你可以给我一些提示。提前致谢!!!

+1

“我要检查,如果层次结构是正确的。”为什么CustomDialog关心整个层次结构树?这是一个对话框,它应该可以在没有父母的情况下自行使用......当然,如果父母有正确的父母朝向树的根部,它可以*添加*功能,但是您不应该关心在哪里这些父母是。你可以简单地使用你发现的第一个和第二个'QDockWidget',但即使如此,我认为这样的接口是笨重的。也许你应该公开几个信号/插槽,并提供一个辅助方法,将它们连接到某个特定的树上。 –

+0

“正确”一词可能会产生误导,我相应地编辑了我的答案。对话框的行为取决于它在层次结构中的位置,因此必须进行某些检查。 – CppChris

+1

也许对话应该明确告诉做什么,从知道层次结构的代码?这将会更清洁。就像这样,你正在暴露一个API到你的对话框中,这个API没有用类的签名来表示...... –

回答

1

由于我没有完整的代码,我只能确认以下编译,但无法对其进行测试。我相信你可以为我做测试,让我知道如果这不起作用或者我误解了你的问题。

#include <QtCore/QObject> 
#include <cassert> 
#include <type_traits> 

// This ends the recursion with the actual qobject_cast. 
template <class T, class U> 
inline U *qobject_cast_parent(T* root) 
{ 
    // Make sure everything's a QObject, clear message if not. 
    static_assert(std::is_base_of<QObject, T>::value, "Object must be a QObject"); 

    if (root) 
    { 
    return qobject_cast<U *>(root->parent()); 
    } 
    else 
    { 
    return nullptr; 
    } 
} 

template <class T, class U, class... Us> 
inline U *qobject_cast_parent(T* root) 
{ 
    // Make sure everything's a QObject, clear message if not. 
    static_assert(std::is_base_of<QObject, T>::value, "Object must be a QObject"); 

    if (root) 
    { 
    return qobject_cast_parent<U, Us...>(qobject_cast<U *>(root->parent())) 
    } 
    else 
    { 
    return nullptr; 
    } 
} 

所以模板参数进行排序,从孩子的父母,你会被强制指定最里面的类型为好。因此,我认为你的assert例子是这样的(再次,未经测试,让我知道它是如何工作):

assert(qobject_cast_parent<CustomDialog, 
          QDockWidget, 
          QMainWindow, 
          CustomDockArea, 
          QDockWidget>(this)); 

编辑:为了完整起见,你也问的方式去参数包的最后一个参数的类型。你可以使用这样的事情:

template <typename T, typename... Ts> 
struct Last 
{ 
    typedef typename Last<Ts...>::type type; 
}; 

template <typename T> 
struct Last<T> 
{ 
    typedef T type; 
}; 

int main() 
{ 
    // For example, this gives std::string: 
    Last<int, float, char, std::string>::type foo = "bar"; 
    return 0; 
} 
+0

感谢您的时间!它不是很工作,我得到“不能将参数1从'CustomDialog *'转换为'QDockWidget *'与[U = QMainWindow T = QDockWidget]”...但是,这个结束递归的模板对我的解决方案很有帮助,所以我认为我其实很接近,我也有一个从参数包中获取最后一个参数并将其返回的解决方案。 – CppChris

+0

@ChrisInked糟糕,我想我发现了这个问题。它应该是第二个函数中的qobject_cast ;更新了我的答案。它解决了吗? – mindriot

2

用C++ 14,你可以简单地使用auto的返回类型:

template <class T> 
T* qobject_cast_parent(QObject* root) 
{ 
    return root 
     ? qobject_cast<T*>(root->parent()) 
     : nullptr; 
} 

template <class T, class T2, class... Ts> 
auto qobject_cast_parent(QObject* root) 
//-> typename Last<T2, Ts...>::type /* In c++11, you have to create this traits */ 
{ 
    return root 
     ? qobject_cast_parent<T2, Ts...>(qobject_cast<T*>(root->parent())) 
     : nullptr; 
}