Qt macro keywords cause name collisions - c++

I am building an NCurses interface for my Qt project. I want to use CDK but I think the signals member of this struct is colliding with the Qt signals keyword.
/usr/include/linux/cdk.h:411: error: expected unqualified-id before 'protected'
How can I get CDK to work with Qt?

You can define the QT_NO_KEYWORDS macro, that disables the “signals” and “slots” macros.
If you use QMake:
CONFIG += no_keywords
(Qt Documentation here)
If you’re using another build system, do whatever it needs to pass -DQT_NO_KEYWORDS to the compiler.
Defining QT_NO_KEYWORDS will require you to change occurrences of signals to Q_SIGNALS and slots to Q_SLOTS in your Qt code.
If you cannot change all the Qt code, e.g. because you're using third-party libraries not being "keyword-clean", you could try to undefine "signals" locally before including cdk.h:
#undef signals
#include <cdk.h>
I'd recommend to use no_keywords though if possible, as it is less tedious and error-prone.

Related

Cmake: target_compile_definitions with find_package

I have a package, libtorch, that depends on libraries that use the keyword slots for some function (e.g. in Aten). In the meantime, the project that I am using is based on Qt, which use slots as a special keyword (macro), which then interferes with torchlib.
The solution to avoid such interference is to add the QT_NO_KEYWORDS definition when importing the external library that enters in conflict with Qt: Turning the no_keyword (Qt option) ON in a cmake file to work with boost::signals
It has then to be done only on the code that depends on libtorch and not the rest, so I tried several ways to add it, like CMake Add and Remove a Macro definition to compile Shared Library/Executable and Add target properties to an existing imported library in CMake, but I can't get it to work.
target_compile_definitions seems to be available after cmake 3.11 on "IMPORTED targets"(on cmake terminology).
So why the following cmakelist extract doesn't work? (in that case, the normal QT slots code are not properly recognized as a macro):
find_package (Torch REQUIRED PATHS ${Torch_DIR})
add_executable( ${PROJECT_NAME} ${header_files} ${source_files} ${hpp_files} ${moc_list} ${generated_ui_files} ${generated_qrc_files} )
target_link_libraries(${PROJECT_NAME} ${Qt_LINK_LIBRARIES})
#add_definitions(-DQT_NO_KEYWORDS)
target_link_libraries(${PROJECT_NAME} ${TORCH_LIBRARIES})
#remove_definitions(-DQT_NO_KEYWORDS)
target_compile_definitions(torch INTERFACE QT_NO_KEYWORDS)
Possible Solutions
Option 1:
Use QT_NO_KEYWORDS and replace slots and other keywords with their equivalent uppercase macros, e.g. Q_SLOTS.
Option 2:
Don't use QT_NO_KEYWORDS and wrap the parts of your C++ code with conflicting keywords like this:
#undef slots
#include <torch/torch.h>
#define slots Q_SLOTS
Explanations
The INTERFACE keyword in target_compile_definitions() tells CMake that all targets that depend on the torch library need the QT_NO_KEYWORDS macro defined as well. This is why all sources of your executable will be compiled with -DAT_NO_KEYWORDS and the slots keyword will not be defined.
I suppose you tried to fix your problem with the commented out add_definitions()/remove_definitions() calls. Several reason why those don't work as expected:
They only affect compilation not linking. Putting them around target_link_libraries() has no effect.
They manipulate the compile flags of all targets created in the current scope (current CMakeLists.txt), no matter if they were created before or after the command gets invoked, see the docs. This means calling add_definitions() and subsequently remove_definitions() with the same arguments will result in no flags being added at all no matter at which point in your CMakeLists.txt you call them.
You should also note that commands operating on the directory level (add_*/remove_*) are considered "old style" CMake. Modern CMake aims to only operate on the target level (target_* commands). Some more "Modern CMake" suggestions:
Use target_sources() instead of variables like header_files, source_files, etc. to add sources to your targets.
Always use target_link_libraries() with a PRIVATE, PUBLIC or INTERFACE keyword. Excerpt from Craig Scotts Professional CMake book:
The target_link_libraries() command also has a few other forms, some of which have been part of CMake from well before version 2.8.11. [...] Their use is generally discouraged for new projects. The full form shown previously with PRIVATE, PUBLIC and INTERFACE sections should be preferred, as it expresses the nature of dependencies with more accuracy. [...] The above form [(without PRIVATE/PUBLIC/INTERFACE)] is generally equivalent to the items being defined as PUBLIC, but in certain situations, they may instead be treated as PRIVATE. In particular, if a project defines a chain of library dependencies with a mix of both old and new forms of the command, the old-style form will generally be treated as PRIVATE.

How to detect OS without directive ifdef?

I have to reset system date and time either on Linux, or on Windows.
How do I check OS without using ifdef, or if defined?
With CMake, you can conditionally add files to a target by using generator expressions. With that, you can isolate any platform-specific code in files, and use them in your build specification as follows.
add_executable(reset-time
$<$<PLATFORM_ID:Darwin>:resettime-macos.cpp>
$<$<PLATFORM_ID:Linux>:resettime-linux.cpp>
$<$<PLATFORM_ID:Windows>:resettime-windows.cpp>)
And as #eeroika pointed out in the comments, a good alternative is
add_executable(reset-time
resettime-$<LOWER_CASE:${CMAKE_SYSTEM_NAME}>.cpp)
as it requires not adjustment (of the CMakeLists.txt) when porting your project to a new system and you can catch a missing implementation file earlier, i.e., before linking.
You could set a global macro definition to the compiler based on which operating system you are compiling for. For example -D IS_WINDOWS=1. You could then use #if IS_WINDOWS instead of #ifdef. I don't know why this would be useful but it achieves what you asked.

Custom compilation flag to enable specific feature

I have a Qt project which requires a library (gphoto2) to enable some features that are not essential. I'd like to add some sort of configuration option to my qmake or make call to enable features using this library, so I can compile without it being installed.
What is the best way to configure something like this?
I guess I need some way to add a define based on a compiler parameter, which I can query in my code using #ifdef ...
I assume you use make (without qmake). It is reasonable and quite easy to use GNU make (alone) on Qt projects. You could use some other build automation tool like ninja.
Then you could decide to enable that Gphoto feature by compiling your code with -DWITH_GPHOTO and using #if WITH_GPHOTO in your C++ code.
You would compile by adding
CXXFLAGS+= -DWITH_GPHOTO
in your Makefile
I won't call that a "custom compiler flag" (e.g. like GCC plugins can provide) but a preprocessor flag. It is pretty standard practice.
Maybe you also want to pass such flags to moc. Then your Makefile is running moc thru some rule and command, which you could tailor too.
BTW, you might consider GNU autoconf or some other Makefile generator like cmake. I don't think you should spend too much time on these...
PS. I don't know how that idea translates into qmake and leave you to find out.
Assuming, you are using qmake, you can add a preprocessor definition depending on the existence of a file or an environment variable.
You could add a qmake project for compiling your external library and place it relative to your own project by default.
LIBGPHOTO2_PATH = $$getenv(LIBGPHOTO2PATH)
isEmpty(LIBGPHOTO2_PATH): LIBGPHOTO2_PATH = ../../libgphoto2
exists($$LIBGPHOTO2_PATH/libgphoto2.pri): include($$LIBGPHOTO2_PATH/libgphoto2.pri)
In libgphoto2.pri you add a preprocessor definition to indicate the presence of libgphoto2, add include and linker paths etc.:
DEFINES += WITH_LIBGPHOTO2
In the code of your dependent project, you check for the presence using #ifdef.
Instead of creating a qmake-project to compile, you could also check for the presence of the compiled library at a given path and set values directly (I don't know how libgphoto compiles, so I assume a default directory structure with include/, lib/ etc):
LIBGPHOTO2_PATH=$$getenv(LIBGPHOTO2PATH)
isEmpty(LIBGPHOTO2_PATH): LIBGPHOTO2_PATH = ../../libgphoto2
exists($$LIBGPHOTO2_PATH/include) {
DEFINES += WITH_LIBGPHOTO2
INCLUDEPATH += $$LIBGPHOTO2_PATH/include
LIBS += -L$$LIBGPHOTO2_PATH/lib -lgphoto2
}
You should however consider to move to something more modern like qbs, which is a lot faster, more flexible and easier to read.

Does Qt have cyclic buffer?

I couldn't find anything similar.
I found that boost library has an implementation, but I'm not sure I want to insert the whole library just for cyclic buffer implementation.
There are no such types in Qt's public API. If you're willing to use Qt's internals, there are two classes of note:
QRingBuffer
Stores bytes and is tailored for I/O buffering. Used by QSerialPort etc.
QT += core-private # In the .pro file
#include <private/qringbuffer_p.h>
https://github.com/qt/qtbase/blob/5.9/src/corelib/tools/qringbuffer_p.h
QCircularBuffer
Stores arbitrary types and is of general use. Provides sensible STL-style API.
QT += 3dcore-private # In the .pro file
#include <private/qcircularbuffer_p.h>
https://github.com/qt/qt3d/blob/5.9/src/core/resources/qcircularbuffer_p.h
Note: After adding the private modules to the project file, you have to re-run qmake, or ideally delete the build folder. A mere rebuild of the project won't get the code to compile!
There is one called QCircularBuffer.
http://doc.qt.io/archives/qt-5.5/qt3d-qcircularbuffer.html#details
QCircularBuffer<T> is one of Qt's generic container classes (from documentation). So this class will fulfill all basic requirements of a container class.
Need to include 3dcore library (Qt3D header).
Check your version of Qt can support it or not. I saw this in Qt5.5.

How enable deprecated functions in Qt5

I want to port a Qt4 program to Qt5 and some functions are not defined (such as QHeaderView::setMoveable), but I see in the qheaderview.h file that with some magic defines (QT_DEPRECATED_SINCE) it should be possible to reenable them.
What do I have to do in order to let QHeaderView::setMovable reappear? I do not want to rewrite my code if there is a way like that.
You can add to your .pro file the following line:
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0