Skip to content

Conversation

@apolukhin
Copy link
Member

@apolukhin apolukhin commented Apr 7, 2025

Changes:

  1. #include <boost/pfr... is now implicitly changed into import boost.pfr if the modules are supported
  2. CI now tests modules on Ubuntu 24.04
  3. Renamed module to boost.pfr
  4. CMakeLists.txt now uses modules for Boost::pfr target if modules are supported
  5. All the library internals now have unconditional module level linkage. 1) allows users to mix #include <boost/pfr... and import boost.pfr in user code without ODR-violations.

Significant differences from https://anarthal.github.io/cppblog/modules3:

  • PFR uses a separate macro for import std; / includes. This allows using boost.pfr module in C++20 and even without usable std module.

@apolukhin
Copy link
Member Author

@anarthal I'd appreciate comments on this PR on modules (as you proposed at boostorg/core#191 (comment))

@coveralls
Copy link

coveralls commented Apr 7, 2025

Pull Request Test Coverage Report for Build 14447165733

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 100.0%

Totals Coverage Status
Change from base Build 12675824451: 0.0%
Covered Lines: 407
Relevant Lines: 407

💛 - Coveralls

@anarthal
Copy link
Contributor

anarthal commented Apr 8, 2025

Note that 2) seems to make it safe to change all the library internals to have module level linkage and still allows users to mix #include <boost/pfr... and import boost.pfr in user code without ODR-violations. I'll try to implement it next time

Have you tried MSVC? My experience is that clang allows for almost anything, with MSVC being much more restrictive.

./b2 -d0 headers
./b2 variant=debug tools/inspect/build
- name: Run modules tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd strongly advise to add a job covering modules under MSVC, since it's much more restrictive than clang. You can have a look at my mp11/charconv proposal for some inspiration.

Copy link
Contributor

@anarthal anarthal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What problems did you encounter in the tests that you commented out? I might be able to help with them.


#include <boost/pfr/tuple_size.hpp>

#if defined(BOOST_USE_STD_MODULE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since PFR is the only library supporting this atm, I'd consider renaming the macro to BOOST_PFR_USE_STD_MODULE


#if defined(BOOST_USE_STD_MODULE)
import std;
#else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd consider replacing this ifdef + includes by either a file like this:

https://github.com/anarthal/mp11/blob/feature/cxx20-modules/include/boost/mp11/detail/std/tuple.hpp

or follow this approach: boostorg/charconv#255 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather leave it as is for now. Maybe I'll change it in the next PR

cd ../boost-root/libs/pfr
mkdir build_module
cd build_module
cmake -DBOOST_USE_MODULES=1 -DBUILD_TESTING=1 -GNinja -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_CXX_FLAGS=-I../../../ ..
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to support building with and without import std, you need to add a CI build for each config/compiler pair. I think you're missing an include in the import std configuration.

#include <boost/pfr/detail/unsafe_declval.hpp>

#ifdef BOOST_PFR_HAS_STD_MODULE
#if defined(BOOST_USE_STD_MODULE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you will get macro definitions like __cpp_lib_is_aggregate without an include (like <version>). #include <version> is safe to use in general, and can be mixed with imports without problem, but you will need to guard it (it's C++20 and above).

Same applies for SIZE_MAX. You can replace this by (std::numeric_limits<std::size_t>::max)() and avoid the problem.

#if defined(BOOST_USE_STD_MODULE)
import std;
#else
#include <climits> // CHAR_BIT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't get CHAR_BIT with this structure. You will need to include the header even in modular builds. IIRC #include <climits> caused problems under MSVC when mixed with imports, but #include <limits.h> is okay.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just use std::numeric_limits<unsigned char>::digits instead

#include <boost/pfr/traits_fwd.hpp>

#ifdef BOOST_PFR_HAS_STD_MODULE
#if defined(BOOST_USE_STD_MODULE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__cpp_lib_is_aggregate

#endif

#ifndef BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE
# if defined(BOOST_USE_MODULES)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can attempt to include <version> to get __GLIBCXX__? Although that makes the include heavier.

CMakeLists.txt Outdated
FILE_SET modules_public TYPE CXX_MODULES FILES
${CMAKE_CURRENT_LIST_DIR}/modules/pfr.cppm
)
if (CXX_MODULE_STD)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this works like this. If I understood CXX_MODULE_STD correctly, this is a property of targets, not a global variable indicating whether import std is supported or not.

To match the rest of Boost, I'd make import std the default when BOOST_USE_MODULES is defined. Then, if the user sets some CMake option (like BOOST_PFR_DISABLE_IMPORT_STD) fall back to includes. Since stdlib is the main source of compile times, I'd expect most of the people using modules because of compile times to also use import std.

You need to test these workflows to avoid this kind of errors. We commonly test installing the project and consuming it with find_package, and consuming the project directly via add_subdirectory. Have a look at the mp11 CI workflows for examples.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I have to check the CMAKE_CXX_COMPILER_IMPORT_STD variable instead of the CXX_MODULE_STD property

CMakeLists.txt Outdated
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.28.0" AND BUILD_MODULE)
add_subdirectory(module)
target_compile_definitions(boost_pfr PUBLIC BOOST_USE_MODULES)
target_include_directories(boost_pfr PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be just include, rather than ${CMAKE_CURRENT_LIST_DIR}/include


int main() {
#if BOOST_PFR_USE_CPP17
#if BOOST_PFR_USE_CPP17 && !defined(BOOST_USE_MODULES) // TODO: fix for BOOST_USE_MODULES
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the problem with this one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses boost::pfr::detail that is an implentation detail and is not exported from module

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I see. I've workarounded these in Charconv by just using includes. It may make sense to leave them as you did, too. I tried creating an internal-only module that exported the detail functions to test, but the approach doesn't map very well to CMake, and it tends to either end up creating an extra static library that ends up getting installed (not good) or just refusing to build at all.

@apolukhin apolukhin changed the title Rewrite modules to the new recommended Boost practice Rewrite modules following the new recommended Boost practice Apr 16, 2025
@apolukhin apolukhin merged commit 5a48d74 into develop Apr 16, 2025
12 checks passed
@apolukhin apolukhin deleted the modules-take-two branch April 16, 2025 06:16
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

Successfully merging this pull request may close these issues.

4 participants