2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-19 04:22:13 +00:00

Initial support for C++20 modules (#177)

This commit is contained in:
Antony Polukhin
2024-08-15 10:59:07 +03:00
committed by GitHub
parent dec9341546
commit 60391652fa
39 changed files with 361 additions and 6 deletions

View File

@@ -1,5 +1,6 @@
# Generated by `boostdep --cmake pfr`
# Copyright 2020 Peter Dimov
# Copyright (c) 2016-2024 Antony Polukhin
#
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
@@ -12,9 +13,11 @@ add_library(Boost::pfr ALIAS boost_pfr)
target_include_directories(boost_pfr INTERFACE include)
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.28.0" AND BUILD_MODULE)
add_subdirectory(module)
endif()
if (BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
endif()

View File

@@ -549,6 +549,40 @@ parameters provided to `BOOST_PFR_CORE_NAME_PARSING` macro [*and] the initial ou
[endsect]
[section PFR as a C++20 module]
[caution C++20 PFR module support is on early stage, targets and flags may change in the future]
If using CMake of version 3.28.0 or higher define CMake option `-DBUILD_MODULE=1`
to make the `Boost::pfr_module` and `Boost::pfr_module_migration` libraries
available. With `Boost::pfr_module` C++20 module Boost.PFR could be used:
[import ../module/usage_sample.cpp]
[pfr_module_example]
The `Boost::pfr_module_migration` CMake target gives an ability to
mix includes and imports of the PFR library in different translation units.
If not using CMake, then the module could be build manually from the
`module/pfr.cppm` file. If mixing of includes in imports is desired, additionally
define `BOOST_PFR_ATTACH_TO_GLOBAL_MODULE` preprocessor macro to attach all the
module entities to a global module and avoid ODR issues.
For manual module build the following commands could be used for clang compiler:
```
cd pfr/module
clang++ -I ../include -std=c++20 --precompile -x c++-module pfr.cppm
```
After that, the module could be used in the following way:
```
clang++ -std=c++20 -fmodule-file=pfr.pcm pfr.pcm usage_sample.cpp
```
[endsect]
[section How it works]
[h2 Fields count detection and getting references to members]

View File

@@ -145,4 +145,12 @@
#undef BOOST_PFR_NOT_SUPPORTED
#ifndef BOOST_PFR_BEGIN_MODULE_EXPORT
# define BOOST_PFR_BEGIN_MODULE_EXPORT
#endif
#ifndef BOOST_PFR_END_MODULE_EXPORT
# define BOOST_PFR_END_MODULE_EXPORT
#endif
#endif // BOOST_PFR_CONFIG_HPP

View File

@@ -29,6 +29,8 @@
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
/// \brief Returns reference or const reference to a field with index `I` in \aggregate `val`.
/// Overload taking the type `U` returns reference or const reference to a field
/// with provided type `U` in \aggregate `val` if there's only one field of such type in `val`.
@@ -260,6 +262,8 @@ constexpr detail::tie_from_structure_tuple<Elements...> tie_from_structure(Eleme
return detail::tie_from_structure_tuple<Elements...>(args...);
}
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_CORE_HPP

View File

@@ -33,6 +33,8 @@
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
/// \brief Returns name of a field with index `I` in \aggregate `T`.
///
/// \b Example:
@@ -82,6 +84,8 @@ names_as_array() noexcept {
);
}
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_CORE_NAME_HPP

View File

@@ -9,8 +9,12 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#include <utility> // metaprogramming stuff
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>
#include <boost/pfr/detail/offset_based_getter.hpp>

View File

@@ -24,8 +24,12 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#include <utility>
#endif
#include <boost/pfr/detail/offset_based_getter.hpp>
#include <boost/pfr/detail/fields_count.hpp>

View File

@@ -21,7 +21,12 @@
#include <boost/pfr/detail/sequence_tuple.hpp>
#include <boost/pfr/detail/size_t_.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits> // for std::conditional_t, std::is_reference
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -19,10 +19,15 @@
#include <boost/pfr/detail/fields_count.hpp>
#include <boost/pfr/detail/stdarray.hpp>
#include <boost/pfr/detail/fake_object.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#include <string_view>
#include <array>
#include <memory> // for std::addressof
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -9,8 +9,12 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <functional>
#include <type_traits>
#endif
namespace boost { namespace pfr { namespace detail {
///////////////////// `value` is true if Detector<Tleft, Tright> does not compile (SFINAE)

View File

@@ -12,9 +12,13 @@
#include <boost/pfr/detail/size_t_.hpp>
#include <boost/pfr/detail/unsafe_declval.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <climits> // CHAR_BIT
#include <type_traits>
#include <utility> // metaprogramming stuff
#endif
#ifdef __clang__
# pragma clang diagnostic push

View File

@@ -9,7 +9,11 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <utility> // metaprogramming stuff
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>
#include <boost/pfr/detail/rvalue_t.hpp>

View File

@@ -9,8 +9,12 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <functional>
#include <cstdint>
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>

View File

@@ -10,6 +10,10 @@
#include <boost/pfr/detail/config.hpp>
#include <boost/pfr/detail/sequence_tuple.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <iosfwd> // stream operators
#include <iomanip>
@@ -19,6 +23,8 @@
# endif
#endif
#endif
namespace boost { namespace pfr { namespace detail {
inline auto quoted_helper(const std::string& s) noexcept {

View File

@@ -9,7 +9,12 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <utility> // metaprogramming stuff
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>
#include <boost/pfr/detail/rvalue_t.hpp>
#include <boost/pfr/detail/make_integer_sequence.hpp>

View File

@@ -10,9 +10,13 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#include <utility>
#include <cstddef>
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -10,9 +10,14 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#include <utility>
#include <memory> // std::addressof
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>
#include <boost/pfr/detail/rvalue_t.hpp>
#include <boost/pfr/detail/size_t_.hpp>

View File

@@ -10,7 +10,11 @@
#include <boost/pfr/detail/config.hpp>
#include <boost/pfr/traits_fwd.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits> // for std::is_aggregate
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -7,8 +7,12 @@
#define BOOST_PFR_DETAIL_RVALUE_T_HPP
#pragma once
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#include <utility> // std::enable_if_t
#endif
// This header provides aliases rvalue_t and lvalue_t.
//

View File

@@ -10,8 +10,12 @@
#include <boost/pfr/detail/config.hpp>
#include <boost/pfr/detail/make_integer_sequence.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <utility> // metaprogramming stuff
#include <cstddef> // std::size_t
#endif
///////////////////// Tuple that holds its values in the supplied order
namespace boost { namespace pfr { namespace detail { namespace sequence_tuple {

View File

@@ -9,7 +9,11 @@
#include <boost/pfr/detail/config.hpp>
#include <cstddef> // metaprogramming stuff
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <cstddef>
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -9,10 +9,14 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <utility> // metaprogramming stuff
#include <array>
#include <type_traits> // for std::common_type_t
#include <cstddef>
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>

View File

@@ -9,8 +9,12 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <utility> // metaprogramming stuff
#include <tuple>
#endif
#include <boost/pfr/detail/sequence_tuple.hpp>

View File

@@ -16,7 +16,11 @@
#include <boost/pfr/tuple_size.hpp>
#include <boost/pfr/detail/make_integer_sequence.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <tuple>
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -9,7 +9,11 @@
#include <boost/pfr/detail/config.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits>
#endif
namespace boost { namespace pfr { namespace detail {

View File

@@ -35,6 +35,8 @@
/// \b Synopsis:
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
///////////////////// Comparisons
/// \brief std::equal_to like comparator that returns \forcedlink{eq}(x, y)
@@ -216,6 +218,8 @@ template <class T> struct hash {
}
};
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_FUNCTORS_HPP

View File

@@ -67,6 +67,8 @@ struct io_impl {
T value;
};
BOOST_PFR_BEGIN_MODULE_EXPORT
template <class Char, class Traits, class T>
enable_not_ostreamable_t<std::basic_ostream<Char, Traits>, T> operator<<(std::basic_ostream<Char, Traits>& out, io_impl<T>&& x) {
return out << boost::pfr::io_fields(std::forward<T>(x.value));
@@ -87,8 +89,12 @@ enable_istreamable_t<std::basic_istream<Char, Traits>, T> operator>>(std::basic_
return in >> x.value;
}
BOOST_PFR_END_MODULE_EXPORT
} // namespace detail
BOOST_PFR_BEGIN_MODULE_EXPORT
/// IO manipulator to read/write \aggregate `value` using its IO stream operators or using \forcedlink{io_fields} if operators are not available.
///
/// \b Example:
@@ -108,6 +114,8 @@ auto io(T&& value) noexcept {
return detail::io_impl<T>{std::forward<T>(value)};
}
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_IO_HPP

View File

@@ -118,6 +118,8 @@ std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& i
return in;
}
BOOST_PFR_BEGIN_MODULE_EXPORT
template <class Char, class Traits, class T>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, io_fields_impl<const T&>&& ) {
static_assert(sizeof(T) && false, "====================> Boost.PFR: Attempt to use istream operator on a boost::pfr::io_fields wrapped type T with const qualifier.");
@@ -130,8 +132,12 @@ std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& i
return in;
}
BOOST_PFR_END_MODULE_EXPORT
} // namespace detail
BOOST_PFR_BEGIN_MODULE_EXPORT
/// IO manipulator to read/write \aggregate `value` field-by-field.
///
/// \b Example:
@@ -159,6 +165,8 @@ auto io_fields(T&& value) noexcept {
return detail::io_fields_impl<T>{std::forward<T>(value)};
}
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_IO_FIELDS_HPP

View File

@@ -77,6 +77,7 @@ namespace detail {
>;
} // namespace detail
BOOST_PFR_BEGIN_MODULE_EXPORT
/// \brief Compares lhs and rhs for equality using their own comparison and conversion operators; if no operators available returns \forcedlink{eq_fields}(lhs, rhs).
///
@@ -182,6 +183,8 @@ constexpr detail::enable_hashable_t<T> hash_value(const T& value) {
return std::hash<T>{}(value);
}
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_OPS_HPP

View File

@@ -33,6 +33,8 @@
/// \b Synopsis:
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
/// Does a field-by-field equality comparison.
///
/// \returns `L == R && tuple_size_v<T> == tuple_size_v<U>`, where `L` and
@@ -122,6 +124,9 @@ namespace boost { namespace pfr {
return result;
#endif
}
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_OPS_HPP

View File

@@ -19,6 +19,8 @@
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
/// Has a static const member variable `value` when it is known that type T can or can't be reflected using Boost.PFR; otherwise, there is no member variable.
/// Every user may (and in some difficult cases - should) specialize is_reflectable on his own.
///
@@ -54,6 +56,8 @@ using is_implicitly_reflectable = std::integral_constant< bool, boost::pfr::deta
template<class T, class WhatFor>
constexpr bool is_implicitly_reflectable_v = is_implicitly_reflectable<T, WhatFor>::value;
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_TRAITS_HPP

View File

@@ -11,9 +11,13 @@
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
template<class T, class WhatFor>
struct is_reflectable;
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_DETAIL_TRAITS_FWD_HPP

View File

@@ -22,6 +22,8 @@
/// \b Synopsis:
namespace boost { namespace pfr {
BOOST_PFR_BEGIN_MODULE_EXPORT
/// Has a static const member variable `value` that contains fields count in a T.
/// Works for any T that satisfies \aggregate.
///
@@ -43,6 +45,8 @@ using tuple_size = detail::size_t_< boost::pfr::detail::fields_count<T>() >;
template <class T>
constexpr std::size_t tuple_size_v = tuple_size<T>::value;
BOOST_PFR_END_MODULE_EXPORT
}} // namespace boost::pfr
#endif // BOOST_PFR_TUPLE_SIZE_HPP

View File

@@ -38,7 +38,12 @@ PROLOGUE = """// Copyright (c) 2016-2024 Antony Polukhin
#include <boost/pfr/detail/sequence_tuple.hpp>
#include <boost/pfr/detail/size_t_.hpp>
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#else
#include <type_traits> // for std::conditional_t, std::is_reference
#endif
namespace boost { namespace pfr { namespace detail {

37
module/CMakeLists.txt Normal file
View File

@@ -0,0 +1,37 @@
# Copyright (c) 2016-2024 Antony Polukhin
#
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
cmake_minimum_required(VERSION 3.28)
function (_add_boost_pfr_module_impl NAME)
add_library(${NAME})
target_compile_features(${NAME} PUBLIC cxx_std_20)
target_sources(${NAME} PUBLIC
FILE_SET modules_public TYPE CXX_MODULES FILES
${CMAKE_CURRENT_LIST_DIR}/pfr.cppm
)
endfunction()
function (add_boost_pfr_module NAME)
_add_boost_pfr_module_impl(${NAME})
target_include_directories(${NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../include)
_add_boost_pfr_module_impl(${NAME}_migration)
target_include_directories(${NAME}_migration PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../include)
target_compile_definitions(${NAME}_migration PRIVATE BOOST_PFR_ATTACH_TO_GLOBAL_MODULE)
endfunction()
add_boost_pfr_module(boost_pfr_module)
add_library(Boost::pfr_module ALIAS boost_pfr_module)
add_library(Boost::pfr_module_migration ALIAS boost_pfr_module_migration)
if (BUILD_TESTING)
add_executable(boost_pfr_module_usage usage_sample.cpp)
target_link_libraries(boost_pfr_module_usage PRIVATE Boost::pfr_module)
# Make sure that mixing includes and imports is fine for different TU
add_executable(boost_pfr_module_usage_mu usage_test_mu1.cpp usage_test_mu2.cpp)
target_link_libraries(boost_pfr_module_usage_mu PRIVATE Boost::pfr_module_migration)
endif()

44
module/pfr.cppm Normal file
View File

@@ -0,0 +1,44 @@
// Copyright (c) 2016-2024 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)
// To compile manually use a command like the folowing:
// clang++ -I ../include -std=c++20 --precompile -x c++-module pfr.cppm
#define BOOST_PFR_BEGIN_MODULE_EXPORT export {
#define BOOST_PFR_END_MODULE_EXPORT }
#ifndef BOOST_PFR_HAS_STD_MODULE
module;
#include <array>
#include <cstddef>
#include <functional>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#endif
export module Boost.PFR;
#ifdef BOOST_PFR_HAS_STD_MODULE
import std;
#endif
#ifdef __clang__
# pragma clang diagnostic ignored "-Winclude-angled-in-module-purview"
#endif
#ifdef BOOST_PFR_ATTACH_TO_GLOBAL_MODULE
extern "C++" {
#include <boost/pfr.hpp>
}
#else
#include <boost/pfr.hpp>
#endif

31
module/usage_sample.cpp Normal file
View File

@@ -0,0 +1,31 @@
// Copyright (c) 2016-2024 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)
// To compile manually use a command like the folowing:
// clang++ -std=c++20 -fmodule-file=pfr.pcm pfr.pcm usage_sample.cpp
//[pfr_module_example
#include <iostream>
#include <iomanip>
#include <string>
import Boost.PFR;
struct some_person {
std::string name;
unsigned birth_year;
};
int main() {
some_person val{"Edgar Allan Poe", 1809};
std::cout << boost::pfr::get<0>(val) // No macro!
<< " was born in " << boost::pfr::get<1>(val); // Works with any aggregate!
std::cout << '\n' << boost::pfr::io(val); // Outputs: {"Edgar Allan Poe", 1809}
std::cout << "\n." << boost::pfr::get_name<0, some_person>()
<< '=' << val.name << '\n'; // Outputs: .name=Edgar Allan Poe
}
//]

27
module/usage_test_mu1.cpp Normal file
View File

@@ -0,0 +1,27 @@
// Copyright (c) 2016-2024 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)
// To compile manually use a command like the folowing:
// clang++ -std=c++20 -fmodule-file=pfr.pcm pfr.pcm usage_sample.cpp
#include <iostream>
#include <boost/pfr.hpp>
struct some_person {
std::string name;
unsigned birth_year;
};
void mu1_act() {
some_person val{"Edgar Allan Poe", 1809};
std::cout << boost::pfr::get<0>(val) // No macro!
<< " was born in " << boost::pfr::get<1>(val); // Works with any aggregate!
std::cout << '\n' << boost::pfr::io(val); // Outputs: {"Edgar Allan Poe", 1809}
std::cout << "\n." << boost::pfr::get_name<0, some_person>()
<< '=' << val.name << '\n'; // Outputs: .name=Edgar Allan Poe
}

32
module/usage_test_mu2.cpp Normal file
View File

@@ -0,0 +1,32 @@
// Copyright (c) 2016-2024 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)
// To compile manually use a command like the folowing:
// clang++ -std=c++20 -fmodule-file=pfr.pcm pfr.pcm usage_sample.cpp
#include <iostream>
#include <iomanip>
import Boost.PFR;
struct some_person {
std::string name;
unsigned birth_year;
};
void mu1_act();
int main() {
mu1_act();
some_person val{"Joseph Brodsky", 1940};
std::cout << boost::pfr::get<0>(val) // No macro!
<< " was born in " << boost::pfr::get<1>(val); // Works with any aggregate!
std::cout << '\n' << boost::pfr::io(val);
std::cout << "\n." << boost::pfr::get_name<0, some_person>()
<< '=' << val.name << '\n';
}