4
\$\begingroup\$

To get n-th member of a struct without non-empty bases and w/o non-public members C++ now have a pretty way:

#include <type_traits>
#include <utility>
#include <tuple>

struct filler { template< typename type > operator type (); };

template< typename aggregate, typename index_sequence = std::index_sequence<>, typename = void >
struct aggregate_arity
        : index_sequence
{

};

template< typename aggregate, std::size_t ...indices >
struct aggregate_arity< aggregate, std::index_sequence< indices... >, std::void_t< decltype(aggregate{(indices, std::declval< filler >())..., std::declval< filler >()}) > >
    : aggregate_arity< aggregate, std::index_sequence< indices..., sizeof...(indices) > >
{

};

template< std::size_t index, typename type >
constexpr
decltype(auto)
get(type & value) noexcept
{
    constexpr std::size_t arity = aggregate_arity< std::remove_reference_t< std::remove_cv_t< type > > >::size();
    if constexpr (arity == 1) {        
        auto & [p1] = value;
        if constexpr (index == 0) {
            return (p1);
        } else {
            return;
        }
    } else if constexpr (arity == 2) {
        auto & [p1, p2] = value;
        if constexpr (index == 0) {
            return (p1);
        } else if constexpr (index == 1) {
            return (p2);
        } else {
            return;
        }
    } else if constexpr (arity == 3) {
        auto & [p1, p2, p3] = value;
        if constexpr (index == 0) {
            return (p1);
        } else if constexpr (index == 1) {
            return (p2);
        } else if constexpr (index == 2) {
            return (p3);
        } else {
            return;
        }
    } else /* extend it by yourself for higher arities */  {
        return;
    }
}

#include <cstdlib>
#include <cassert>

int
main()
{
    using S = struct { int i; char c; bool b; };
    S s{1, '2', true};
    decltype(auto) i = get< 0 >(s);
    decltype(auto) c = get< 1 >(s);
    decltype(auto) b = get< 2 >(s);
    static_assert(std::is_same< decltype(i), int & >{});
    static_assert(std::is_same< decltype(c), char & >{});
    static_assert(std::is_same< decltype(b), bool & >{});
    assert(i == 1);
    assert(c == '2');
    assert(b == true);
    s.i = 2;
    s.c = '3';
    s.b = false;
    assert(i == 2);
    assert(c == '3');
    assert(b == false);
    return EXIT_SUCCESS;
}

It is not as generic as one can want, but it is workable on clang from a current trunk.

It also currently not works for top level cv-qualified class (due to clang bug) and can't properly forward referenceness of the input (though, it can be implemented for non-const references), but latter is hardly desired by anybody at all (maybe there would be conversion of referenceness from xvalue to prvalue, whereas lvalueness should be still preserved).

What can I do it to improve the style and correcteness?

Live example.

\$\endgroup\$
  • \$\begingroup\$ Interesting. I guess you meant and w/o non-public members. I think you should add that the struct actually needs to be an aggregate (i.e. no custom constructors, no virtual/private/protected base classes, no virtual functions, no default member initializers) (or have some very coincidental constructor set) \$\endgroup\$ – user4407569 Sep 29 '16 at 21:25
  • \$\begingroup\$ @Eichhörnchen Thank you for correction. Actually class may have constructors. Constructor with highest arity will be considered. In the case this arity should match the number of data members. Default member initializers are also not the problem. About virtual stuff you are right. \$\endgroup\$ – Orient Sep 30 '16 at 1:46
  • \$\begingroup\$ I mean "highest" in continouos integer sequence started from 1 (i.e. unary). \$\endgroup\$ – Orient Sep 30 '16 at 2:43
  • \$\begingroup\$ That's what I meant by "coincidental constructor set". There would have to be for every m from 1 to n, where n is the number of data members, exactly one constructor accepting m arguments, but no constructor accepting n+1 arguments and all the constructors mentioned would need to be non-explicit. I thought about wether it is possible to use SFINAE over auto& [...] = value to decide the number of members, but I think that doesn't work, because there is no way to get the statement into a template argument expression. \$\endgroup\$ – user4407569 Sep 30 '16 at 11:05
  • \$\begingroup\$ @Eichhörnchen It has been recently discussed here. \$\endgroup\$ – Orient Sep 30 '16 at 11:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.