diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 7225e53..96858b4 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -184,6 +184,41 @@ Argument Dependant Lookup works well, `std::less` will find the operators for `s [endsect] + +[section Reflection of unions ] + +With [*precise] reflection you could do reflection if a type contains union. But be sure that operations for union are manually defined: + +``` +#include + +union test_union { + int i; + float f; +}; + +inline bool operator==(test_union l, test_union r) noexcept; // Compile time error without this operator + + +struct foo { int i; test_union u; }; + +bool some_function(foo f1, foo f2) { + using namespace boost::pfr::ops; + return f1 == f2; // OK +} + +``` + +[*Flat] reflection of types that contain unions is disabled. +[*Flat] and [*precise] reflection of unions is disabled for safety reasons. There's a way to reflect the first member of a union and use it. Unfortunately there's no way to find out [*active] member of a union. Accessing an inactive union member is an Undefined Behavior. Using the first union member for reflection could lead to disaster if it is some character pointer. For example ostreaming `union {char* c; long long ll; } u; u.ll= 1;` will crash your program, as the active member is `ll` that holds `1` but we are trying to output a `char*`. This would cause an invalid pointer dereference. + +Any attempt to reflect unions leads to a compile time error. In many cases a static assert is triggered that outputs the following message: + +``` +error: static_assert failed "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." +``` +[endsect] + [endsect] diff --git a/include/boost/pfr/detail/core14_classic.hpp b/include/boost/pfr/detail/core14_classic.hpp index 66884ff..31b5643 100644 --- a/include/boost/pfr/detail/core14_classic.hpp +++ b/include/boost/pfr/detail/core14_classic.hpp @@ -236,7 +236,7 @@ template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>*) noexcept { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); return 0; } @@ -549,7 +549,7 @@ template auto tie_as_flat_tuple(T& lvalue) noexcept { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); using type = std::remove_cv_t; using tuple_type = internal_tuple_with_same_alignment_t; @@ -564,7 +564,7 @@ template auto tie_as_tuple(T& val) noexcept { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); static_assert( boost::pfr::detail::is_flat_refelectable( std::make_index_sequence()>{} ), @@ -671,7 +671,7 @@ template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); /// Compile time error at this point means that you have called `for_each_field` or some other non-flat function or operator for a diff --git a/include/boost/pfr/detail/core14_loophole.hpp b/include/boost/pfr/detail/core14_loophole.hpp index b8ba708..93cf411 100644 --- a/include/boost/pfr/detail/core14_loophole.hpp +++ b/include/boost/pfr/detail/core14_loophole.hpp @@ -142,7 +142,7 @@ template auto tie_or_value(T& val, std::enable_if_t >::value>* = 0) noexcept { static_assert( sizeof(T) && false, - "====================> Boost.PFR: For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); return 0; } @@ -177,7 +177,7 @@ template auto tie_as_flat_tuple(T& t) { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); auto rec_tuples = boost::pfr::detail::tie_as_tuple_recursively( boost::pfr::detail::tie_as_tuple_loophole_impl(t) @@ -194,7 +194,7 @@ template auto tie_as_tuple(T& val) noexcept { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); return boost::pfr::detail::tie_as_tuple_loophole_impl( val @@ -205,7 +205,7 @@ template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); std::forward(f)( boost::pfr::detail::tie_as_tuple_loophole_impl(t) diff --git a/include/boost/pfr/detail/core17.hpp b/include/boost/pfr/detail/core17.hpp index f389ed9..5615067 100644 --- a/include/boost/pfr/detail/core17.hpp +++ b/include/boost/pfr/detail/core17.hpp @@ -50,7 +50,7 @@ template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); std::forward(f)( detail::tie_as_tuple(t) diff --git a/include/boost/pfr/detail/core17_generated.hpp b/include/boost/pfr/detail/core17_generated.hpp index da60fc9..4df2a89 100644 --- a/include/boost/pfr/detail/core17_generated.hpp +++ b/include/boost/pfr/detail/core17_generated.hpp @@ -1030,7 +1030,7 @@ template constexpr auto tie_as_tuple(T& val) noexcept { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); typedef size_t_()> fields_count_tag; return boost::pfr::detail::tie_as_tuple(val, fields_count_tag{}); diff --git a/misc/generate_cpp17.py b/misc/generate_cpp17.py index d57cec8..aec56ba 100644 --- a/misc/generate_cpp17.py +++ b/misc/generate_cpp17.py @@ -70,7 +70,7 @@ template constexpr auto tie_as_tuple(T& val) noexcept { static_assert( !std::is_union::value, - "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); typedef size_t_()> fields_count_tag; return boost::pfr::detail::tie_as_tuple(val, fields_count_tag{});