Skip to content

Does not work in C++23 due to conflicts with import std cxx modules #2969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
igormcoelho opened this issue Apr 1, 2025 · 4 comments
Open

Comments

@igormcoelho
Copy link
Contributor

igormcoelho commented Apr 1, 2025

Describe the bug
In past discussions, Catch2 devs explained that it's not possible to migrate the project for CXX Modules, because modules cannot export macros, which is correct (#2299). However, even if the user has cxx modules in its code, specially C++23 import std, then it cannot currently interoperate with header only catch2, because it imposes its #include , generating ODR with import std, if compiler is not "smart enough" (which is currently the case and maybe will never be fully solved).
My problem now is that I'm a long time Catch2 user, and I cannot make it work with Clang 19 or GCC 15, because I'm using import std on C++23 and that's incompatible with current Catch2 design (but it's easy to solve!).

Expected behavior
Catch2 should provide some way to disable its #include for STD things, for example, adding an option or macro like "CATCH2_DISABLE_STD_INCLUDE"... so , all #include should be surrounded by this option:

#ifndef CATCH2_DISABLE_STD_INCLUDE
#include <string>
#include <vector>
#endif

Macro name can also be CATCH_USE_STDLIB_MODULE, as in, #2575, but I don't know if Catch2 is interested in really using import std... maybe not! But this proposal here is simpler, it just allows users to choose between its own import std or the current Catch2 #include dependencies.

Reproduction steps
Create some Module.cpp file, with import std, and try to #include catch2, or try to link against it. ODR violations will be reported.

Platform information:

  • OS: Ubuntu Linux
  • Compiler: Clang 19 or GCC 15

Additional context
If necessary, I can do the PR... specially, for the amalgamated hpp file, it would be nice to disable all its header inclusions.

@igormcoelho
Copy link
Contributor Author

@ChrisThrasher

@ChrisThrasher
Copy link
Collaborator

ChrisThrasher commented Apr 8, 2025

I don't know if Catch2 is interested in really using import std... maybe not!

Seeing as that import std; is still a rather cutting edge feature and Catch2 is currently targeting C++14 (with optional C++17 features) and a version of CMake much to old to support any C++20 modules, using import std; is well out of reach for the library.

I certainly want to support those who use import std;. I think it's a lovely feature and don't want Catch2 to prevent its use. However I have to acknowledge that this is ultimately an issue with the implementations which as of the present moment are not shipping what I'd call a production-ready implementation of standard library modules. I'm open to this idea and will keep this issue open, but this is not a change I'm eager to make today. I will revisit this opinion in the future as we can get more real world feedback on C++20 modules and import std; usage in particular.

Perhaps I'm overestimating how much would have to change to implement this but I'm picturing having to add and maintain that #ifndef CATCH2_DISABLE_STD_INCLUDE preprocessor conditional for every single public header in the library. Such a feature would also need some CI testing infrastructure to make sure we don't introduce any regressions which may be difficult since import std; requires a particularly new release of compilers and CMake to work in the first place.

@horenmar
Copy link
Member

AIUI, you should be fine as long as you include catch2 and then import std, because

#include <vector>
import std;

works on major implementation (because this is the order that can be made to work with workarounds inside the stdlib).

@igormcoelho
Copy link
Contributor Author

I agree that it's still experimental, but people are already starting to use it, or at least, to understand how they will adapt their projects, preparing for a future modularized C++.

Such a feature would also need some CI testing infrastructure to make sure we don't introduce any regressions which may be difficult since import std; requires a particularly new release of compilers and CMake to work in the first place.

The CI part is not that hard, at least for github actions and clang testing, just pip install cmake, it will get CMake 4.0, and clang can be installed easily as well... for MSVC I agree it can be challenging, I didn't try. I thought it could be an easy change (just add some macro), but since it's not, I understand the position of Catch2 project. To be honest, I'm still not sure if compilers and tools will be able to fully resolve all these ODR stuff, that's why I believe some macro to fully disable all #include STD can be a good thing.

works on major implementation

It works on Clang 19, my previous message was not precise... ODR violation is only reported by clangd, and it does not work with GCC 15 (latest).

clangd-19 complains while it compiles with clang 19:
Image

I agree that clangd is not completed for cxx modules, more troubling is GCC 15 (maybe won't fix that soon, I don't know...), but as longs as major compilers manage to compile with modules, there's really no need to change or remove the inclusions from Catch2.
Indeed it's experimental, but modules are certainly the future of C++, compilation times on some projects reduced from minutes to zero, it's impressive... but that's fine to keep using catch2 this way, as long as it's on clang-19 and assuming same behavior will happen on future improvements of gcc 15 (I could not test on MSVC).

My working example for clang 19 on CMake 4.0 (latest):

// bloco1.cppm (module file)

export module bloco1;
export import std;

export auto exercicioA(std::string_view s) {
  return 1;
}
// teste1.cpp (test file)

#include <catch2/catch_test_macros.hpp>

import bloco1;
import std;

TEST_CASE("test1 catch") {
  //
  exercicioA("a");     // not ok for clangd!
  std::vector<int> v; // ok on clang, not for gcc 15 
  std::string s;          // ok on clang, not for gcc 15
  int x = 10;
  REQUIRE(x == 10);
}
# set(CMAKE_CXX_COMPILER /usr/bin/g++-15)
set(CMAKE_CXX_COMPILER /usr/bin/clang++-19)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457")
set(CMAKE_CXX_MODULE_STD 1)

project(autograding_project VERSION 0.1.0 LANGUAGES CXX)

add_library(bloco1)
target_sources(bloco1  PUBLIC  FILE_SET CXX_MODULES FILES   src/bloco1.cppm)

#==========
#  TESTS
#----------
Include(FetchContent)
#
FetchContent_Declare(Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v3.8.0)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
#
add_executable(app_teste1 tests/teste1.cpp)
target_link_libraries(app_teste1 PRIVATE bloco1 Catch2::Catch2WithMain)

Errors on GCC 15 happen on import std line:

:
[build] /usr/include/c++/15/type_traits: In substitution of ‘template<class _TypeIdentity, class _NestedType> constexpr typename std::__or_<std::is_reference<_NestedType>, std::is_function<_NestedType>, std::is_void<_NestedType>, std::__is_array_unknown_bounds<_NestedType> >::type std::__is_complete_or_unbounded(_TypeIdentity) [with _TypeIdentity = std::__type_identity<std::allocator<int> >; _NestedType = std::allocator<int>]’:
[build] /usr/include/c++/15/type_traits:1246:52:   required from ‘struct std::is_nothrow_default_constructible<std::allocator<int> >’
[build]  1246 |       static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
[build]       |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
[build] PATH/teste1.cpp:11:20:   recursively required from ‘constexpr std::vector<_Tp, _Alloc>::vector() [with _Tp = int; _Alloc = std::allocator<int>]’
[build]    11 |   std::vector<int> v; // ok on clang, not for gcc 15 

The only solution for GCC 15 is to not use import std on my end, like this:

// adapted src/bloco1.cppm for GCC (module file)
module;
#include <string_view>
export module bloco1;
// import std;
export auto exercicioA(std::string_view s) {
  return 1;
}

My intention here is to only give some feedback to a project I like a lot. Thanks for the feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants