Class member variables based on a variation pattern

Given a list of types, names, and default values, I could easily write a tool that generates valid C ++ code that declares a class with a member variable of each type, name, and default value. For example, given a list

  • int, foo, 42
  • float, bar, 0.1f

(and the class name is "Baz"), it will generate

class Baz {
    int foo = 42;
    float bar = 0.1f;
}

If the tool can generate such a class, could the compiler do this for me? I am thinking of something in these lines (note: this is pseudocode):

template <typename ...MemberTypes> class Baz {
    MemberTypes::type... MemberTypes::name... = MemberTypes::default...;
}

Something like this will be created above the class

using MyBaz = Baz<member_type<int, "foo", 42>, member_type<float, "bar", 0.1f>>;

Reasons why this is possible:

  • All necessary information is available at compile time. An external tool can easily do this.
  • - ( - ).
  • , .
  • (++- Turing-complete?), "" .

, :

  • ( ++) , , , .
  • , ( ).

, ? , ? ++ 17 - ?

: : - " ". , , (config.get<int>("core.timeout")) , , (config.get<int>("core.timeuot")).

, . , -. , .

, ( ). , .

+4
2

++ (). , , .

, , , ( Boost.PP , ) ( ):

#define GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_0(...) \
     ((__VA_ARGS__)) GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_1

#define GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_1(...) \
     ((__VA_ARGS__)) GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_0

#define GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_0_END
#define GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_1_END

// Double the parentheses of a Boost.PP sequence
// I.e. (a, b)(c, d) becomes ((a, b))((c, d))
#define GLK_PP_SEQ_DOUBLE_PARENS(seq) \
    BOOST_PP_CAT(GLK_PP_DETAIL_SEQ_DOUBLE_PARENS_0 seq, _END)


#define MAKE_ONE_VARIABLE(r, data, elem) \
    BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem) = BOOST_PP_TUPLE_ELEM(2, elem);

#define MAKE_CLASS(className, members) \
    struct className { \
        BOOST_PP_SEQ_FOR_EACH(MAKE_ONE_VARIABLE, ~, GLK_PP_SEQ_DOUBLE_PARENS(members)) \
    }

... :

MAKE_CLASS(Baz, (int, foo, 42)(float, bar, 0.1f));

... :

struct Baz {
    int foo = 42;
    float bar = 0.1f;
};
+7

, . , - , , .

member_type:

// Class member
template<class type_t, class name_t, type_t default_value = type_t() >
struct member_type {
    using type = type_t;
    using name = name_t;
    type_t value = default_value;
};

, . - :

template<class ... T>
struct t_templated_members
{ 
    using t_members_list = std::tuple<T...>;
    t_members_list members;
};

, , , .

// "names" of your members
struct member_x {};
struct member_y {};

using foo = t_templated_members< 
    member_type<int, member_x, 10>,
    member_type<char, member_y, 'a'> >;

"".

namespace details
{
    // Checks if the member at index I is the right one
    template<class T, class U, size_t I>
    using is_right_member = std::is_same<
        typename std::tuple_element_t<I, typename U::t_members_list>::name, T>;

    // Get the index of a member
    template<class T, class U, size_t I = 0 >
    struct find_element : public std::conditional_t<
        is_right_member<T, U, I>::value,
        std::integral_constant<decltype(I), I>,
        find_element<T, U, I + 1>> 
    { };
}

template<class T, class U>
auto & member_get(U & host)
{
    constexpr auto index = details::find_element<T, U>::value;

    return std::get<index>(host.members).value;
};

member_get, , foo:

#include <iostream>

int main()
{
    foo my_foo;

    auto & x = member_get<member_x>(my_foo);
    std::cout << x << ' ';
    x = 6;
    std::cout << member_get<member_x>(my_foo) << '\n';

    std::cout << member_get<member_y>(my_foo) << ' ';
    member_get<member_y>(my_foo) = 'b';
    std::cout << member_get<member_y>(my_foo) << '\n';

    return 0;
}
+2

Source: https://habr.com/ru/post/1677470/


All Articles