Compile errors with moc and an (unused) header with variadic templates



  • I have some c++14 code in my project using variadic templates. This code works perfectly on Visual Studio 2015, gcc 6 (linux) and clang (osx 10.11.5). These classes are even 100% covered in my unittests.

    I recently introduced Qt 5 into the codebase. When this header containing the class with variadic templates is even included in a cpp file that had it's header file moc'ed (through qt5_wrap_cpp in cmake), it spits out loads of errors:

    [ 68%] Building CXX object engine/CMakeFiles/aeon_engine.dir/platform/qt/platform_qt_opengl_widget.cpp.o
    In file included from /Users/robindegen/Development/Projects/aeon/engine/platform/qt/platform_qt_opengl_widget.cpp:17:
    In file included from /Users/robindegen/Development/Projects/aeon/engine/platform/qt/platform_qt_window.h:17:
    In file included from /Users/robindegen/Development/Projects/aeon/engine/platform/platform_window.h:17:
    In file included from /Users/robindegen/Development/Projects/aeon/engine/gfx/gfx_render_target.h:18:
    In file included from /Users/robindegen/Development/Projects/aeon/dep/libaeon/src/aeon/utility.h:63:
    /Users/robindegen/Development/Projects/aeon/dep/libaeon/src/aeon/utility/signals.h:46:19: error: expected ')'
        void emit(Args...args)
                      ^
    /Users/robindegen/Development/Projects/aeon/dep/libaeon/src/aeon/utility/signals.h:46:14: note: to match this '('
        void emit(Args...args)
                 ^
    /Users/robindegen/Development/Projects/aeon/dep/libaeon/src/aeon/utility/signals.h:46:15: error: declaration of 'Args'
          shadows template parameter
        void emit(Args...args)
                  ^
    /Users/robindegen/Development/Projects/aeon/dep/libaeon/src/aeon/utility/signals.h:26:19: note: template parameter is
          declared here
    template<class... Args>
                      ^
    /Users/robindegen/Development/Projects/aeon/dep/libaeon/src/aeon/utility/signals.h:46:15: error: field has incomplete
          type 'void'
        void emit(Args...args)
    

    Any idea's on how to fix this? Currently i'm not even using this class, and it's already giving problems. When I comment out the inclusion if this header, it works perfectly.; I want to start using this class in the future though, so just not including this header isn't an option. Anybody any ideas?



  • Hi! Did I understand this correctly that you have a template class that inherits from QObject?



  • @Wieland Nope, the class isn't even used (yet). Just included. It's used in other places in the codebase without problems.



  • Can you show us that header?



  • @Wieland Sure. This class is covered by unit tests and also used in other places in the code, so it should be fine. It's just as soon as I merely include it in something that has a Q_OBJECT it fails.

    #pragma once
    
    namespace aeon
    {
    namespace utility
    {
    
    template<class... Args>
    using signal_func = std::function<void(Args...)>;
    
    template<class... Args>
    class signal_connection
    {
    public:
        signal_connection()
            : handle_(0)
        {
        }
    
        explicit signal_connection(int handle, signal_func<Args...> func)
            : handle_(handle)
            , func_(func)
        {
        }
    
        int get_handle() const
        {
            return handle_;
        }
    
        void emit(Args...args)
        {
            func_(args...);
        }
    
    private:
        int handle_;
        signal_func<Args...> func_;
    };
    
    template<class... Args>
    class signal
    {
    public:
        signal() = default;
        ~signal() = default;
    
        signal_connection<Args...> operator+=(signal_func<Args...> f)
        {
            return connect(f);
        }
    
        signal_connection<Args...> connect(signal_func<Args...> f)
        {
            auto connection = signal_connection<Args...>(++last_handle_, f);
            connections_.push_back(connection);
            return connection;
        }
    
        void disconnect(signal_connection<Args...> c)
        {
            connections_.remove_if([&c](const signal_connection<Args...> &other) { return other.get_handle() == c.get_handle(); });
        }
    
        void operator()(Args...args)
        {
            for (auto c : connections_)
            {
                c.emit(args...);
            }
        }
    
    private:
        int last_handle_ = 0;
        std::list<signal_connection<Args...>> connections_;
    };
    
    template<class... Args>
    class signal_mt
    {
        using mutex_type = std::mutex;
        using handle_type = std::atomic<int>;
        using list_type = std::list<signal_connection<Args...>>;
    public:
        signal_mt() = default;
        ~signal_mt()
        {
            /* \note This does not solve the 'destruction' while signal is executing problem.
             * Reasoning:
             * Thread a is the owner (he creates and destroys) and thread b executes the signal multiple times. Then
             * while the signal is being destroyed from thread a, thread b tries to execute the signal. Thread a will
             * acquire the mutex and execute the signal destructor. When the signal is destroyed it will release the
             * mutex and allow thread b to execute the signal that does not exist. Which will result in havoc.
             */
            std::lock_guard<mutex_type> guard(lock_);
            connections_.clear();
        }
    
        signal_connection<Args...> operator+=(signal_func<Args...> f)
        {
            return connect(f);
        }
    
        signal_connection<Args...> connect(signal_func<Args...> f)
        {
            auto connection = signal_connection<Args...>(++last_handle_, f);
            {
                std::lock_guard<mutex_type> guard(lock_);
                connections_.emplace_back(connection);
            }
    
            return connection;
        }
    
        void disconnect(signal_connection<Args...> c)
        {
            std::lock_guard<mutex_type> guard(lock_);
            connections_.remove_if([&c](const signal_connection<Args...> &other) { return other.get_handle() == c.get_handle(); });
        }
    
        void operator()(Args...args)
        {
            std::lock_guard<mutex_type> guard(lock_);
            for (auto &c : connections_)
                c.emit(args...);
        }
    
    private:
        handle_type last_handle_{ 0 };
        list_type connections_;
        mutex_type lock_;
    };
    
    } // namespace utility
    } // namespace aeon
    


  • That's the problem:

    void emit(Args...args)
    {
    func_(args...);
    }

    You can't have a function named "emit" because that's a special keyword for the MOC. Simply rename it to something else.



  • Ah! Hmm.. I'd have to see if I can get away with renaming it. But if this works it'd be a great help thanks. I'll come back to you to let you know if that worked.



  • Great that has indeed worked.



  • @RobinDegen Great :)


  • Moderators

    Yes. It's a macro that expands to nothing. There are couple more of these: signals and slots. If they cause trouble you can disable them and use even uglier (but not clashing) macros like Q_EMIT.



  • @Chris-Kawa Oh thanks for that. That might actually be a much better solution for us.


Log in to reply
 

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