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?
-
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.
-
@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?
-
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.
-
Great that has indeed worked.
@RobinDegen Great :)
-
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.
-
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.