C++ 有没有一种方法可以将操作应用于NC++;在成员名称上的循环中对成员进行类化(可能通过预处理器)?

C++ 有没有一种方法可以将操作应用于NC++;在成员名称上的循环中对成员进行类化(可能通过预处理器)?,c++,reflection,c-preprocessor,C++,Reflection,C Preprocessor,问题: 我有一个C++类,其中GJILLION(>100)成员的行为几乎相同: 同类型 在一个函数中,每个成员都有与其他成员相同的代码,例如,从构造函数中的映射赋值,其中映射键与成员键相同 这种行为的一致性在许多函数(>20)中重复出现,当然每个函数中的行为都是不同的,因此没有办法将其考虑在内 成员列表非常不稳定,不断增加,有时删除,一些(但不是全部)是由DB表中的更改列驱动的 正如您可以想象的那样,这在代码创建和维护方面带来了巨大的困难,因为要添加新成员,您必须向每个函数添加代码 使用类似

问题:

我有一个C++类,其中GJILLION(>100)成员的行为几乎相同:

  • 同类型

  • 在一个函数中,每个成员都有与其他成员相同的代码,例如,从构造函数中的映射赋值,其中映射键与成员键相同

  • 这种行为的一致性在许多函数(>20)中重复出现,当然每个函数中的行为都是不同的,因此没有办法将其考虑在内

  • 成员列表非常不稳定,不断增加,有时删除,一些(但不是全部)是由DB表中的更改列驱动的

正如您可以想象的那样,这在代码创建和维护方面带来了巨大的困难,因为要添加新成员,您必须向每个函数添加代码 使用类似构件的位置

我想要的解决方案示例

实际C++代码(例如,构造函数):

我希望能够编写的C++代码:

要求

  • 该解决方案必须与GCC兼容(如果有必要的话,可以使用Nmake作为make系统)。 不要关心其他编译器

  • 解决方案可以是预处理器级别的,也可以是可编译的。我对任何一个都没意见;但到目前为止,我所有的研究都指出了结论:C++ C++中的后者是不可能的(我是强的,所以<< /强> Perl小姐,现在我被迫做C++)< < /P>
  • 解决方案至少在某种程度上必须是“行业标准”(例如Boost是很棒的,但是Joe Quick Fingers创建的一个定制的Perl脚本并不是在他的博客上发布的。Heck,我可以很容易地写出Perl脚本,它比Perl专家更像C++的一个——我只是不能在我的大公司中获得软件工程的大人物来购买它:)
  • 解决方案应该允许我声明一个ID列表(理想情况下,只在一个头文件中声明,而不是像我在上面的示例中那样在每个“#FOR_EACH_WORD”指令中声明)

  • 解决方案不能局限于“从DB表创建对象”构造函数。有很多函数,其中大多数不是构造函数,都需要这个

  • “在一个向量中生成所有值,然后在向量上运行‘for’循环”的解决方案是显而易见的,而且无法使用——代码在许多应用程序使用的库中,成员是公共的,很遗憾,将这些应用程序重新编写为使用向量成员而不是命名成员是不可能的


    • 你可以像他那样做:

      #define DOTHAT(m) m = map[#m]
      DOTHAT(member1); DOTHAT(member2);
      #undef DOTHAT
      

      这不完全符合您的描述,但与它最接近,可以节省您的键入。

      您可以这样做:创建一个适配器类或修改现有类,使其具有指向这些字段的指针向量,将所有相关成员变量的地址添加到类构造函数中的向量,然后在需要时在该向量上运行for循环。这样,您就不会(或几乎不会)更改外部用户的类,并且具有良好的for循环功能。

      您可以使用预处理器定义成员,然后使用相同的定义访问它们:

      #define MEMBERS\
        MEMBER( int, value )\
        SEP MEMBER( double, value2 )\
        SEP MEMBER( std::string, value3 )\
      
      struct FluctuatingMembers {
      #define SEP ;
      #define MEMBER( type, name ) type name
      MEMBERS
      #undef MEMBER
      #undef SEP
      };
      
      
      .. client code:
      FluctuatingMembers f = { 1,2., "valuesofstringtype" };
      std::cout <<
        #define SEP <<
        #define MEMBER( type, name ) #name << ":" << f.##name
        MEMBERS;
        #undef MEMBER
        #undef SEP
      
      #定义成员\
      成员(int,value)\
      SEP成员(双重,价值2)\
      SEP成员(标准::字符串,值3)\
      结构成员{
      #定义SEP;
      #定义成员(类型、名称)类型名称
      成员
      #未定义成员
      #未定义SEP
      };
      .. 客户端代码:
      波动成员f={1,2.,“valuesofstringtype”};
      
      std::cout在您自己的机器上秘密地使用perl来创建构造函数。然后要求加薪,因为你成功地维护了这么多代码。

      你还可以实现一个基于成员指针的访问者模式。在预处理器解决方案之后,这个解决方案变得更易于调试

      struct FluctuatingMembers {
          int v1;
          double v2;
          std::string v3;
          template<typename Visitor> static void each_member( Visitor& v );
      };
      
      template<typename Visitor> void FluctuatingMembers::each_member( Visitor& v ) {
        v.accept( &FluctuatingMembers::v1 );
        v.accept( &FluctuatingMembers::v2 );
        v.accept( &FluctuatingMembers::v3 );
      }
      
      
      struct Printer {
          FluctuatingMembers& f;
          template< typename pt_member > void accept( pt_member m ) const {
              std::cout << (f::*m) << "\n";
          }
      };
      
      // you can even use this approach for visiting
      // multiple objects simultaneously
      struct MemberComparer {
      
          FluctuatingMembers& f1, &f2;
          bool different;
          MemberComparer( FluctuatingMembers& f1, FluctuatingMembers& f2 )
            : f1(f1),f2(f2)
            ,different(false)
          {}
      
          template< typename pt_member > void accept( pt_member m ) {
            if( (f1::*m) != (f2::*m) ) different = true;          
          }
      };
      
      ... client code:
      FluctuatingMembers object1 = { 1, 2.2, "value2" }
                       , object2 = { 1, 2.2, "valuetoo" };
      
      Comparer compare( object1, object2 );
      FluctuatingMembers::each_member( compare );
      Printer pr = { object1 };
      FluctuatingMembers::each_member( pr );
      
      struct成员{
      int v1;
      双v2;
      std::stringv3;
      模板静态作废每个_成员(访客和v);
      };
      模板成员::每个成员(访客和v){
      v、 接受(&v)成员::v1);
      v、 接受(&v)成员::v2);
      v、 接受(&v)成员::v3);
      }
      结构打印机{
      波动成员&f;
      模板无效接受(pt_成员m)常量{
      
      当然,一个显而易见的问题是:为什么你的班级有100名成员?这看起来并不太正常


      假设它是正常的——你看过了吗?我自己从来没有使用过它(正如一位朋友常说的:这样做会导致黑暗面),但据我所知,它应该是这项工作的工具。

      Boost包括一个很棒的预处理器库,你可以使用它来生成这样的代码:

      #include <boost/preprocessor/repetition.hpp>
      #include <boost/preprocessor/stringize.hpp>
      #include <boost/preprocessor/cat.hpp>
      
      typedef std::map<std::string, int> SomeMap;
      
      class MyClass
      {
      public:
          int intMember1, intMember2, intMember3;
      
          MyClass(SomeMap & map) 
          {
              #define ASSIGN(z,n,_) BOOST_PP_CAT(intMember, n) = map[ BOOST_PP_STRINGIZE(BOOST_PP_CAT(intMember, n))];
              BOOST_PP_REPEAT_FROM_TO(1, 4, ASSIGN, nil)
          }
      };
      
      #包括
      #包括
      #包括
      typedef std::map SomeMap;
      类MyClass
      {
      公众:
      int intMember1、intMember2、intMember3;
      MyClass(SomeMap和map)
      {
      #定义赋值(z,n,u)BOOST_PP_CAT(intMember,n)=映射[BOOST_PP_STRINGIZE(BOOST_PP_CAT(intMember,n))];
      从(1,4,赋值,零)到(1,4,赋值,零)(1,4,赋值,零)
      }
      };
      
      我想做的可能是利用运行时多态性(动态调度)。为那些具有执行公共内容的方法的成员创建父类。成员从该父类派生其类。需要不同方法实现的成员实现自己的类。如果他们也需要执行公共内容,则在方法内部,他们可以向下转换到基类并调用其meth版本od


      然后,您在原始类中所要做的就是为每个方法调用成员。

      我建议您使用一个小型命令行应用程序,它可以用您或您的团队最精通的任何语言编写

      在源文件中添加某种模板语言。对于这种情况,您不需要实现完全成熟的模板语言
      struct FluctuatingMembers {
          int v1;
          double v2;
          std::string v3;
          template<typename Visitor> static void each_member( Visitor& v );
      };
      
      template<typename Visitor> void FluctuatingMembers::each_member( Visitor& v ) {
        v.accept( &FluctuatingMembers::v1 );
        v.accept( &FluctuatingMembers::v2 );
        v.accept( &FluctuatingMembers::v3 );
      }
      
      
      struct Printer {
          FluctuatingMembers& f;
          template< typename pt_member > void accept( pt_member m ) const {
              std::cout << (f::*m) << "\n";
          }
      };
      
      // you can even use this approach for visiting
      // multiple objects simultaneously
      struct MemberComparer {
      
          FluctuatingMembers& f1, &f2;
          bool different;
          MemberComparer( FluctuatingMembers& f1, FluctuatingMembers& f2 )
            : f1(f1),f2(f2)
            ,different(false)
          {}
      
          template< typename pt_member > void accept( pt_member m ) {
            if( (f1::*m) != (f2::*m) ) different = true;          
          }
      };
      
      ... client code:
      FluctuatingMembers object1 = { 1, 2.2, "value2" }
                       , object2 = { 1, 2.2, "valuetoo" };
      
      Comparer compare( object1, object2 );
      FluctuatingMembers::each_member( compare );
      Printer pr = { object1 };
      FluctuatingMembers::each_member( pr );
      
      #include <boost/preprocessor/repetition.hpp>
      #include <boost/preprocessor/stringize.hpp>
      #include <boost/preprocessor/cat.hpp>
      
      typedef std::map<std::string, int> SomeMap;
      
      class MyClass
      {
      public:
          int intMember1, intMember2, intMember3;
      
          MyClass(SomeMap & map) 
          {
              #define ASSIGN(z,n,_) BOOST_PP_CAT(intMember, n) = map[ BOOST_PP_STRINGIZE(BOOST_PP_CAT(intMember, n))];
              BOOST_PP_REPEAT_FROM_TO(1, 4, ASSIGN, nil)
          }
      };
      
      MyClass::MyClass(SomeMap & map) { // construct an object from a map
      ▐REPLACE_EACH, LABEL, "intMember1", "intMember2, ... , "intMemberN"
      ▐   LABEL = map["$Label"];
      }
      
      #include <boost/preprocessor/seq/for_each.hpp>
      #include <boost/preprocessor/stringize.hpp>
      
      // sequence of member names (can be declared in a separate header file)
      #define MEMBERS (foo)(bar)
      
      // macro for the map example
      #define GET_FROM_MAP(r, map, member) member = map[BOOST_PP_STRINGIZE(member)];
      
      BOOST_PP_SEQ_FOR_EACH(GET_FROM_MAP, mymap, MEMBERS)
      // generates
      // foo = mymap["foo"]; bar = mymap["bar];
      
      -------
      
      //Somewhere else, we need to print all the values on the standard output:
      #define PRINT(r, ostream, member) ostream << member << std::endl;
      
      BOOST_PP_SEQ_FOR_EACH(PRINT, std::cout, MEMBERS)
      
      class AMember{
       ......
      };
      
      class YourClass{
          AMember member1;
          AMember member2;
          ....
          AMember memberN;
          typedef AMember YourClass::* pMember_t;
          struct MetaData : public std::vector<std::pair<std::string,pMember_t>>{
              MetaData(){
                  push_back(std::make_pair(std::string("member1"),&YourClass::member1));
                  ...
                  push_back(std::make_pair(std::string("memberN"),&YourClass::memberN)); 
              }
          };
      
          static const MetaData& myMetaData() {
              static const MetaData m;//initialized once
              return m;
          }
      
          YourClass(const std::map<std::string,AMember>& m){
              const MetaData& md = myMetaData();
              for(MetaData::const_iterator i = md.begin();i!= md.end();++i){
                  this->*(i->second) = m[i->first];
              }
          }
          YourClass(const std::vector<std::pair<std::string,pMember_t>>& m){
              const MetaData& md = myMetaData();
              for(MetaData::const_iterator i = md.begin();i!= md.end();++i){
                  this->*(i->second) = m[i->first];
              }
          }
      };
      
       push_back(make_pair(std::string("newMember"),&YourClass::newMember)); 
      
       YourClass::setAllValuesTo(const AMember& value){
          const MetaData& md = myMetaData();
          for(MetaData::const_iterator i = md.begin();i!= md.end();++i){
              this->*(i->second) = value;
          }
       }
      
      struct MemberData
      {
          size_t Offset;
          const char* ID;
      };
      
      static const MemberData MyClassMembers[] = 
      {
          { offsetof(MyClass, Member1), "Member1" },
          { offsetof(MyClass, Member2), "Member2" },
          { offsetof(MyClass, Member3), "Member3" },
      };
      
      size_t GetMemberCount(void)
      {
          return sizeof(MyClassMembers)/sizeof(MyClassMembers[0]);
      }
      
      const char* GetMemberID(size_t i)
      {
          return MyClassMembers[i].ID;
      }
      
      int* GetMemberPtr(MyClass* p, size_t i) const
      {
          return (int*)(((char*)p) + MyClassMembers[i].Offset);
      }
      
      MyClass::MyClass(SomeMap& Map)
      {
          for(size_t i=0; i<GetMemberCount(); ++i)
          {
              *GetMemberPtr(i) = Map[GetMemberID(i)];
          }
      }
      
      class MyClass
      {
      public:
          MyClass(SomeMap& Map);
      
          int Member1;
          int Member2;
          int Member3;
      
          static size_t GetMemberCount(void);
          static const char* GetMemberID(size_t i);
          int* GetMemberPtr(size_t i) const;
      };
      
      #define MEMBERS\
          MEMBER( Member1 )\
          SEP MEMBER( Member2 )\
          SEP MEMBER( Member3 )\
      
      class MyClass
      {
      public:
          #define SEP ;
          #define MEMBER( name ) int name
          MEMBERS;
          #undef MEMBER
          #undef SEP
      
          // other stuff, member functions, etc
      };
      
      const MemberData MyClassMembers[] = 
      {
          #define SEP ,
          #define MEMBER( name ) { offsetof(MyClass, name), #name }
          MEMBERS
          #undef MEMBER
          #undef SEP
      };