2

I collected the following from several snippets, using the technique of testing the ability of aggregate-initialization, and transforming the struct into a tuple for counting the size, but when there is an array, the struct could be brace-elided and makes the structured binding incorrect. Is there any workaround/other trick for this in C++17?

struct data_t {
    int a;
    char b[2];
    bool c;
};

// data_t could be init as {x, y1, y2, z}...

template <class T, class... Args>
decltype(void(T{std::declval<Args>()...}), std::true_type{}) test_is_braces_constructible(int);

template <class, class...>
std::false_type test_is_braces_constructible(...);

template <class T, class... TArgs>
using is_braces_constructible = decltype(test_is_braces_constructible<T, TArgs...>(0));

struct any_type {
    template <class T>
    constexpr operator T(); // non explicit
};

template <class T>
auto to_tuple(T &&object) noexcept
{
    if constexpr (is_braces_constructible<T, any_type, any_type, any_type, any_type>{}) {
        // the object coming here...  the binding is incorrect
        auto &&[p1, p2, p3, p4] = object;
        return std::make_tuple(p1, p2, p3, p4);
    } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type>{}) {
        auto &&[p1, p2, p3] = object;
        return std::make_tuple(p1, p2, p3);
    } else if constexpr (is_braces_constructible<T, any_type, any_type>{}) {
        auto &&[p1, p2] = object;
        return std::make_tuple(p1, p2);
    } else if constexpr (is_braces_constructible<T, any_type>{}) {
        auto &&[p1] = object;
        return std::make_tuple(p1);
    } else {
         return std::make_tuple();
    }
}
Anqur
  • 111
  • 1
  • 4
  • 1
    Are you initializing the values to anything else other than 0/false? – cup Dec 06 '19 at 07:23
  • AFAIK there's not anything you can do about that. Except staying away from arrays. You get the same problem using `std::array` unforutnately. You could make your own array wrapper if you really need this to work. – super Dec 06 '19 at 07:33
  • @super Array wrapper can be a go, but compared to manually declaring a static member for fields number, testing it in the `constexpr` if, it might be tedious to use... My goal is (sadly due to legacy C code) (de)serializing the POD types. – Anqur Dec 06 '19 at 07:45
  • @AnqurVanillapy If you're looking to serialize, then flattening the struct could be ok? That can be done, it's available in [boost](http://apolukhin.github.io/magic_get/boost_precise_and_flat_reflectio/tutorial.html). – super Dec 06 '19 at 07:48
  • @cup Yes I tested it with `to_tuple(Data{1, "a", true})`. But i cannot relate it to value initializations. What will happen though? – Anqur Dec 06 '19 at 07:49
  • @super Oops pretty sorry for wording. Besides serdes, I need to reflectionally get the fields count and types (ideally structured binding it) and for-each filling the contents. It's done by codegen before, but I'm thinking about the reflection tricks. – Anqur Dec 06 '19 at 08:00
  • @AnqurVanillapy Ok, in that case I don't think it can be done. – super Dec 06 '19 at 08:08
  • 1
    Try change `void(T{std::declval()...})` to `void(T{{std::declval()}...})` – pure cuteness Dec 06 '19 at 12:15

1 Answers1

1

According to a friend of mine, a little pair of braces can help the trick.

template <class T, class... Args>
decltype(void(T{ {std::declval<Args>()}... }), std::true_type{}) test_is_braces_constructible(int);
                 ^^^                  ^^^
Anqur
  • 111
  • 1
  • 4