Boost logo

Boost :

From: Gero Peterhoff (g.peterhoff_at_[hidden])
Date: 2021-11-12 18:38:18


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




Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk