C++ std::tuple作为成员替换,方便宏

C++ std::tuple作为成员替换,方便宏,c++,stdtuple,C++,Stdtuple,我最近开始使用元组而不是普通的类成员,因为我发现使用它们很方便。因此,我的代码如下所示: class TestClass final { public: TestClass() = default; ~TestClass() = default; public: template<int M> auto get()->decltype(std::get<M>(m_private_members)) const { return

我最近开始使用元组而不是普通的类成员,因为我发现使用它们很方便。因此,我的代码如下所示:

class TestClass final {
public:
   TestClass() = default;
   ~TestClass() = default;

public:
   template<int M>
   auto get()->decltype(std::get<M>(m_private_members)) const {
      return std::get<M>(m_private_members);
   }

   enum PrivateIdx {
      count,
      use_stuff,
      name
   };

private:
   std::tuple<int, bool, std::string> m_private_members{1, true, "bla"};

};
这将扩展到元组和枚举代码。问题是,我不认为宏可以解决这个问题,因为它需要扩展到两种不同的数据结构,对吗?此外,我必须承认,我从未研究过复杂的宏


我也在考虑一个模板来解决这个问题,但我也不能想出一个可行的解决方案,因为枚举不能由模板生成。

就像我在注释宏中说的那样,调试起来很痛苦。一个不知道如何写的人应该三思而后行。OTOH,当一个人理解了这些逻辑时,写起来就相对简单了

请注意,给定的只是一种方法,就像所有东西都有几种方法一样。 所以宏是这样的:

#define GET_NAME(NAME,TYPE,VALUE) NAME
#define GET_TYPE(NAME,TYPE,VALUE) TYPE
#define GET_VALUE(NAME,TYPE,VALUE) VALUE

#define DECLARE_ENUM(PRIVATES) \
    enum PrivateIdx { \
        PRIVATES(GET_NAME) \
    };

#define DECLARE_TUPLE(PRIVATES) \
    std::tuple<PRIVATES(GET_TYPE)> m_private_members{PRIVATES(GET_VALUE)};

#define DECLARE_IN_ONE_GO(PRIVATES) \
    public: \
        DECLARE_ENUM(PRIVATES) \
    private: \
        DECLARE_TUPLE(PRIVATES)
#include <iostream>
#include <tuple>
#include "enum_tuple_macros.h"

class TestClass final {
public:
    TestClass() = default;
    ~TestClass() = default;

    #define PRIVATES(MEMBER) \
        MEMBER(count,int,1), \
        MEMBER(use_stuff,bool,true), \
        MEMBER(name,std::string,"bla")

    DECLARE_IN_ONE_GO(PRIVATES)

    // note that the get can be also generated by DECLARE_IN_ONE_GO
public:
    template<int M>
    auto get() const -> decltype(std::get<M>(m_private_members)) {
        return std::get<M>(m_private_members);
    }
};

int main()
{
    TestClass t;
    std::cout << t.get<TestClass::name>() << " in one go" << std::endl;
}
#定义获取名称(名称、类型、值)名称
#定义获取类型(名称、类型、值)类型
#定义GET_值(名称、类型、值)值
#定义声明枚举(私有)\
枚举PrivateIdx{\
私家车(取名字)\
};
#定义声明元组(私有)\
std::tuple m_private_成员{PRIVATES(GET_VALUE)};
#定义一次行动中的声明(私人)\
公众:\
申报(私人)\
私人:\
声明组(私人)
用法是这样的:

#define GET_NAME(NAME,TYPE,VALUE) NAME
#define GET_TYPE(NAME,TYPE,VALUE) TYPE
#define GET_VALUE(NAME,TYPE,VALUE) VALUE

#define DECLARE_ENUM(PRIVATES) \
    enum PrivateIdx { \
        PRIVATES(GET_NAME) \
    };

#define DECLARE_TUPLE(PRIVATES) \
    std::tuple<PRIVATES(GET_TYPE)> m_private_members{PRIVATES(GET_VALUE)};

#define DECLARE_IN_ONE_GO(PRIVATES) \
    public: \
        DECLARE_ENUM(PRIVATES) \
    private: \
        DECLARE_TUPLE(PRIVATES)
#include <iostream>
#include <tuple>
#include "enum_tuple_macros.h"

class TestClass final {
public:
    TestClass() = default;
    ~TestClass() = default;

    #define PRIVATES(MEMBER) \
        MEMBER(count,int,1), \
        MEMBER(use_stuff,bool,true), \
        MEMBER(name,std::string,"bla")

    DECLARE_IN_ONE_GO(PRIVATES)

    // note that the get can be also generated by DECLARE_IN_ONE_GO
public:
    template<int M>
    auto get() const -> decltype(std::get<M>(m_private_members)) {
        return std::get<M>(m_private_members);
    }
};

int main()
{
    TestClass t;
    std::cout << t.get<TestClass::name>() << " in one go" << std::endl;
}
#包括
#包括
#包括“enum\u tuple\u macros.h”
类测试类期末考试{
公众:
TestClass()=默认值;
~TestClass()=默认值;
#定义私人(成员)\
成员(计数,整数,1)\
成员(使用_stuff、bool、true)\
成员(名称,标准::字符串,“bla”)
宣布进入(私人)
//请注意,get也可以由DECLARE_IN_ONE_GO生成
公众:
模板
auto get()const->decltype(std::get(m_private_成员)){
return std::get(m_private_成员);
}
};
int main()
{
测试类t;

std::cout与此同时,我提出了一些使用var args的方法

taken from
[https://stackoverflow.com/questions/16374776/macro-overloading][1]
#define EXPAND(X) X 
#define __NARG__(...)  EXPAND(__NARG_I_(__VA_ARGS__,__RSEQ_N()))
#define __NARG_I_(...) EXPAND(__ARG_N(__VA_ARGS__))
#define __ARG_N( \
      _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
     _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
     _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
     _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
     _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
     _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
     _61,_62,_63,N,...) N
#define __RSEQ_N() \
     63,62,61,60,                   \
     59,58,57,56,55,54,53,52,51,50, \
     49,48,47,46,45,44,43,42,41,40, \
     39,38,37,36,35,34,33,32,31,30, \
     29,28,27,26,25,24,23,22,21,20, \
     19,18,17,16,15,14,13,12,11,10, \
     9,8,7,6,5,4,3,2,1,0

// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) EXPAND(_VFUNC(func, EXPAND( __NARG__(__VA_ARGS__))) (__VA_ARGS__))


#define MEMBER_LIST(...) EXPAND(VFUNC(MEMBER_LIST, __VA_ARGS__))

#define MEMBER_LIST3(mem_type1, mem_name1, default_value1)\
\
enum PrivateIdx { \
   mem_name1 \
}; \
\
std::tuple<mem_type1> m_private_members{default_value1} 

#define MEMBER_LIST6( mem_type0, mem_name0, default_value0,\
                     mem_type1, mem_name1, default_value1)\
\
enum PrivateIdx { \
   mem_name0, \
   mem_name1 \
}; \
\
std::tuple< mem_type0, \
            mem_type1 > m_private_members{ default_value0, \
                                            default_value1}
..and so on
取自
[https://stackoverflow.com/questions/16374776/macro-overloading][1]
#定义扩展(X)X
#定义(…)展开(uuuu NARG_ui_uu(uuu VA_ARGS_uuu,uuu RSEQ_N())
#定义(…)扩展(uuu ARG_N(uu VA_ARGS_uu))
#定义参数(\
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,…)N
#定义_urseq_N()\
63,62,61,60,                   \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
//任何函数名的通用定义
#定义VFUNC(name,n)name 35;#n
#定义VFUNC(名称,n)VFUNC(名称,n)
#定义VFUNC(func,…)EXPAND(VFUNC(func,EXPAND(uu-NARG_uu(u-VA_-ARGS_uu)))(u-VA_-ARGS_u))
#定义成员列表(…)展开(VFUNC(成员列表、变量参数))
#定义成员列表3(成员类型1、成员名称1、默认值1)\
\
枚举PrivateIdx{\
成员名称1\
}; \
\
std::tuple m_private_成员{default_value1}
#定义成员列表6(成员类型0、成员名称0、默认值0、\
mem_类型1、mem_名称1、默认值1)\
\
枚举PrivateIdx{\
mem_name0\
成员名称1\
}; \
\
std::tuplem_private_成员{default_value0\
默认值_值1}
等等

工作正常,但我仍然不够优雅。我想我的方向是正确的。

有趣的问题。我很好奇你为什么要这样做。这是我想到的。好消息:没有宏

我认为,主要的问题是,您希望声明标识符以访问成员。这无法通过模板解决,因此您必须a)使用宏,或b)不直接声明这些标识符。我尝试在
get
中使用类型名来标识成员,而不是使用常量/枚举

我将从一个使用示例开始:

class User
{
public:
    enum class AccessLevel
    {
        ReadOnly,
        ReadWrite,
        Admin
    };

    struct Name : MemberId<std::string> {};
    struct Id : MemberId<unsigned> {};
    struct Access : MemberId<AccessLevel> {};

    template<typename MemberType>
    auto& get() { return PrivMembers::getFromTuple<MemberType>(m_members); }

    template<typename MemberType>
    const auto& get() const { return PrivMembers::getFromTuple<MemberType>(m_members); }

private:
    using PrivMembers = MembersList<Name, Id, Access>;

    PrivMembers::Tuple m_members;
};

int main()
{
    User user;
    user.get<User::Name>() = "John Smith";
    user.get<User::Id>() = 1;
    user.get<User::Access>() = User::AccessLevel::ReadWrite;

    return 0;
}
第一件事:
Tuple
别名。我认为发生的事情是不言自明的。然后,
getFromTuple
有重载,这是由
用户使用的。
当使用
MemberId
派生类型而不是常量来访问元组元素时,我需要找到对应于给定成员Id的索引。这就是
getFromTuple
中发生的情况。有一个帮助器类用于搜索:

namespace detail
{
    template<typename Needle, typename HaystackHead, typename... Haystack>
    struct IndexOf { static constexpr std::size_t value = IndexOf<Needle, Haystack...>::value + 1; };

    template<typename Needle, typename... Haystack>
    struct IndexOf<Needle, Needle, Haystack...> { static constexpr std::size_t value = 0; };
}
名称空间详细信息
{
模板
结构IndexOf{static constexpr std::size\u t value=IndexOf::value+1;};
模板
结构IndexOf{static constexpr std::size\u t value=0;};
}

所有这些都解决了必须为每个成员维护索引的问题,就像在原始解决方案中一样。声明成员ID(
struct Name:MemberId{};
)的语法可能有点烦人,但我想不出更紧凑的解决方案

所有这些都适用于C++14。如果您可以使用
User::get
的尾部返回类型,那么您可以将其编译为C++11


不确定困难是什么?似乎您需要成员(名称,std::string,“bla”)。并且玩得开心。请注意,宏几乎不可能调试,它们的产品也很难调试。问题是,我看不到一个明显的方法,这个宏如何一次过生成enum PrivateIdx和std::tuple的声明。Boost.Preprocessor头可以帮到您。@HolyBlackCat Boost.Preprocessor用于truly深度预处理器元编程。当前的情况可以处理几个简单的宏。我给出了一个例子作为答案。如果你能使用
C++17
,你可以通过使类“可分解”(有一个get m
namespace detail
{
    template<typename Needle, typename HaystackHead, typename... Haystack>
    struct IndexOf { static constexpr std::size_t value = IndexOf<Needle, Haystack...>::value + 1; };

    template<typename Needle, typename... Haystack>
    struct IndexOf<Needle, Needle, Haystack...> { static constexpr std::size_t value = 0; };
}