diff --git a/README.md b/README.md index f827167..d562fbf 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,58 @@ Outputs: my_struct has 2 fields: {"Das ist fantastisch!", 100} ``` +### Motivating Example #3 + +```c++ +#include +#include + +#include +#include +#include + +#include "boost/pfr/io.hpp" + +namespace x3 = boost::spirit::x3; + +struct ast_employee { // No BOOST_FUSION_ADAPT_STRUCT defined + int age; + std::string forename; + std::string surname; + double salary; +}; + +auto const quoted_string = x3::lexeme['"' >> +(x3::ascii::char_ - '"') >> '"']; + +x3::rule const employee = "employee"; +auto const employee_def = + x3::lit("employee") + >> '{' + >> x3::int_ >> ',' + >> quoted_string >> ',' + >> quoted_string >> ',' + >> x3::double_ + >> '}' + ; +BOOST_SPIRIT_DEFINE(employee); + +int main() { + std::string str = R"(employee{34, "Chip", "Douglas", 2500.00})"; + ast_employee emp; + x3::phrase_parse(str.begin(), + str.end(), + employee, + x3::ascii::space, + emp); + std::cout << boost::pfr::io(emp) << std::endl; +} + +``` +Outputs: +``` +(34 Chip Douglas 2500) +``` + ### Requirements and Limitations diff --git a/doc/pfr.qbk b/doc/pfr.qbk old mode 100644 new mode 100755 index ce339ab..6373e3c --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -168,6 +168,7 @@ Boost.PFR adds the following out-of-the-box functionality for aggregate initiali * member type retrieval * methods for cooperation with `std::tuple` * methods to visit each field of the structure +* trait to detect potential ability to reflect type, and ability to override trait's decision in user-side code Boost.PFR is a header only library that does not depend on Boost. You can just copy the content of the "include" folder [@https://github.com/boostorg/pfr from the Boost.PFR github] into your project, and the library will work fine. For a version of the library without `boost::` namespace see [@https://github.com/apolukhin/pfr_non_boost PFR]. @@ -467,6 +468,7 @@ By default Boost.PFR [*auto-detects your compiler abilities] and automatically d [[*BOOST_PFR_USE_LOOPHOLE*] [Define to `1` if you wish to override Boost.PFR choice and exploit [@http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 CWG 2118] for reflection. Define to `0` to override Boost.PFR choice and disable CWG 2118 usage.]] [[*BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE*] [Define to `0` if you are hit by the template instantiation depth issues with `std::make_integer_sequence` and wish to use Boost.PFR version of that metafunction. Define to `1` to override Boost.PFR detection logic. ]] [[*BOOST_PFR_HAS_GUARANTEED_COPY_ELISION*] [Define to `0` if your compiler does not implement C++17 guaranteed copy elision properly and fails to reflect aggregates with non-movable fields. Define to `1` to override Boost.PFR detection logic. ]] + [[*BOOST_PFR_ENABLE_IMPLICIT_REFLECTION*] [Define to `0` if you are hit by lots of non-effective choices made by implicitly reflection. Define to `1` to override Boost.PFR detection logic. ]] [[*BOOST_NO_PFR*] [On platforms where Boost.PFR is not supported, the boost/pfr/config.hpp header defines the BOOST_NO_PFR macro equals 1. Defining this macro as 1 before including the header disables the Boost.PFR library. ]] ] diff --git a/include/boost/pfr.hpp b/include/boost/pfr.hpp index 600b2fb..b01bce7 100644 --- a/include/boost/pfr.hpp +++ b/include/boost/pfr.hpp @@ -18,5 +18,7 @@ #include #include #include +#include +#include #endif // BOOST_PFR_HPP diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp old mode 100644 new mode 100755 index 01a6615..032e03f --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -82,6 +82,15 @@ # endif #endif +#ifndef BOOST_PFR_ENABLE_IMPLICIT_REFLECTION +# if defined(__cpp_lib_is_aggregate) +# define BOOST_PFR_ENABLE_IMPLICIT_REFLECTION 1 +# else +// There is no way to detect potential ability to be reflectable without std::is_aggregare +# define BOOST_PFR_ENABLE_IMPLICIT_REFLECTION 0 +# endif +#endif + #if defined(__has_cpp_attribute) # if __has_cpp_attribute(maybe_unused) # define BOOST_PFR_MAYBE_UNUSED [[maybe_unused]] diff --git a/include/boost/pfr/detail/config.hpp b/include/boost/pfr/detail/config.hpp old mode 100644 new mode 100755 diff --git a/include/boost/pfr/detail/possible_reflectable.hpp b/include/boost/pfr/detail/possible_reflectable.hpp new file mode 100644 index 0000000..069fafd --- /dev/null +++ b/include/boost/pfr/detail/possible_reflectable.hpp @@ -0,0 +1,37 @@ +// Copyright (c) 2022 Denis Mikhailov +// +// 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_PFR_DETAIL_POSSIBLE_REFLECTABLE_HPP +#define BOOST_PFR_DETAIL_POSSIBLE_REFLECTABLE_HPP +#pragma once + +#include +#include + +#include // for std::is_aggregate + +namespace boost { namespace pfr { namespace detail { + +///////////////////// Returns false when the type exactly wasn't be reflectable +template +constexpr decltype(is_reflectable::value) possible_reflectable(long) noexcept { + return is_reflectable::value; +} + +template +constexpr bool possible_reflectable(int) noexcept { +# if defined(__cpp_lib_is_aggregate) + using type = std::remove_cv_t; + return std::is_aggregate(); +# else + return true; +# endif +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_POSSIBLE_REFLECTABLE_HPP + + diff --git a/include/boost/pfr/traits.hpp b/include/boost/pfr/traits.hpp new file mode 100644 index 0000000..79d8f43 --- /dev/null +++ b/include/boost/pfr/traits.hpp @@ -0,0 +1,64 @@ +// Copyright (c) 2022 Denis Mikhailov +// +// 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_PFR_TRAITS_HPP +#define BOOST_PFR_TRAITS_HPP +#pragma once + +#include + +#include +#include + +/// \file boost/pfr/traits.hpp +/// Contains traits \forcedlink{is_reflectable} and \forcedlink{is_implicitly_reflectable} for detecting an ability to reflect type. +/// +/// \b Synopsis: + +namespace boost { namespace pfr { + +/// Has a static const member variable `value` when it 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. +/// +/// \b Example: +/// \code +/// namespace boost { namespace pfr { +/// template struct is_reflectable : std::false_type {}; // 'A' won't be interpreted as reflectable everywhere +/// template<> struct is_reflectable : std::false_type {}; // 'B' won't be interpreted as reflectable in only Boost Fusion +/// }} +/// \endcode +/// \note is_reflectable affects is_implicitly_reflectable, the decision made by is_reflectable has more priority than is_implicitly_reflectable, +/// because is_reflectable is more sharp than is_implicitly_reflectable +/// +template +struct is_reflectable { /* do not has 'value' because value is unknown */ }; + +// these specs can't be inherited from 'std::integral_constant< bool, boost::pfr::is_reflectable::value >', +// because it will break the sfinae-friendliness +template +struct is_reflectable : boost::pfr::is_reflectable {}; + +template +struct is_reflectable : boost::pfr::is_reflectable {}; + +template +struct is_reflectable : boost::pfr::is_reflectable {}; + +#if BOOST_PFR_ENABLE_IMPLICIT_REFLECTION +/// Checks the input type for the potential to be reflected. +/// Specialize is_reflectable if you are disagree with is_implicitly_reflectable's default decision. +template +using is_implicitly_reflectable = std::integral_constant< bool, boost::pfr::detail::possible_reflectable(1L) >; + +/// Checks the input type for the potential to be reflected. +/// Specialize is_reflectable if you are disagree with is_implicitly_reflectable_v's default decision. +template +constexpr bool is_implicitly_reflectable_v = is_implicitly_reflectable::value; +#endif + +}} // namespace boost::pfr + +#endif // BOOST_PFR_TRAITS_HPP + diff --git a/include/boost/pfr/traits_fwd.hpp b/include/boost/pfr/traits_fwd.hpp new file mode 100644 index 0000000..c015e9d --- /dev/null +++ b/include/boost/pfr/traits_fwd.hpp @@ -0,0 +1,21 @@ +// Copyright (c) 2022 Denis Mikhailov +// +// 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_PFR_DETAIL_TRAITS_FWD_HPP +#define BOOST_PFR_DETAIL_TRAITS_FWD_HPP +#pragma once + +#include + +namespace boost { namespace pfr { + +template +struct is_reflectable; + +}} // namespace boost::pfr + +#endif // BOOST_PFR_DETAIL_TRAITS_FWD_HPP + + diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 21ff053..b1e1558 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -53,6 +53,8 @@ test-suite pfr_tests [ run offset_based_getter.cpp ] + [ run can_be_as_fallback_in_the_fusion.cpp ] + [ run test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=char : test_tuple_sizes_on_chars ] [ run test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=int : test_tuple_sizes_on_ints ] [ run test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=short : test_tuple_sizes_on_shorts ] diff --git a/test/can_be_as_fallback_in_the_fusion.cpp b/test/can_be_as_fallback_in_the_fusion.cpp new file mode 100644 index 0000000..d9e5376 --- /dev/null +++ b/test/can_be_as_fallback_in_the_fusion.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2022 Denis Mikhailov +// +// 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 + +struct Aggregate {}; +using NonAggregate = int; + +template +struct is_implicitly_reflectable : std::false_type +{}; + +template<> +struct is_implicitly_reflectable : std::true_type +{}; + +template +struct tag_of_fallback { + using type = int; // unknown +}; + +template +struct tag_of_fallback::value>> +{ + using type = std::conditional_t< + is_implicitly_reflectable::value + , std::true_type + , std::false_type + >; +}; + +static_assert(tag_of_fallback::type::value == true, ""); +static_assert(tag_of_fallback::type::value == false, ""); + +int main() { } diff --git a/test/print_config.cpp b/test/print_config.cpp old mode 100644 new mode 100755 index 2c514b3..1e5c2c9 --- a/test/print_config.cpp +++ b/test/print_config.cpp @@ -13,6 +13,7 @@ int main() { << "BOOST_PFR_USE_LOOPHOLE == " << BOOST_PFR_USE_LOOPHOLE << '\n' << "BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE == " << BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE << '\n' << "BOOST_PFR_HAS_GUARANTEED_COPY_ELISION == " << BOOST_PFR_HAS_GUARANTEED_COPY_ELISION << '\n' + << "BOOST_PFR_ENABLE_IMPLICIT_REFLECTION == " << BOOST_PFR_ENABLE_IMPLICIT_REFLECTION << '\n' << "BOOST_NO_PFR == " << BOOST_NO_PFR << '\n' << "__cplusplus == " << __cplusplus << '\n' #ifdef __cpp_structured_bindings diff --git a/test/run/is_implicitly_reflectable.cpp b/test/run/is_implicitly_reflectable.cpp new file mode 100644 index 0000000..38ff816 --- /dev/null +++ b/test/run/is_implicitly_reflectable.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2022 Denis Mikhailov +// +// 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 // for std::true_type, std::false_type and std::is_aggregate + +namespace boost { namespace pfr { + struct boost_fusion_tag; + struct boost_json_tag; +}} + +struct Aggregate {}; +using Nonaggregate = int; + +#if defined(__cpp_lib_is_aggregate) +static_assert(std::is_aggregate::value && !std::is_aggregate::value, ""); +#endif + +using Reflectable = short; +struct Nonrefrectable {}; + +using ReflectableBoostFusion = short; +struct NonrefrectableBoostFusion {}; + +using ReflectableBoostJson = short; +struct NonrefrectableBoostJson {}; + +namespace boost { namespace pfr { + template struct is_reflectable : std::true_type {}; + template struct is_reflectable : std::false_type {}; + template<> struct is_reflectable : std::true_type {}; + template<> struct is_reflectable : std::false_type {}; + template<> struct is_reflectable : std::true_type {}; + template<> struct is_reflectable : std::false_type {}; +}} + + +#if BOOST_PFR_ENABLE_IMPLICIT_REFLECTION +template +void assert_reflectable() { + static_assert(boost::pfr::is_implicitly_reflectable_v, ""); + static_assert(boost::pfr::is_implicitly_reflectable_v, ""); + static_assert(boost::pfr::is_implicitly_reflectable_v, ""); + static_assert(boost::pfr::is_implicitly_reflectable_v, ""); +} + +template +void assert_non_reflectable() { + static_assert(!boost::pfr::is_implicitly_reflectable_v, ""); + static_assert(!boost::pfr::is_implicitly_reflectable_v, ""); + static_assert(!boost::pfr::is_implicitly_reflectable_v, ""); + static_assert(!boost::pfr::is_implicitly_reflectable_v, ""); +} +#endif // #if BOOST_PFR_ENABLE_IMPLICIT_REFLECTION + +int main() { +#if BOOST_PFR_ENABLE_IMPLICIT_REFLECTION + std::cout << "Implicit reflection is available in this platform.." << std::endl; + { + using tag = boost::pfr::boost_json_tag; + assert_reflectable(); + assert_non_reflectable(); + assert_reflectable(); + assert_non_reflectable(); + assert_reflectable(); + assert_non_reflectable(); + } + + { + using tag = boost::pfr::boost_fusion_tag; + assert_reflectable(); + assert_non_reflectable(); + assert_reflectable(); + assert_non_reflectable(); + assert_reflectable(); + assert_non_reflectable(); + } +#endif // #if BOOST_PFR_ENABLE_IMPLICIT_REFLECTION +} + diff --git a/test/run/is_reflectable.cpp b/test/run/is_reflectable.cpp new file mode 100644 index 0000000..fe737fb --- /dev/null +++ b/test/run/is_reflectable.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2022 Denis Mikhailov +// +// 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 // for std::true_type, std::false_type + +namespace boost { namespace pfr { + struct boost_fusion_tag; + struct boost_json_tag; +}} + +struct Reflectable {}; +struct Nonrefrectable {}; +struct Unknown {}; + +struct ReflectableBoostFusion {}; +struct NonrefrectableBoostFusion {}; + +struct ReflectableBoostJson {}; +struct NonrefrectableBoostJson {}; + +namespace boost { namespace pfr { + template struct is_reflectable : std::true_type {}; + template struct is_reflectable : std::false_type {}; + template<> struct is_reflectable : std::true_type {}; + template<> struct is_reflectable : std::false_type {}; + template<> struct is_reflectable : std::true_type {}; + template<> struct is_reflectable : std::false_type {}; +}} + + + + +namespace helpers { +template +constexpr decltype(boost::pfr::is_reflectable::value) is_reflectability_known(long) { + return true; +} + +template +constexpr bool is_reflectability_known(int) { + return false; +} +} + +template +void assert_reflectable_impl() { + static_assert(helpers::is_reflectability_known(1L), ""); + static_assert(boost::pfr::is_reflectable::value, ""); +} + +template +void assert_non_reflectable_impl() { + static_assert(helpers::is_reflectability_known(1L), ""); + static_assert(!boost::pfr::is_reflectable::value, ""); +} + +template +void assert_unknown_impl() { + static_assert(!helpers::is_reflectability_known(1L), ""); +} + +template +void assert_reflectable() { + assert_reflectable_impl(); + assert_reflectable_impl(); + assert_reflectable_impl(); + assert_reflectable_impl(); +} + +template +void assert_non_reflectable() { + assert_non_reflectable_impl(); + assert_non_reflectable_impl(); + assert_non_reflectable_impl(); + assert_non_reflectable_impl(); +} + +template +void assert_unknown() { + assert_unknown_impl(); + assert_unknown_impl(); + assert_unknown_impl(); + assert_unknown_impl(); +} + +int main() { + { + using tag = boost::pfr::boost_json_tag; + assert_reflectable(); + assert_non_reflectable(); + assert_unknown(); + assert_unknown(); + assert_unknown(); + assert_reflectable(); + assert_non_reflectable(); + } + + { + using tag = boost::pfr::boost_fusion_tag; + assert_reflectable(); + assert_non_reflectable(); + assert_unknown(); + assert_reflectable(); + assert_non_reflectable(); + assert_unknown(); + assert_unknown(); + } +} +