Template: Force const parameter



  • Let's assume I have the following class:

    class LibraryMimeData : public QMimeData
    {
        Q_OBJECT
    
    public:
        LibraryMimeData() = default;
        virtual ~LibraryMimeData() override = default;
    
        virtual QStringList formats() const;
        virtual bool hasFormat(const QString& mimeType) const;
    
        void setLibraryItems(QList<const LibraryItem*> libraryItems);
        QList<const LibraryItem*> libraryItems() const;
    
        template<class T>
        QList<T> libraryItems() const
        {
            QList<T> list;
    
            for (const LibraryItem* libraryItem : _libraryItems) {
                T item = dynamic_cast<T>(libraryItem);
                if (item) {
                    list << item;
                }
            }
    
            return list;
        }
    
    private:
        Q_DISABLE_COPY(LibraryMimeData)
    
        QList<const LibraryItem*> _libraryItems;
    };
    

    LibraryItem is a base class with different subclasses. The class shown above is used to pass sets of those items around during drag'n'drop operations. The method QList<T> LibraryItemMimeData::libraryItems() const has been added for convenience to allow the receiver of the mime data to retrieve a list only containing items of a specific type. As all those "specific types" inherit from the LibraryItem base class, I am just using dynamic_cast here (note: qobject_cast wouldn't work because LibraryItem doesn't inherit from QObject).

    My question is: Can I force the caller of that method to only use template types/parameters (the T thing) that are const pointers? Right now he could request a list of QList<MyLibraryItemSubclass*>. However, as the LibraryItemMimeData class only has const pointers to those library items itself, it can only return QList<const MyLibraryItemSubclass*>.

    Any other comments regarding that piece of code are more than welcomed :)


  • Qt Champions 2016

    I haven't tried it, but:

    template<class T>
    QList<const T *> libraryItems() const
    {
        // ...
        QList<const T *> list;
        // ...
    }
    

    ?



  • The problem with that is that then the user/caller of that method has to write code like this:

    QList<const MyLibraryItem*> myLibraryItems = mimeData->libraryItems<MyLibraryItem*>();
    

    In my opinion that is just a no-go. You'd expect the two template parameters to be the same in that assignment.


  • Qt Champions 2016

    @Joel-Bodenmann
    Actually:

    QList<const MyLibraryItem *> myLibraryItems = mimeData->libraryItems<MyLibraryItem>();
    

    But yes, they differ. Whether or not it's acceptable it's up to you, I know of no other way.

    PS.
    I think a fat notice in the docs should be just enough. This isn't c++11 so what's on the left can be different from what's on the right (i.e. there's no auto confusing code). It's still valid to write:

    QWidget * button = new QToolButton();
    

  • Moderators

    Can I force the caller of that method to only use template types/parameters (the T thing) that are const pointers?

    I'll just assume you really meant pointers to const, because const pointer would be LibraryItem* const, which is entirely different beast.

    This is one of those cases where templates come really handy. Here's a stripped down implementation:

    class LibraryMimeData
    {
    private:
        QList<const LibraryItem*> _libraryItems;
    
        // this general type will get rid of non-pointer and pointer to non-const types
        template<typename T, bool is_pointer_to_const> struct Foo {
            static inline QList<T> convert(const QList<const LibraryItem*>& items) = delete;
        };
    
        //the specialized type will only accept pointer to const types
        template<typename T> struct Foo<T, true> {
            static inline QList<T> convert(const QList<const LibraryItem*>& items)
            {
                return /* DO THE CONVERSION */;
            }
        };
    
    public:
        //and here's a public interface
        template<typename T> QList<T> libraryItems() const {
            return Foo<T, std::is_pointer<T>::value && std::is_const<std::remove_pointer<T>::type>::value>::convert(_libraryItems);
        }
    };
    

    This way it will work for pointer to const types and will give a lovely error about using deleted function for all others.


  • Qt Champions 2016

    Ahahah, I needed a couple of reads to get it. It's quite nifty, albeit pretty verbose.


  • Moderators

    @kshegunov Yeah, templates usually do that when it's not you who wrote them ;) I usually steer away from them if I can but when you need to deal with type system and avoid runtime overhead they are a very flexible tool.
    The verbose part is an implementation detail. Commented well and renamed to something better than Foo can live happily in a codebase. The user facing function has a nice clean signature.


  • Qt Champions 2016

    @Chris-Kawa said in Template: Force const parameter:

    I usually steer away from them

    Me too, which you can deduce from my simplistic answer :P
    They're just marginally better than the preprocessor ... code writing code always perplexes me, I must admit ...

    PS.
    I must also give you credit for the partial specialization trick ... never would've come up with it myself.


  • Moderators

    They're just marginally better than the preprocessor

    Awww, that's hurtful to the templates :) Preprocessor is just glorified find/replace machinery. Templates are a full blown, type safe, statically checked sub-language. Combined with constexpr they really have a Death-Star-like power (with similar caveats ;) )
    I remember when I wrote template based compile time quick sort back in the days. Sure it resulted in a 500Mb executable and crashed the compiler for input arrays larger then a dozen elements but it was constant O(1) time for any input :P
    Sorry for offtopic. Nostalgia took me over ;)



  • @Chris-Kawa said in Template: Force const parameter:

    I'll just assume you really meant pointers to const, because const pointer would be LibraryItem* const, which is entirely different beast.

    Your assumption is correct. Sorry for using the wrong terminology. I'll blame not-getting-enough-sleep for it :p

    @Chris-Kawa said in Template: Force const parameter:

    Here's a stripped down implementation:

    Oh boy... This is going to take a while (and lots of reading) for me to understand it. But I appreciate your efforts. Also, experience tells me that all code examples you write/post on this forum always just work, so huge Thank you! although I don't understand it yet :p

    @Chris-Kawa said in Template: Force const parameter:

    I remember when I wrote template based compile time quick sort back in the days. Sure it resulted in a 500Mb executable and crashed the compiler for input arrays larger then a dozen elements but it was constant O(1) time for any input :P

    Is there any blog post / white paper you can link to? Sounds interesting :)


  • Moderators

    I guess the line that need the most explaining is this one:

    return Foo<T, std::is_pointer<T>::value && std::is_const<std::remove_pointer<T>::type>::value>::convert(_libraryItems);
    

    All the std magic is just a verbose way of saying "is T a pointer and does it point to a const value" so it's pretty much
    Foo<T, true>::convert(_libraryItems) for types that qualify and
    Foo<T, false>::convert(_libraryItems) for those that don't.

    Foo<T, false> will call into thet deleted specialization and Foo<T, true> into the real conversion.

    std::is_pointer<T>::value - this is true for pointers, false for others
    std::remove_pointer<T>::type - for any given pointer type it's a typedef for the pointed to type e.g for T being int* it's int.
    std::is_const<X>::value - this is true for const types and false for others

    Is there any blog post / white paper you can link to? Sounds interesting :)

    It was a class assignment over a decade ago. I'm sure the topic has a vast coverage, but I can't give you any links because it's simply useless in practice and I hadn't any interest in it after completing it.



  • I'm definitely in the wrong section of the forum given my knowledge of the language but why wouldn't a simple static_assert work here?

    template<class T>
        QList<T> libraryItems() const
        {
    static_assert(std::is_pointer<T>::value && std::is_const<std::remove_pointer<T>::type>::value,"You can only use const pointers as template parameters");
            QList<T> list;
    \\ etc
    

  • Moderators

    @VRonin said in Template: Force const parameter:

    why wouldn't a simple static_assert work here?

    It definitely would.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.