C++ 最小化C+中的重复代码+;,一个不那么直截了当的案例

C++ 最小化C+中的重复代码+;,一个不那么直截了当的案例,c++,dry,C++,Dry,我正在编写一段代码,将“Person”对象从一个数据表示形式复制到另一个数据表示形式。每个类中的名称(名称、地址、标题)匹配,所有类型都是字符串。对于每个字段,我希望根据一些同样依赖于字段名的条件应用相同的转换。棘手的是,重复代码使用基于字段名的函数后缀。它看起来像这样: LibraryA::Person person1; LibraryB::Person person2; if (person1.name_valid() && [...somestuff...]) {

我正在编写一段代码,将“Person”对象从一个数据表示形式复制到另一个数据表示形式。每个类中的名称(名称、地址、标题)匹配,所有类型都是字符串。对于每个字段,我希望根据一些同样依赖于字段名的条件应用相同的转换。棘手的是,重复代码使用基于字段名的函数后缀。它看起来像这样:

LibraryA::Person person1;
LibraryB::Person person2;

if (person1.name_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.name())
    person2.set_name(v);
}
if (person1.address_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.address())
    person2.set_address(v);
}
if (person1.title_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.title())
    person2.set_title(v);
}
  struct trans_info {
      trans_info(bool         (S::*valid)() const,
                 std::string* (S::*get)()const,
                 void         (T::*set)(std::string*)):
        valid_(valid),
        get_(get),
        set_(set)
      {
      }
      bool         (S::*valid_)() const;
      std::string* (S::*get_)() const;
      void         (S::*set_)(std::string*);
  };

  trans_info const info[] = {
    trans_info(&S::name_valid, &S::name, &T::set_name),
    trans_info(&S::address_valid, &S::address, &T::set_address),
    trans_info(&S::title_valid, &S::title, &T::set_title),
    ...
  };

  template <typename T, int Size> T* begin(T (&array)[Size]) { return array; }
  template <typename T, int Size> T* end(T (&array)[Size])   { return array + Size; }

  transform(S const& person1, T& person2)
  {
     for (trans_info const* it(begin(info)), e(end(info)); it != end; ++it)
     {
        if ((person1.*(it->valid_))() && [...somestuff...]) {
            string *v = SomeOtherFunction(person1.*(it->get_))())
            (person2.*(it->set))(v);
        }
     }
  }

是否有技巧(或技巧:)将重复部分分解成模板?我更喜欢不涉及定义宏的解决方案(这太容易了:)

如果person对象对您来说是不可变的,那么您就倒霉了

如果不是,请使用标记类
Person::name
Person::Address
等将信息从方法名称中剔除,然后重新写入
*\u valid
*\u set
以使用函数重载:

 bool Person::is_valid( Name, std::string ) {...)
 bool Person::is_valid( Address, std::string ) {...)

如果person对象对您来说是不可变的,那么您就不走运了

如果不是,请使用标记类
Person::name
Person::Address
等将信息从方法名称中剔除,然后重新写入
*\u valid
*\u set
以使用函数重载:

 bool Person::is_valid( Name, std::string ) {...)
 bool Person::is_valid( Address, std::string ) {...)

可以使用指向模板的成员参数的指针


但我质疑字符串指针的使用,这看起来像是内存泄漏。

您可以使用指向模板成员参数的指针


但我对使用指向字符串的指针表示怀疑,这看起来可能是内存泄漏。

您可以使用指向成员函数的指针。例如(我没有检查此代码是否编译):

我认为模板不适合这里,因为所有字段的类型都相同。
我真的不知道在这种情况下你对宏有什么看法。如果需要,可以使用宏来生成对ApplyField()的调用。

可以使用指向成员函数的指针。例如(我没有检查此代码是否编译):

我认为模板不适合这里,因为所有字段的类型都相同。
我真的不知道在这种情况下你对宏有什么看法。如果需要,可以使用宏来生成对ApplyField()的调用。

这符合您的要求,但我是否使用它是另一个问题。只有在有大量重复的情况下,我才会通过此路径,然后将其与宏结合,以简化调用代码:

void test_and_set( Person const & person1, Person & person2, 
                   bool (Person::*test)() const,
                   std::string (Person::*get)() const,
                   void (Person::*set)( std::string const &) )
{
   if ( (person1.*test)() ) {
      (person2.*set)( (person1.*get)() );
   }
}
用作:

test_and_set( person1, person2, &Person::valid_name, &Person::get_name, &Person::set_name );
并结合本地宏:

#define TEST_AND_SET( p1, p2, field ) \
    test_and_set( (p1), (p2), &Person::valid_##field, &Person::get_##field, &Person::set_##field )

TEST_AND_SET( person1, person2, name );
TEST_AND_SET( person1, person2, title );
#undef TEST_AND_SET

这符合你的要求,但我是否会使用它是一个不同的问题。只有在有大量重复的情况下,我才会通过此路径,然后将其与宏结合,以简化调用代码:

void test_and_set( Person const & person1, Person & person2, 
                   bool (Person::*test)() const,
                   std::string (Person::*get)() const,
                   void (Person::*set)( std::string const &) )
{
   if ( (person1.*test)() ) {
      (person2.*set)( (person1.*get)() );
   }
}
用作:

test_and_set( person1, person2, &Person::valid_name, &Person::get_name, &Person::set_name );
并结合本地宏:

#define TEST_AND_SET( p1, p2, field ) \
    test_and_set( (p1), (p2), &Person::valid_##field, &Person::get_##field, &Person::set_##field )

TEST_AND_SET( person1, person2, name );
TEST_AND_SET( person1, person2, title );
#undef TEST_AND_SET

这是快速的,当然不是有效的C++,但是我希望你能得到这样的想法:

struct MyFunctor
{
    Person *person1, *person2;

    void operator()(void Person::*validator(), string* Person::*getter(), void Person::*setter(string *))
    {
        if (person1->*validator() && [...somestuff...])
        {
            string* v = SomeOtherFunction(person1->*getter());
            person2->*setter(v);
        }
    }
};


// Usage
MyFunctor f = { person1, person2 };

f(&Person::name_valid, &Person::name, &Person::set_name);
f(&Person::address_valid, &Person::address, &Person::set_address);
f(&Person::title_valid, &Person::title, &Person::set_title);

这是快速的,当然不是有效的C++,但是我希望你能得到这样的想法:

struct MyFunctor
{
    Person *person1, *person2;

    void operator()(void Person::*validator(), string* Person::*getter(), void Person::*setter(string *))
    {
        if (person1->*validator() && [...somestuff...])
        {
            string* v = SomeOtherFunction(person1->*getter());
            person2->*setter(v);
        }
    }
};


// Usage
MyFunctor f = { person1, person2 };

f(&Person::name_valid, &Person::name, &Person::set_name);
f(&Person::address_valid, &Person::address, &Person::set_address);
f(&Person::title_valid, &Person::title, &Person::set_title);

您可以使用指向成员对象的指针数组,将其填充为转换的源和目标,然后将转换应用于该数组中的每个条目。这可能看起来像这样:

LibraryA::Person person1;
LibraryB::Person person2;

if (person1.name_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.name())
    person2.set_name(v);
}
if (person1.address_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.address())
    person2.set_address(v);
}
if (person1.title_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.title())
    person2.set_title(v);
}
  struct trans_info {
      trans_info(bool         (S::*valid)() const,
                 std::string* (S::*get)()const,
                 void         (T::*set)(std::string*)):
        valid_(valid),
        get_(get),
        set_(set)
      {
      }
      bool         (S::*valid_)() const;
      std::string* (S::*get_)() const;
      void         (S::*set_)(std::string*);
  };

  trans_info const info[] = {
    trans_info(&S::name_valid, &S::name, &T::set_name),
    trans_info(&S::address_valid, &S::address, &T::set_address),
    trans_info(&S::title_valid, &S::title, &T::set_title),
    ...
  };

  template <typename T, int Size> T* begin(T (&array)[Size]) { return array; }
  template <typename T, int Size> T* end(T (&array)[Size])   { return array + Size; }

  transform(S const& person1, T& person2)
  {
     for (trans_info const* it(begin(info)), e(end(info)); it != end; ++it)
     {
        if ((person1.*(it->valid_))() && [...somestuff...]) {
            string *v = SomeOtherFunction(person1.*(it->get_))())
            (person2.*(it->set))(v);
        }
     }
  }
struct trans\u info{
传输信息(bool(S::*有效)常数,
std::string*(S::*get)()const,
void(T::*set)(标准::字符串*):
有效(有效),,
得到(得到),,
集合(集合)
{
}
bool(S):*有效)(const ;;
std::string*(S::*get_u1;)const;
空(S::*set_)(标准::字符串*);
};
传输信息常量信息[]={
传输信息(&S::名称有效,&S::名称,&T::设置名称),
传输信息(&S::地址有效,&S::地址,&T::设置地址),
trans_信息(&S::title_有效,&S::title,&T::set_title),
...
};
模板T*begin(T(&array)[Size]){return array;}
模板T*end(T(&array)[Size]){return array+Size;}
变换(S常量和person1、T和person2)
{
for(trans_info const*it(begin(info)),e(end(info));it!=end;+it)
{
如果((person1.*(it->valid_))()&&[…somestuff…]){
string*v=SomeOtherFunction(person1.*(it->get_))())
(person2.*(it->set))(v);
}
}
}

您可以使用指向成员对象的指针数组,用转换的源和目标填充它,然后将转换应用于该数组中的每个条目。这可能看起来像这样:

LibraryA::Person person1;
LibraryB::Person person2;

if (person1.name_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.name())
    person2.set_name(v);
}
if (person1.address_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.address())
    person2.set_address(v);
}
if (person1.title_valid() && [...somestuff...]) {
    string *v = SomeOtherFunction(person.title())
    person2.set_title(v);
}
  struct trans_info {
      trans_info(bool         (S::*valid)() const,
                 std::string* (S::*get)()const,
                 void         (T::*set)(std::string*)):
        valid_(valid),
        get_(get),
        set_(set)
      {
      }
      bool         (S::*valid_)() const;
      std::string* (S::*get_)() const;
      void         (S::*set_)(std::string*);
  };

  trans_info const info[] = {
    trans_info(&S::name_valid, &S::name, &T::set_name),
    trans_info(&S::address_valid, &S::address, &T::set_address),
    trans_info(&S::title_valid, &S::title, &T::set_title),
    ...
  };

  template <typename T, int Size> T* begin(T (&array)[Size]) { return array; }
  template <typename T, int Size> T* end(T (&array)[Size])   { return array + Size; }

  transform(S const& person1, T& person2)
  {
     for (trans_info const* it(begin(info)), e(end(info)); it != end; ++it)
     {
        if ((person1.*(it->valid_))() && [...somestuff...]) {
            string *v = SomeOtherFunction(person1.*(it->get_))())
            (person2.*(it->set))(v);
        }
     }
  }
struct trans\u info{
传输信息(bool(S::*有效)常数,
std::string*(S::*get)()const,
void(T::*set)(标准::字符串*):
有效(有效),,
得到(得到),,
集合(集合)
{
}
bool(S):*有效)(const ;;
std::string*(S::*get_u1;)const;
空(S::*set_)(标准::字符串*);
};
传输信息常量信息[]={
传输信息(&S::名称有效,&S::名称,&T::设置名称),
传输信息(&S::地址有效,&S::地址,&T::设置地址),
trans_信息(&S::title_有效,&S::title,&T::set_title),
...
};
模板T*begin(T(&array)[Size]){return array;}
模板T*end(T(&array)[Size]){return array+Size;}
变换(S常量和person1、T和person2)
{
for(trans_info const*it(begin(info)),e(end(info));it!=end;+it)
{
如果((person1.*(it->valid_))()&&[…somestuff…]){
string*v=SomeOtherFunction(person1.*(it->get_))())
(person2.*(it->set))(v);
}
}
}

至少您可以使用宏。在我所使用的更动态的语言中,我曾多次希望如此。对于每个
if
语句,
[…something…]
是否都相同?@gahooa在这些更动态的语言中,您经常可以在运行时基于变量调用方法,从而使这变得相当简单。如果只有3个字段,这与其说是重复,不如说是重复