Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

how to organize a container of views?



  • i'm making a widget container class, that is an arbitrary number of views can be added to it:

    class Viewer : public QFrame
    {
    	Q_OBJECT
    
    public:
    	/// Constructor
    	explicit Viewer(QWidget *parent = nullptr,	Qt::WindowFlags flags = Qt::Window) noexcept;
    
    	void addView(int nIndex, QWidget *pView);
    
    	QWidget *getView(int nIndex) const;
    
    protected:
    	/// This map contains the views of the viewer
    	QHash<int, QWidget *> m_mapViews;
    
    	/// The widget container layout
    	QSplitter *m_pWidgetSplitter = nullptr;
    
    }; // class Viewer
    
    Viewer::Viewer(QWidget *parent /* = nullptr */, Qt::WindowFlags flags /* = Qt::Window */) noexcept
    	: QFrame(parent, flags)
    	, m_pWidgetSplitter(new QSplitter(this))
    {
    	// configure the layout
    	auto pWidgetsLayout = new QHBoxLayout(this);
    	pWidgetsLayout->setContentsMargins(0, 0, 0, 0);
    	pWidgetsLayout->addWidget(m_pWidgetSplitter);
    }
    
    void Viewer::addView(int nIndex, QWidget *pView)
    {
    	// save in the map
    	m_mapViews[nIndex] = pView;
    
    	// and add to the widgets layout
    	m_pWidgetSplitter->insertWidget(nIndex, pView);
    }
    
    QWidget *Viewer::getView(int nIndex) const
    {
    	return m_mapViews.contains(nIndex) ? m_mapViews[nIndex] : nullptr;
    }
    

    adding views is easy:

    Browser::Browser(QWidget *parent /* = nullptr */,
    	Qt::WindowFlags flags /* = Qt::Window */)
    	: Viewer(parent, flags)
    	, m_pTreeView(new Tree(this))
    	, m_pTableView(new Table(this))
    {
    	addView(Widgets::TreeView, m_pTreeView);
    	addView(Widgets::TableView, m_pTableView);
    }
    

    the problem here is that i need to save the pointers to get them easily:

    Tree *Browser::getTree() const
    {
    	return m_pTreeView;
    }
    
    Table *Browser::getTable() const
    {
    	return m_pTableView;
    }
    

    what i'd like to do is to use the getView function of Viewer, but in that case i need to deal with casts a lot:

    Tree *Browser::getTree() const
    {
    	return static_cast<Tree *>(getView(Widgets::TreeView));
    }
    

    how can i do this effectively?


  • Moderators

    You could, for example, give your classes an explicit type id:

    class Tree
    {
       enum {TypeId = Widgets::TreeView };
    };
    class Table
    {
       enum {TypeId = Widgets::TableView };
    };
    

    Then you could make a template wrapper:

    template<class T> T* getView()
    {
        return static_cast<T*>(getView(T::TypeId));
    }
    

    and use it like this:

    Tree* tree = getView<Tree>();
    Table* table = getView<Table>();
    

    If you don't want to modify your view classes like that you can do something similar outside of them i.e. make a QHash<QMetaObject*, int> m_metaTypes member and modify addView to do this:

    m_metaTypes[pView->metaObject()] = nIndex;
    

    and then the getView template wrapper would look something like this:

    template<class T> T* getView()
    {
        int nIndex = m_metaTypes[T::staticMetaObject()];
        return static_cast<T*>(getView(nIndex));
    }
    


  • i don't get how it's different or better from my version of getView. it's still using casts. what if i get the view a lot? it's casting all the time.


  • Moderators

    I thought your worry was about the amount of times you had to write that cast i.e. a separate function for each type. In that case the difference is you only write that single wrapper instead of a function for each type.

    Are you actually worried about the performance of that cast? static_cast of one pointer type to another is next to nothing and in many cases, with optimizations turned on, it's actually nothing. Remember that a processor has no notion of those types. All pointers are just numbers. In some cases a processor might need to add an offset to a number when you cast which is just totally insignificant compared to other things you do in that code. A call to operator[] in the getView method is like dozens times more costly. A function call is way more costly. If you're worried about performance on that level you're definitely looking in the wrong places.



  • okay i get it.

    so in your first suggestion you call getView(int) from getView() which is again operator[].

    by the way, i wrote

    Tree *Browser::getTree() const
    {
    	return static_cast<Tree *>(getView(Widgets::TreeView));
    }
    Table *Browser::getTable() const
    {
    	return static_cast<Table *>(getView(Widgets::TableView));
    }
    

    to hide the implementation of the getter. although, since i'm working with views, having getView<type>(); makes sense too. i guess i'll use that option.


Log in to reply