I decided to get familiar with C++20 features, starting with modules. While doing that, I would like to create a simple GUI application based on QT. But apart from modules support in Cmake being crude at best, I noticed some strange errors, which are obviously caused by usage of modules, though I have no idea where do they come from.
Minimal reproducible example is as follows:
CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(TestProj VERSION 0.1.0 LANGUAGES CXX)
set(OUTPUT_BIN_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
SET(CMAKE_CXX_FLAGS "-fmodules-ts ${CMAKE_CXX_FLAGS}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_BIN_DIRECTORY})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_BIN_DIRECTORY})
# add_custom_target(
# std_modules ALL
# COMMAND ${CMAKE_CXX_COMPILER} -std=c++20 -g -fmodules-ts -xc++-system-header memory
# )
find_package(Qt5 REQUIRED COMPONENTS Widgets)
add_executable(TestApp main.cpp)
#add_dependencies(TestApp std_modules)
target_link_libraries(TestApp Qt5::Widgets)
main.cpp
#include <QApplication>
#include <QLabel>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QLabel hello("Hello world!");
hello.show();
return app.exec();
}
If I build this app as it is now (with custom target and its dependency commented out in CMakeLists.txt), then everything works.
But suppose I would like to use something from memory
header. As I understand, all headers from std can (should?) be imported as (if they were) modules, like so:
import <memory>;
To do that, hovewer, I need these headers pre-compiled, since Cmake would not prepare them for me. That’s what this custom target std_modules
is for. But as soon as I try re-build my app with this target and dependency uncommented (before I even add import <memory>;
to my code), compilation fails with lots of strange errors:
[main] Building folder: testproj
[build] Starting build
[proc] Executing command: /usr/bin/cmake --build /ramdisk/testproj/build --config Debug --target all --
[build] [1/3 33% :: 0.575] cd /ramdisk/testproj/build && /usr/bin/g++ -std=c++20 -g -fmodules-ts -xc++-system-header memory
[build] [2/3 66% :: 1.859] Building CXX object CMakeFiles/TestApp.dir/main.cpp.o
[build] FAILED: CMakeFiles/TestApp.dir/main.cpp.o
[build] /usr/bin/g++ -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -isystem /usr/include/qt -isystem /usr/include/qt/QtWidgets -isystem /usr/include/qt/QtGui -isystem /usr/include/qt/QtCore -isystem /usr/lib/qt/mkspecs/linux-g++ -fmodules-ts -g -std=gnu++20 -fPIC -MD -MT CMakeFiles/TestApp.dir/main.cpp.o -MF CMakeFiles/TestApp.dir/main.cpp.o.d -o CMakeFiles/TestApp.dir/main.cpp.o -c /ramdisk/testproj/main.cpp
[build] In file included from /usr/include/c++/13.2.1/bits/stl_tempbuf.h:63,
[build] from /usr/include/c++/13.2.1/memory:66,
[build] of module /usr/include/c++/13.2.1/memory, imported at /usr/include/qt/QtCore/qsharedpointer_impl.h:71,
[build] included from /usr/include/qt/QtCore/qsharedpointer.h:48,
[build] from /usr/include/qt/QtGui/qpixmap.h:48,
[build] from /usr/include/qt/QtGui/qbrush.h:52,
[build] from /usr/include/qt/QtGui/qpalette.h:46,
[build] from /usr/include/qt/QtWidgets/qwidget.h:48,
[build] from /usr/include/qt/QtWidgets/qframe.h:44,
[build] from /usr/include/qt/QtWidgets/qlabel.h:44,
[build] from /usr/include/qt/QtWidgets/QLabel:1,
[build] from /ramdisk/testproj/main.cpp:2:
[build] /usr/include/c++/13.2.1/ext/numeric_traits.h: In instantiation of ‘struct __gnu_cxx::__numeric_traits_integer<short unsigned int>’:
[build] /usr/include/c++/13.2.1/charconv:533:69: required from ‘constexpr bool std::__detail::__from_chars_alnum(const char*&, const char*, _Tp&, int) [with bool _DecOnly = true; _Tp = short unsigned int]’
[build] /usr/include/c++/13.2.1/format:277:42: required from ‘constexpr std::pair<short unsigned int, const _CharT*> std::__format::__parse_integer(const _CharT*, const _CharT*) [with _CharT = char]’
[build] /usr/include/c++/13.2.1/format:288:49: required from ‘constexpr std::pair<short unsigned int, const _CharT*> std::__format::__parse_integer(const _CharT*, const _CharT*) [with _CharT = wchar_t]’
[build] /usr/include/c++/13.2.1/format:471:51: required from ‘static constexpr std::__format::_Spec<_CharT>::iterator std::__format::_Spec<_CharT>::_S_parse_width_or_precision(iterator, iterator, short unsigned int&, bool&, std::basic_format_parse_context<_CharT>&) [with _CharT = wchar_t; iterator = const wchar_t*]’
[build] /usr/include/c++/13.2.1/format:509:43: required from ‘constexpr std::__format::_Spec<_CharT>::iterator std::__format::_Spec<_CharT>::_M_parse_width(iterator, iterator, std::basic_format_parse_context<_CharT>&) [with _CharT = wchar_t; iterator = const wchar_t*]’
[build] /usr/include/c++/13.2.1/format:904:33: required from ‘constexpr typename std::basic_format_parse_context<_CharT>::iterator std::__format::__formatter_int<_CharT>::_M_do_parse(std::basic_format_parse_context<_CharT>&, std::__format::_Pres_type) [with _CharT = wchar_t; typename std::basic_format_parse_context<_CharT>::iterator = const wchar_t*]’
[build] /usr/include/c++/13.2.1/format:987:21: required from ‘constexpr typename std::basic_format_parse_context<_CharT>::iterator std::__format::__formatter_int<_CharT>::_M_parse(std::basic_format_parse_context<_CharT>&) [with _Tp = char; _CharT = wchar_t; typename std::basic_format_parse_context<_CharT>::iterator = const wchar_t*]’
[build] /usr/include/c++/13.2.1/format:1777:28: required from here
[build] /usr/include/c++/13.2.1/ext/numeric_traits.h:130:3: error: definition of ‘enum __gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::<unnamed>’ does not match
[build] 130 | _GLIBCXX_INT_N_TRAITS(__int128, 128)
[build] | ^~~~~~~~~~~~~~~~~~~~~
[build] In file included from /usr/include/c++/13.2.1/utility:79,
[build] from /usr/include/qt/QtCore/qglobal.h:47,
[build] from /usr/include/qt/QtGui/qtguiglobal.h:43,
[build] from /usr/include/qt/QtWidgets/qtwidgetsglobal.h:43,
[build] from /usr/include/qt/QtWidgets/qapplication.h:43,
[build] from /usr/include/qt/QtWidgets/QApplication:1,
[build] from /ramdisk/testproj/main.cpp:1:
[build] /usr/include/c++/13.2.1/ext/numeric_traits.h:57:12: note: existing definition ‘enum __gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::<unnamed>’
[build] 57 | enum { __width = __value ? sizeof(_Tp) * __CHAR_BIT__ : 0 };
[build] | ^
[build] /usr/include/c++/13.2.1/ext/numeric_traits.h:130:3: note: ... this enumerator ‘__gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::__value’
[build] 130 | _GLIBCXX_INT_N_TRAITS(__int128, 128)
[build] | ^~~~~~~~~~~~~~~~~~~~~
[build] /usr/include/c++/13.2.1/ext/numeric_traits.h:57:14: note: enumerator ‘__gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::__width’ does not match ...
[build] 57 | enum { __width = __value ? sizeof(_Tp) * __CHAR_BIT__ : 0 };
[build] | ^~~~~~~
[build] /usr/include/c++/13.2.1/ext/numeric_traits.h:64:53: note: during load of pendings for ‘__gnu_cxx::__is_integer_nonstrict’
[build] 64 | static_assert(__is_integer_nonstrict<_Value>::__value,
[build] | ^~~~~~~
[build] ninja: build stopped: subcommand failed.
[proc] The command: /usr/bin/cmake --build /ramdisk/testproj/build --config Debug --target all -- exited with code: 1
[driver] Build completed: 00:00:01.892
[build] Build finished with exit code 1
It looks like the problem is connected with QT headers including memory
header in old-fashioned way, although it somehow seems to map to importing is as module automatically. I doubt this has anything to do with the fact I use QT in particular, I guess the problem is more generic. Still, this disables me from developing my application using modules.
Of course, I could just give up on importing std headers as modules and call it a day. But isn’t one of the points of modules to eliminate (or at least limit to minimum) including headers in the code? Why then should I give up on importing std headers like modules? This is more like a pointless workaround than a solution.