在进行大量类似的方法调用时避免定义宏

时间:2017-03-30 11:36:49

标签: c++ legacy-code

以下代码使用#define宏。我想避免这种情况,但我没有看到使用C ++功能的方法更具可读性。该代码用于测试将ClassUnderTest的两个实例合并为一个的类PoorLegacyClass。这个PoorLegacyClass有许多getter和setter,出于测试目的,我需要调用很多setter(或者我呢?)。

简而言之,而不是写作

a.setValueX(20);
b.setValueX(30);

我想写点像

set(a, b, ValueX, 20, 30);
// or
set<ValueX>(a, 20, b, 30);
// or even
set(a, b, &PoorLegacyClass::setValueX, 20, 30);

这里的代码类似于我目前使用的代码:

#include <boost/test/unit_test.hpp>

#include "ClassUnderTest.h"

BOOST_AUTO_TEST_SUITE(Test_ClassUnderTest);

#define SETTER_A_B(fieldname, valueA, valueB)\
        a.set##fieldname(valueA);\
        b.set##fieldname(valueB);

struct TestContext
{
    PoorLegacyClass a, b;
};

BOOST_FIXTURE_TEST_CASE(ClassUnderTest_meaningful_description1, TestContext)
{
    SETTER_A_B(ValueX, 20, 30);
    auto result = ClassUnderTest().merge(a, b);

    BOOST_TEST(20 == result.getValueX());
}

BOOST_FIXTURE_TEST_CASE(ClassUnderTest_meaningful_description2, TestContext)
{
    SETTER_A_B(ValueY, 21, 37);
    auto result = ClassUnderTest().merge(a, b);

    BOOST_TEST(21 + 37 == result.getValueY());
}

BOOST_FIXTURE_TEST_CASE(ClassUnderTest_meaningful_description3, TestContext)
{
    SETTER_A_B(ValueZ, 12, 83);
    auto result = ClassUnderTest().merge(a, b);

    BOOST_TEST(12 + 83 == result.getValueZ());
}

BOOST_FIXTURE_TEST_CASE(ClassUnderTest_meaningful_description4, TestContext)
{
    SETTER_A_B(ValueY, 212, 37);
    SETTER_A_B(ValueX, 20, 30);
    auto result = ClassUnderTest().merge(a, b);

    BOOST_TEST(a.getValueY() == result.getValueY());
}

// more test cases that are similar to those above

BOOST_AUTO_TEST_SUITE_END();

虽然我将来会重构PoorLegacyClass,而不是这个问题的主题。但是,我想知道的是如何避免使用#define宏。

3 个答案:

答案 0 :(得分:2)

由于您愿意接受语法set(a, b, &PoorLegacyClass::setValueX, 20, 30);,因此实施此类set应该很简单:

template <class T>
void set(PoorLegacyClass &a, PoorLegacyClass &b, void (PoorLegacyClass::*setter)(T), T valForA, T valForB)
{
  (a.*setter)(valForA);
  (b.*setter)(valForB);
}

你也可以这样做,以便valFor参数在非推导的上下文中使用T,这样T只能从setter中推断出来,隐式转换就会发生在值:

template <class T>
struct NonDeduced { using type = T; }

template <class T>
void set(PoorLegacyClass &a, PoorLegacyClass &b, void (PoorLegacyClass::*setter)(T), typename NonDeduced<T>::type valForA, typename NonDeduced<T>::type valForB)

答案 1 :(得分:1)

set(a, b, ValueX, 20, 30)set<ValueX>(a, 20, b, 30)都需要某种预处理才能将"set""ValueX"连接到一个新术语中。

就我个人而言,我认为一些预处理器比大量的样板更好,但正如@Angew所说,你可以很容易地选择第三种选择:

set(a, b, &PoorLegacyClass::setValueX, 20, 30);

我想要添加的唯一内容是您可以使用可变参数模板使其适用于任意数量的对象:

template<class T> void set(void (PoorLegacyClass::*setter)(T))
{}

template<class T, typename... Args> void set(void (PoorLegacyClass::*setter)(T), 
PoorLegacyClass& obj, T val, Args&&... args)
{
    (obj.*setter)(val);
    set(setter, std::forward<Args>(args)...);
}

在这种情况下,调用语法会略有变化:

set(&PoorLegacyClass::setValueX, a, 20, b, 30);

答案 2 :(得分:0)

你可以试试这个:

  1. 使用procedure TForm1.sh1(SheetIndex: integer); Var Xlapp1,Xlrange, Sheet:Variant ; MaxRow, MaxCol,X, Y:integer ; str:string; arrData:Variant; begin try Str:=trim(form1.OpenDialog1.FileName); XLApp1 := createoleobject('excel.application'); XLApp1.Workbooks.open(Str) ; Sheet := XLApp1.WorkSheets[SheetIndex] ; MaxRow := Sheet.Usedrange.EntireRow.count ; MaxCol := sheet.Usedrange.EntireColumn.count; arrData:= Sheet.UsedRange.Value; stringgrid1.RowCount:=maxRow+1; StringGrid1.ColCount:=maxCol+1; for x := 1 to maxCol do for y := 1 to maxRow do stringgrid1.Cells[x,y]:=arrData[y, x]; XLApp1.Workbooks.close; Except on E : Exception do begin XLApp1.Workbooks.close; ShowMessage(E.Message); end; end; end; 制作结构的融合序列。
  2. 然后使用BOOST_FUSION_ADAPT_STRUCT访问第N个元素并设置其值
  3. 在上面放置一个自由函数来获取序列并使用fusion::at_c<N>来适当地访问成员。
  4. 应该避免使用宏..