diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e9957f8..fb766fc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,18 +18,52 @@ jobs: matrix: include: - toolset: gcc-14 # Do not remove! It is the only toolset that tests CMake tests down below + compiler: g++-14 cxxstd: "03,11,14,17,20" os: ubuntu-24.04 - toolset: gcc-12 + compiler: g++-12 cxxstd: "03,11,14,17,2a" os: ubuntu-22.04 cxxflags: "cxxflags=--coverage -fsanitize=address,leak,undefined -fno-sanitize-recover=undefined" linkflags: "linkflags=--coverage -lasan -lubsan" gcov_tool: "gcov-12" - - toolset: clang + - toolset: clang-14 compiler: clang++-14 cxxstd: "03,11,14,17,2a" os: ubuntu-22.04 + - toolset: clang-19 + compiler: clang++-19 + cxxstd: "20" + cmake_cxx_standard: "" + os: ubuntu-24.04 + install: &cxx19 + - clang-19 + - llvm-19 + - libclang-rt-19-dev + - libc++-19-dev + - libc++abi-19-dev + - clang-tools-19 + - toolset: clang-19 + compiler: clang++-19 + cxxstd: "20" + cmake_cxx_standard: 20 + os: ubuntu-24.04 + install: *cxx19 + - toolset: clang-19 + compiler: clang++-19 + cxxstd: "23" + cmake_cxx_standard: 23 + os: ubuntu-24.04 + install: *cxx19 + - toolset: clang-19 + compiler: clang++-19 + cxxstd: "26" + cmake_cxx_standard: 26 + os: ubuntu-24.04 + install: *cxx19 + + name: ${{matrix.os}}, ${{matrix.toolset}}, ${{matrix.cxxstd}} runs-on: ${{matrix.os}} @@ -38,7 +72,7 @@ jobs: - name: Install packages if: matrix.install - run: sudo apt install ${{matrix.install}} + run: sudo apt install -y ${{join(matrix.install, ' ')}} - name: Setup Boost run: | @@ -65,16 +99,50 @@ jobs: ./b2 -j4 variant=debug tools/inspect - name: Run CMake tests - if: ${{matrix.toolset == 'gcc-14'}} + if: ${{matrix.toolset == 'gcc-14' || matrix.toolset == 'clang-19'}} run: | cd ../boost-root/ - mkdir __build - cd __build - cmake -DBUILD_TESTING=1 -DBOOST_INCLUDE_LIBRARIES=conversion -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER=gcc-14 .. - cmake --build . --target tests - ctest --output-on-failure --no-tests=error - cd .. + cmake -S . -B __build \ + -DBUILD_TESTING=1 \ + -DBOOST_INCLUDE_LIBRARIES=conversion \ + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ + ${{ matrix.cmake_cxx_standard != '' && format('-DCMAKE_CXX_STANDARD={0}', matrix.cmake_cxx_standard) || '' }} + cmake --build __build --target tests + ctest --test-dir __build --output-on-failure --no-tests=error rm -rf __build + + - name: Run modules tests + if: ${{matrix.toolset == 'clang-19'}} + run: | + cd ../boost-root/libs/$LIBRARY + cmake -S test/cmake_subdir_test \ + -B build_module \ + -GNinja \ + -DBOOST_USE_MODULES=1 \ + -DBUILD_TESTING=1 \ + ${{ matrix.cmake_cxx_standard != '' && format('-DCMAKE_CXX_STANDARD={0}', matrix.cmake_cxx_standard) || '' }} \ + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ + -DCMAKE_CXX_MODULE_STD=ON \ + -DCMAKE_EXPERIMENTAL_CXX_IMPORT_STD=0e5b6991-d74f-4b3d-a41c-cf096e0b2508 \ + -DCMAKE_CXX_FLAGS=-stdlib=libc++ + cmake --build build_module + ctest --test-dir build_module -VV + rm -rf build_module + + - name: Run modules tests without 'import std;' + if: ${{matrix.toolset == 'clang-19'}} + run: | + cd ../boost-root/libs/$LIBRARY + cmake -S test/cmake_subdir_test \ + -B build_module \ + -GNinja \ + -DBOOST_USE_MODULES=1 \ + -DBUILD_TESTING=1 \ + ${{ matrix.cmake_cxx_standard != '' && format('-DCMAKE_CXX_STANDARD={0}', matrix.cmake_cxx_standard) || '' }} \ + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} + cmake --build build_module + ctest --test-dir build_module -VV + rm -rf build_module - name: Run tests run: | @@ -95,7 +163,7 @@ jobs: echo "$LCOV --directory ../boost-root/bin.v2/libs/$LIBRARY/ --base-directory `pwd`/libs/$LIBRARY/test --capture --output-file $GITHUB_WORKSPACE/coveralls/coverage.info" $LCOV --directory ../boost-root/bin.v2/libs/$LIBRARY/ --base-directory ../boost-root/ --capture --output-file $GITHUB_WORKSPACE/coveralls/coverage.info - $LCOV --remove $GITHUB_WORKSPACE/coveralls/coverage.info "/usr*" "*/$LIBRARY/test/*" ${{matrix.ignore_coverage}} "*/$LIBRARY/tests/*" "*/$LIBRARY/examples/*" "*/$LIBRARY/example/*" -o $GITHUB_WORKSPACE/coveralls/coverage.info + $LCOV --remove $GITHUB_WORKSPACE/coveralls/coverage.info "/usr*" "*/$LIBRARY/test/*" "*/$LIBRARY/tests/*" "*/$LIBRARY/examples/*" "*/$LIBRARY/example/*" -o $GITHUB_WORKSPACE/coveralls/coverage.info cd ../boost-root OTHER_LIBS=`grep "submodule .*" .gitmodules | sed 's/\[submodule\ "\(.*\)"\]/"\*\/boost\/\1\.hpp" "\*\/boost\/\1\/\*"/g'| sed "/\"\*\/boost\/$LIBRARY\/\*\"/d" | sed ':a;N;$!ba;s/\n/ /g'` diff --git a/CMakeLists.txt b/CMakeLists.txt index d8da837f..63aaad86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,19 +2,43 @@ # Distributed under the Boost Software License, Version 1.0. # See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -cmake_minimum_required( VERSION 3.5...4.20 ) -project( boost_conversion VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX ) +cmake_minimum_required(VERSION 3.5...4.20) +project(boost_conversion VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) -add_library( boost_conversion INTERFACE ) -add_library( Boost::conversion ALIAS boost_conversion ) +if (BOOST_USE_MODULES) + add_library(boost_conversion) + target_sources(boost_conversion PUBLIC + FILE_SET modules_public + TYPE CXX_MODULES + BASE_DIRS modules + FILES ${CMAKE_CURRENT_LIST_DIR}/modules/boost_conversion.cppm + ) -target_include_directories( boost_conversion INTERFACE include ) + target_compile_features(boost_conversion PUBLIC cxx_std_20) + target_compile_definitions(boost_conversion PUBLIC BOOST_USE_MODULES) + get_property(__standard TARGET boost_conversion PROPERTY CXX_STANDARD) + if (__standard IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD) + target_compile_definitions(boost_conversion PRIVATE BOOST_CONVERSION_USE_STD_MODULE) + message(STATUS "Using `import std;`") + else() + message(STATUS "`import std;` is not available") + endif() + unset(__standard) + set(__scope PUBLIC) +else() + add_library(boost_conversion INTERFACE) + set(__scope INTERFACE) +endif() + +target_include_directories(boost_conversion ${__scope} include) +add_library(Boost::conversion ALIAS boost_conversion) -target_link_libraries( boost_conversion - INTERFACE +target_link_libraries(boost_conversion + ${__scope} Boost::assert Boost::config Boost::throw_exception + Boost::smart_ptr ) if(BUILD_TESTING) diff --git a/include/boost/conversion/detail/config.hpp b/include/boost/conversion/detail/config.hpp new file mode 100644 index 00000000..8b2cf2d2 --- /dev/null +++ b/include/boost/conversion/detail/config.hpp @@ -0,0 +1,28 @@ +// Copyright (c) 2016-2026 Antony Polukhin +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_CONVERSION_DETAIL_CONFIG_HPP +#define BOOST_CONVERSION_DETAIL_CONFIG_HPP + +#if !defined(BOOST_CONVERSION_INTERFACE_UNIT) +# include +# ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +# endif +#endif + +#ifdef BOOST_CONVERSION_INTERFACE_UNIT +# define BOOST_CONVERSION_BEGIN_MODULE_EXPORT export { +# define BOOST_CONVERSION_END_MODULE_EXPORT } +#else +# define BOOST_CONVERSION_BEGIN_MODULE_EXPORT +# define BOOST_CONVERSION_END_MODULE_EXPORT +#endif + +#if defined(BOOST_USE_MODULES) && !defined(BOOST_CONVERSION_INTERFACE_UNIT) +import boost.conversion; +#endif + +#endif diff --git a/include/boost/implicit_cast.hpp b/include/boost/implicit_cast.hpp index 678195b0..8e719662 100644 --- a/include/boost/implicit_cast.hpp +++ b/include/boost/implicit_cast.hpp @@ -6,7 +6,10 @@ #ifndef BOOST_IMPLICIT_CAST_DWA200356_HPP #define BOOST_IMPLICIT_CAST_DWA200356_HPP -#include +#include + +#if !defined(BOOST_USE_MODULES) || defined(BOOST_CONVERSION_INTERFACE_UNIT) + #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif @@ -22,6 +25,8 @@ template struct icast_identity } // namespace detail +BOOST_CONVERSION_BEGIN_MODULE_EXPORT + // implementation originally suggested by C. Green in // http://lists.boost.org/MailArchives/boost/msg00886.php @@ -32,7 +37,10 @@ constexpr T implicit_cast (typename boost::detail::icast_identity::type x) { return x; } +BOOST_CONVERSION_END_MODULE_EXPORT + } // namespace boost +#endif // !defined(BOOST_USE_MODULES) || defined(BOOST_CONVERSION_INTERFACE_UNIT) #endif // BOOST_IMPLICIT_CAST_DWA200356_HPP diff --git a/include/boost/polymorphic_cast.hpp b/include/boost/polymorphic_cast.hpp index f6162aa3..990757cd 100644 --- a/include/boost/polymorphic_cast.hpp +++ b/include/boost/polymorphic_cast.hpp @@ -49,18 +49,22 @@ #ifndef BOOST_POLYMORPHIC_CAST_HPP #define BOOST_POLYMORPHIC_CAST_HPP -#include +#include + +#if !defined(BOOST_USE_MODULES) || defined(BOOST_CONVERSION_INTERFACE_UNIT) #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif +#if !defined(BOOST_CONVERSION_INTERFACE_UNIT) # include # include # include // std::addressof # include # include +#endif #if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L #define BOOST_CONVERSION_IMPL_CONSTEXPR_DYN_CAST constexpr @@ -68,6 +72,8 @@ #define BOOST_CONVERSION_IMPL_CONSTEXPR_DYN_CAST inline #endif +BOOST_CONVERSION_BEGIN_MODULE_EXPORT + namespace boost { // See the documentation for descriptions of how to choose between @@ -125,8 +131,12 @@ namespace boost ); } +BOOST_CONVERSION_END_MODULE_EXPORT + } // namespace boost #undef BOOST_CONVERSION_IMPL_CONSTEXPR_DYN_CAST +#endif // !defined(BOOST_USE_MODULES) || defined(BOOST_CONVERSION_INTERFACE_UNIT) + #endif // BOOST_POLYMORPHIC_CAST_HPP diff --git a/include/boost/polymorphic_pointer_cast.hpp b/include/boost/polymorphic_pointer_cast.hpp index 6253294c..c7c6437f 100644 --- a/include/boost/polymorphic_pointer_cast.hpp +++ b/include/boost/polymorphic_pointer_cast.hpp @@ -10,16 +10,21 @@ #ifndef BOOST_CONVERSION_POLYMORPHIC_POINTER_CAST_HPP #define BOOST_CONVERSION_POLYMORPHIC_POINTER_CAST_HPP -#include +#include + +#if !defined(BOOST_USE_MODULES) || defined(BOOST_CONVERSION_INTERFACE_UNIT) + #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif +#if !defined(BOOST_CONVERSION_INTERFACE_UNIT) # include # include # include +#endif - +BOOST_CONVERSION_BEGIN_MODULE_EXPORT namespace boost { // See the documentation for descriptions of how to choose between @@ -56,5 +61,8 @@ namespace boost } } // namespace boost +BOOST_CONVERSION_END_MODULE_EXPORT + +#endif // !defined(BOOST_USE_MODULES) || defined(BOOST_CONVERSION_INTERFACE_UNIT) #endif // BOOST_CONVERSION_POLYMORPHIC_POINTER_CAST_HPP diff --git a/modules/boost_conversion.cppm b/modules/boost_conversion.cppm new file mode 100644 index 00000000..35667442 --- /dev/null +++ b/modules/boost_conversion.cppm @@ -0,0 +1,33 @@ +// Copyright (c) 2016-2026 Antony Polukhin +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +module; + +#include +#include +#include +#include + +#ifndef BOOST_CONVERSION_USE_STD_MODULE +#include +#include +#include +#endif + +#define BOOST_CONVERSION_INTERFACE_UNIT + +export module boost.conversion; + +#ifdef BOOST_CONVERSION_USE_STD_MODULE +import std; +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Winclude-angled-in-module-purview" +#endif + +#include +#include +#include diff --git a/modules/usage_sample.cpp b/modules/usage_sample.cpp new file mode 100644 index 00000000..f2958d2f --- /dev/null +++ b/modules/usage_sample.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2016-2026 Antony Polukhin +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +import boost.conversion; + +namespace { + +struct Base { + virtual ~Base() = default; + virtual std::string name() const { + return "base"; + } +}; + +struct Derived : Base { + std::string name() const override { + return "derived"; + } +}; + +} + +int main() { + std::cerr << boost::implicit_cast(42) << '\n'; + + std::unique_ptr base = std::make_unique(); + std::cerr << boost::polymorphic_downcast(*base).name() << '\n'; + + return 0; +} diff --git a/modules/usage_test_mu1.cpp b/modules/usage_test_mu1.cpp new file mode 100644 index 00000000..b1fde734 --- /dev/null +++ b/modules/usage_test_mu1.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016-2026 Antony Polukhin +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +long make_me_long(int x); + +int main() { + std::cout << make_me_long(42) << '\n'; + std::cout << boost::implicit_cast(42) << '\n'; +} diff --git a/modules/usage_test_mu2.cpp b/modules/usage_test_mu2.cpp new file mode 100644 index 00000000..0b257d38 --- /dev/null +++ b/modules/usage_test_mu2.cpp @@ -0,0 +1,10 @@ +// Copyright (c) 2016-2026 Antony Polukhin +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +import boost.conversion; + +long make_me_long(int x) { + return boost::implicit_cast(x); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 95664d0b..3e1bfdfe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,16 +2,19 @@ # Distributed under the Boost Software License, Version 1.0. # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt -include(BoostTest OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) - -if(NOT HAVE_BOOST_TEST) - return() +if(NOT TARGET tests) + add_custom_target(tests) endif() -set(BOOST_TEST_LINK_LIBRARIES Boost::conversion Boost::core) - -boost_test(TYPE run SOURCES cast_test.cpp) -boost_test(TYPE run SOURCES implicit_cast.cpp) -boost_test(TYPE compile-fail SOURCES implicit_cast_fail.cpp) -boost_test(TYPE compile-fail SOURCES implicit_cast_fail2.cpp) -boost_test(TYPE run SOURCES polymorphic_cast_test.cpp LINK_LIBRARIES Boost::smart_ptr) +set(TEST_SOURCES + cast_test.cpp + implicit_cast.cpp + polymorphic_cast_test.cpp +) +foreach (testsourcefile ${TEST_SOURCES}) + get_filename_component(testname ${testsourcefile} NAME_WE) + add_executable(conversion_${testname} ${testsourcefile}) + target_link_libraries(conversion_${testname} Boost::conversion Boost::core Boost::smart_ptr) + add_test(NAME conversion_${testname} COMMAND conversion_${testname}) + add_dependencies(tests conversion_${testname}) +endforeach() diff --git a/test/cmake_subdir_test/CMakeLists.txt b/test/cmake_subdir_test/CMakeLists.txt new file mode 100644 index 00000000..27728e69 --- /dev/null +++ b/test/cmake_subdir_test/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2016-2026 Antony Polukhin +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +cmake_minimum_required(VERSION 3.5...4.2) + +project(conversion_subdir_test LANGUAGES CXX) + +add_subdirectory(../../../assert boostorg/assert) +add_subdirectory(../../../core boostorg/core) +add_subdirectory(../../../config boostorg/config) +add_subdirectory(../../../smart_ptr boostorg/smart_ptr) +add_subdirectory(../../../throw_exception boostorg/throw_exception) + + +enable_testing() +add_subdirectory(../../ boostorg/conversion) + +if (BOOST_USE_MODULES) + add_executable(boost_conversion_module_usage ../../modules/usage_sample.cpp) + target_link_libraries(boost_conversion_module_usage PRIVATE Boost::conversion) + add_test(NAME boost_conversion_module_usage COMMAND boost_conversion_module_usage) + + # Make sure that mixing includes and imports is fine for different TU + add_executable(boost_conversion_module_usage_mu ../../modules/usage_test_mu1.cpp ../../modules/usage_test_mu2.cpp) + target_link_libraries(boost_conversion_module_usage_mu PRIVATE Boost::conversion) + add_test(NAME boost_conversion_module_usage_mu COMMAND boost_conversion_module_usage_mu) +endif() diff --git a/test/implicit_cast.cpp b/test/implicit_cast.cpp index 92f21607..a7e27d12 100644 --- a/test/implicit_cast.cpp +++ b/test/implicit_cast.cpp @@ -3,10 +3,11 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include #include #include +#include + using boost::implicit_cast; using boost::type; diff --git a/test/polymorphic_cast_test.cpp b/test/polymorphic_cast_test.cpp index 498f36fd..48f74f33 100644 --- a/test/polymorphic_cast_test.cpp +++ b/test/polymorphic_cast_test.cpp @@ -14,13 +14,14 @@ // #define BOOST_ENABLE_ASSERT_HANDLER -#include -#include #include #include #include #include -#include + +#include +#include + #include static bool expect_assertion = false; @@ -42,7 +43,7 @@ void boost::assertion_failed( char const * expr, char const * function, char con { BOOST_ERROR( "unexpected assertion" ); - BOOST_LIGHTWEIGHT_TEST_OSTREAM + std::cerr << file << "(" << line << "): assertion '" << expr << "' failed in function '" << function << "'" << std::endl; } @@ -370,14 +371,19 @@ int main() test_polymorphic_pointer_downcast_intrusive(); test_polymorphic_cast_fail(); test_polymorphic_pointer_cast_fail(); + +// NOTE: this tests depend on BOOST_ASSERT macro implementation substitution +// implemented via BOOST_ENABLE_ASSERT_HANDLER macro. +// Such mechanism is not possible with modules because +// module import in TU do not inherit defined TU's macroses. +#ifndef BOOST_USE_MODULES test_polymorphic_downcast_fail(); test_polymorphic_pointer_downcast_builtin_fail(); test_polymorphic_pointer_downcast_boost_shared_fail(); test_polymorphic_pointer_downcast_intrusive_fail(); - test_polymorphic_pointer_downcast_std_shared(); test_polymorphic_pointer_downcast_std_shared_fail(); - +#endif return boost::report_errors(); }