[boost] union support in describe

10 views
Skip to first unread message

Gero Peterhoff via Boost

unread,
Nov 12, 2021, 1:48:55 PM11/12/21
to bo...@lists.boost.org, Gero Peterhoff
Hello,
first of all, thank you very much for the great work.
But I miss the support for unions. Is that correct?
namespace boost
{
namespace describe
{
#define BOOST_DESCRIBE_UNION(C, Public) \
static_assert(std::is_union<C>::value, "BOOST_DESCRIBE_UNION should only be used with union types"); \
BOOST_DESCRIBE_PUBLIC_MEMBERS_(C BOOST_DESCRIBE_PP_UNPACK Public) \
BOOST_DESCRIBE_PROTECTED_MEMBERS_(C) \
BOOST_DESCRIBE_PRIVATE_MEMBERS_(C)
} // describe
} // boost

This version only supports public members.


I also miss the support for std::get. Is that correct?
namespace boost
{
namespace describe
{
#define BOOST_DESCRIBE_GET(C) \
template <std::size_t Index> \
inline constexpr const auto& get(const C& arg) noexcept \
{ \
static_assert(std::is_class<C>::value || std::is_union<C>::value, "BOOST_DESCRIBE_GET should only be used with class, struct or union types"); \
using members = boost::describe::describe_members<C, boost::describe::mod_any_access>; \
static_assert(Index < boost::mp11::mp_size<members>()); \
return arg.*boost::mp11::mp_at_c<members, Index>().pointer; \
} \
template <std::size_t Index> \
inline constexpr auto& get(C& arg) noexcept \
{ \
static_assert(std::is_class<C>::value || std::is_union<C>::value, "BOOST_DESCRIBE_GET should only be used with class, struct or union types"); \
using members = boost::describe::describe_members<C, boost::describe::mod_any_access>; \
static_assert(Index < boost::mp11::mp_size<members>()); \
return arg.*boost::mp11::mp_at_c<members, Index>().pointer; \
}
} // describe
} // boost

However, the access rights are not taken into account. E.g.:

struct A
{
int i;
float f;
};

BOOST_DESCRIBE_STRUCT(A, (), (i, f))

namespace std
{
BOOST_DESCRIBE_GET(A)
} // std


This also solves a very special problem, provided the compiler supports it: active member of a constexp union.
Problem:

union U
{
int64_t i;
double d;
};

constexpr U u{.d{3.1415}};
constexpr auto i=u.i;
-> error

In short:
namespace std
{
template <typename Type>
inline constexpr bool is_constant_initialized(const Type& arg) noexcept
{
#if __has_builtin(__builtin_constant_p)
return __builtin_constant_p(arg);
#elif defined(BOOST_MSVC)
???
#else
static_assert(false, "is_constant_initialized not supportet");
return false;
#endif
}

template <typename Type>
inline constexpr bool is_constant_initialized_front(const Type& arg) noexcept
{
return is_constant_initialized(arg);
}

template <typename Type, size_t Size>
inline constexpr bool is_constant_initialized_front(const Type(&arg)[Size]) noexcept
{
static_assert(Size > 0);
return is_constant_initialized(arg[0]);
}

template <typename Type, size_t Size>
inline constexpr bool is_constant_initialized_front(const std::array<Type, Size>& arg) noexcept
{
static_assert(Size > 0);
return is_constant_initialized(arg[0]);
}
} // std

namespace boost
{
namespace describe
{
template <typename Type>
inline constexpr std::size_t active_index(const Type& arg) noexcept
{
static_assert(std::is_union_v<Type>);
using any = describe_members<Type, mod_any_access>;

std::size_t
result{0},
index{0};
bool
first{true};

boost::mp11::mp_for_each<any>([&](const auto& desc) constexpr noexcept
{
const auto&
elem{arg.*desc.pointer};

if (std::is_constant_initialized_front(elem))
{
if (first)
{
first = false;
result = index;
}
}
++index;
});
return first ? 0 : result;
}

template <auto Value>
inline constexpr std::size_t active_index_v = std::integral_constant<std::size_t, active_index(Value)>::value;
} // describe
} // boost

union __mm128_emu
{
float m128_f32[4];
double m128_f64[2];

int64_t m128_i64[2];
int32_t m128_i32[4];
int16_t m128_i16[8];
int8_t m128_i8[16];

uint64_t m128_u64[2];
uint32_t m128_u32[4];
uint16_t m128_u16[8];
uint8_t m128_u8[16];
};

BOOST_DESCRIBE_UNION(__mm128_emu, (m128_f32, m128_f64, m128_i64, m128_i32, m128_i16, m128_i8, m128_u64, m128_u32, m128_u16, m128_u8))

namespace std
{
BOOST_DESCRIBE_GET(__mm128_emu)
} // std

constexpr __mm128_emu m128{.m128_u32{4711}};
constexpr auto arr = std::to_array(std::get<boost::describe::active_index_v<m128>>(m128)); // works with gcc, clang, intel

Do you have an idea how to implement __builtin_constant_p for all compilers?

thx
Gero



OpenPGP_signature

Peter Dimov via Boost

unread,
Nov 12, 2021, 2:54:40 PM11/12/21
to bo...@lists.boost.org, Peter Dimov
Gero Peterhoff wrote:
> Hello,
> first of all, thank you very much for the great work.
> But I miss the support for unions. Is that correct?
> namespace boost
> {
> namespace describe
> {
> #define BOOST_DESCRIBE_UNION(C, Public) \
> static_assert(std::is_union<C>::value, "BOOST_DESCRIBE_UNION
> should only be used with union types"); \
> BOOST_DESCRIBE_PUBLIC_MEMBERS_(C
> BOOST_DESCRIBE_PP_UNPACK Public) \
> BOOST_DESCRIBE_PROTECTED_MEMBERS_(C) \
> BOOST_DESCRIBE_PRIVATE_MEMBERS_(C)
> } // describe
> } // boost

Interesting point. In principle, BOOST_DESCRIBE_STRUCT and
BOOST_DESCRIBE_CLASS should support unions, and a separate
macro shouldn't be needed. So the static_assert on is_class is
probably too strict and needs to be relaxed.

However, I suspect that most code supporting described classes
won't work correctly when passed a union. The examples in the
documentation don't, so they would need to be updated with a
check for is_class.

> I also miss the support for std::get. Is that correct?
> namespace boost
> {
> namespace describe
> {
> #define BOOST_DESCRIBE_GET(C) \
> template <std::size_t Index> \
> inline constexpr const auto& get(const C& arg) noexcept \
> { \
> static_assert(std::is_class<C>::value || std::is_union<C>::value,
> "BOOST_DESCRIBE_GET should only be used with class, struct or union
> types"); \
> using members = boost::describe::describe_members<C,
> boost::describe::mod_any_access>; \
> static_assert(Index < boost::mp11::mp_size<members>());
> \
> return arg.*boost::mp11::mp_at_c<members, Index>().pointer;
> \
> } \
> template <std::size_t Index> \
> inline constexpr auto& get(C& arg) noexcept \
> { \
> static_assert(std::is_class<C>::value || std::is_union<C>::value,
> "BOOST_DESCRIBE_GET should only be used with class, struct or union
> types"); \
> using members = boost::describe::describe_members<C,
> boost::describe::mod_any_access>; \
> static_assert(Index < boost::mp11::mp_size<members>());
> \
> return arg.*boost::mp11::mp_at_c<members, Index>().pointer;
> \
> }
> } // describe
> } // boost

It's not clear why this needs to be std::get; adding overloads to namespace
std is undefined behavior. It might be better off as a helper function in some
namespace of your own.

And a helper function of your own wouldn't need to be defined via a macro,
it can be templated on C.

template<std::size_t Index, class C>
inline constexpr auto& get(C& arg) noexcept
{
using members = boost::describe::describe_members<C,
boost::describe::mod_any_access>;
static_assert(Index < boost::mp11::mp_size<members>());
return arg.*boost::mp11::mp_at_c<members, Index>().pointer;
}

> Do you have an idea how to implement __builtin_constant_p for all
> compilers?

I can't think of a way.



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Gero Peterhoff via Boost

unread,
Nov 12, 2021, 3:47:21 PM11/12/21
to bo...@lists.boost.org, Gero Peterhoff
> Interesting point. In principle, BOOST_DESCRIBE_STRUCT and
> BOOST_DESCRIBE_CLASS should support unions, and a separate
> macro shouldn't be needed. So the static_assert on is_class is
> probably too strict and needs to be relaxed.
>
> However, I suspect that most code supporting described classes
> won't work correctly when passed a union. The examples in the
> documentation don't, so they would need to be updated with a
> check for is_class.

Just. Currently it is probably better to make a strict distinction.

> It's not clear why this needs to be std::get; adding overloads to namespace
> std is undefined behavior. It might be better off as a helper function in some
> namespace of your own.
>
> And a helper function of your own wouldn't need to be defined via a macro,
> it can be templated on C.
>
> template<std::size_t Index, class C>
> inline constexpr auto& get(C& arg) noexcept
> {
> using members = boost::describe::describe_members<C,
> boost::describe::mod_any_access>;
> static_assert(Index < boost::mp11::mp_size<members>());
> return arg.*boost::mp11::mp_at_c<members, Index>().pointer;
> }

You don't have to instantiate get in std. That's why I did it as a macro.

>> Do you have an idea how to implement __builtin_constant_p for all
>> compilers?
>
> I can't think of a way.

I have found https://github.com/nemequ/attic/blob/master/is_constant.h . But doesn't work :-(
But that should be with the upcoming reflections
https://en.cppreference.com/w/cpp/experimental/reflect
https://en.cppreference.com/w/cpp/experimental/reflect/Constant
to be possible?

regards
Gero




OpenPGP_signature
Reply all
Reply to author
Forward
0 new messages