Boost logo

Boost :

Subject: Re: [boost] Nasty compile time reflection and iteration for POD types using C++14 without macro
From: charleyb123 . (charleyb123_at_[hidden])
Date: 2016-03-22 15:45:56


Including whole email so context is not lost:

On Tue, Mar 22, 2016 at 1:33 PM, Antony Polukhin <antoshkka_at_[hidden]>
wrote:

> Hello,
>
> I've come up with a trick that may be useful in Boost.Hana,
> Boost.Serialization or some other libraries.
>
> Here's a small motivating example:
>
> #include <cstdio>
> #include <boost/type_index.hpp>
> #include "magic.hpp"
>
> struct foo {
> unsigned char i0;
> unsigned int i1;
> unsigned short i2;
> unsigned long long i3;
> unsigned char ar[2];
> int q;
> std::size_t w;
> int* p1;
> const void* p2;
> int const**const**** p_crazy;
> const double d;
> };
>
> template <std::size_t I, class T>
> void print(T& f) {
> printf(
> "%lu\t\t%s\n",
> (std::size_t)get<I>(f),
>
> boost::typeindex::type_id<decltype(get<I>(f))>().pretty_name().c_str()
> );
> }
>
> int main() {
> foo f {10, 11, 12, 13, {14, 15}, 16, 17, 0, 0, 0, 30.0};
> print<0>(f); print<1>(f); print<2>(f);
> print<3>(f); print<4>(f); print<5>(f);
> print<6>(f); print<7>(f); print<8>(f);
> print<9>(f); print<10>(f); print<11>(f);
> static_assert(tuple_size<foo>() == 12, "failed tuple size check");
> }
>
> That example will output:
>
> 10 unsigned char
> 11 unsigned int
> 12 unsigned short
> 13 unsigned long long
> 14 unsigned char
> 15 unsigned char
> 16 int
> 17 unsigned long
> 0 int*
> 0 void const*
> 0 int const** const****
> 30 double
>
> Core ideas:
>
> 1) We may detect the POD structure fields by the POD's constructor:
>
> template <std::size_t I>
> struct ubiq {
> std::size_t* ref_;
>
> template <class Type>
> constexpr operator Type() const noexcept {
> ref_[I] = type_to_id(identity<Type>{});
> return Type{};
> }
> };
>
>
> template <class T, std::size_t... I>
> constexpr auto type_to_array_of_type_ids(std::size_t* types) noexcept ->
> decltype(T{ ubiq<I>{types}... }) {
> return T{ ubiq<I>{types}... };
> }
>
> Here type_to_array_of_type_ids will return array with types via `types`,
> each element of array encodes some basic type.
>
> 2) Then we convert that array into an std::index_sequence<I...> and create
> a type with same layout as our POD type:
>
> template <std::size_t... I>
> constexpr auto as_tuple_impl(std::index_sequence<I...>) noexcept {
> return std::tuple< decltype(
> id_to_type(std::integral_constant<std::size_t, I>{}) )... >{};
> }
>
> 3) Now we have a type with same layout, and we cast the structure to that
> type:
>
> template <std::size_t I, class T>
> decltype(auto) get(const T& val) noexcept {
> auto t = reinterpret_cast<const decltype(detail::as_tuple<T>())*>(
> std::addressof(val) );
> return get<I>(*t);
> }
>
>
> Drawbacks:
> * works for POD types only (could be possibly extended to trivial
> constructible type)
> * structures with non-default alignment are not supported
> * step 3) contains an UB, but even without that step we're still able to
> get field types of the POD type and fields count
> * current draft implementation does no support nested PODs (could be fixed
> easily)
> * current draft implementation works only on libc++ (could be fixed easily,
> requires reimplementing std::tuple)
> * there's no known to me way to make get<>() method constexpr
> * does not detect constness (because of that get<>() always returns const
> reference)
>
>
> Is there any interest in this feature?
>

This is really cool. Yes, absolutely this is interesting.

Specifically, we do a lot of bare-metal interfacing, and primitive
serialization -- it would be great to generate "reports" of what is
physically serialized to catch differences across versions. Also, many
serialization optimizations are possible if we confirm the native types are
the "same" on the two sides (e.g., "fwrite(outfile, &my_pod)" rather than
marshaling each member specifically). This could be a runtime comparison
of the "generated-reports", followed by an "optimized" call (if both
reports are the same), or an "each-member conversion" call (if the reports
are different).

Loving the compile-time reflection. I think I could use your approach at
both compile-time and at runtime to select most-efficient implementations.

--charley


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