Boost logo

Boost :

Subject: [boost] Nasty compile time reflection and iteration for POD types using C++14 without macro
From: Antony Polukhin (antoshkka_at_[hidden])
Date: 2016-03-22 15:33:53


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?

Sources https://github.com/apolukhin/magic_get/blob/master/magic2.cpp
Online experiments can be done here:
http://coliru.stacked-crooked.com/a/c33857e566e76ce2

-- 
Best regards,
Antony Polukhin

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