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@gmail.com> wrote:
Including whole email so context is not lost:
On Tue, Mar 22, 2016 at 1:33 PM, Antony Polukhin <antoshkka@gmail.com> 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