Boost logo

Boost :

Subject: Re: [boost] Nasty compile time reflection and iteration for POD types using C++14 without macro
From: Brent Friedman (fourthgeek_at_[hidden])
Date: 2016-03-22 19:39:25


In my head, lambdas are basically just pods that capture data in their
constructor. Obviously this is not exactly the case, but I think it's more
or less accurate. It would probably require some language tweaks to get it
to work with lambdas, but if we could it might be extremely useful.

my_assert( x < 5 );

x < 5 would be the body of a &-capture lambda

We reflect the (theoretical) lambda constructor to find that the lambda
captured an int&
Use implementation-specific knowledge to get a pointer to x
And can print the actual value of x when the assertion fails

Assertion Failed: x<5 where x=10

On Tue, Mar 22, 2016 at 2:45 PM, charleyb123 . <charleyb123_at_[hidden]>
wrote:

> 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
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


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