最大限度地减少C ++中的重复代码,这是一个不太直接的案例

时间:2011-12-29 23:02:37

标签: c++ dry

我正在编写一段代码,将“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);
}

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

6 个答案:

答案 0 :(得分:1)

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

typedef bool (LibraryA::Person::Validator)();
typedef string (LibraryA::Person::FieldGetter)();
typedef void (LibraryB::Person::FieldSetter)(string*);

void ApplyField(LibraryA::Person& person1, LibraryB::Person& person2, Validator vl, FieldGetter get, FieldSetter set)
{
    if (person1.vl() && [...somestuff...])
    {
        string* v = SomeOtherFunction(person1.get());
        person2.set(v);
    }
}

ApplyField(person1, person2, &LibraryA::Person::name_valid, &LibraryA::Person::name, &LibraryB::Person::set_name);
ApplyField(person1, person2, &LibraryA::Person::address_valid, &LibraryA::Person::address, &LibraryB::Person::set_address);
ApplyField(person1, person2, &LibraryA::Person::title_valid, &LibraryA::Person::title, &LibraryB::Person::set_title);

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

答案 1 :(得分:1)

这符合您的要求,但我是否会使用它是一个不同的问题。只有当巨大重复量时,我才会通过这条路径,然后我会将它与宏结合起来以简化调用代码:

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

答案 2 :(得分:1)

这个快速,肯定不是有效的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);

答案 3 :(得分:1)

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

  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);
        }
     }
  }

答案 4 :(得分:0)

如果人物对象不可变,那你就不走运了。

如果不是,请使用标记类Person::NamePerson::Address等将信息排除在方法名称之外,然后重写*_valid和{{1}使用函数重载:

*_set

答案 5 :(得分:0)

您可以将指针到成员的参数用于模板。

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