Вызначэнне C ++ структура абнаўляецца з модульнага тэставання

У мяне ёсць сямейства структур дадзеных, якія павінны быць перададзены ад аднаго пласта да ў параўнанні з выкарыстаннем падштурхоўвання :: серыялізацыі. Напрыклад

struct DataType1
{
    std::string field1;
    std::string field2;

    template
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & field1;
        ar & field2;
    }
};

Я хачу напісаць модульны тэст на гэта, проста каб пераканацца, што я не зрабіў ніякага промахі некалькі палёў (ёсць шмат структур і палёў).

Праблема заключаецца ў тым, калі дадаць новае поле ў структуры (я вызначана буду рабіць) і забудзьцеся абнавіць модульнае тэставанне, гэта поле не будзе пакрыта модульным тэставаннем.

Маё пытанне: як вызначыць, што структура (або клас) змяняецца. Мая ідэя складалася ў тым, каб выкарыстоўваць static_assert (SizeOf (DataType1) == HARD_CODED_VALUE), але пакутуе ад розніцы ў памерах структуры ў розных кампілятарах, платформы (x64, x86) і канфігурацыях (выпуск, адладка).

Любая добрая ідэя, як справіцца з гэтым?

8
Вы павінны былі б памятаць, каб абнавіць HARD_CODED_VALUE, а таксама, так што не вырашае гэтую праблему! У рэшце рэшт, калі вы не Автогенераторный ваш код і модульнае тэставанне, няма аўтаматычнага спосабу трымаць іх сінхранізацыю.
дададзена аўтар Oliver Charlesworth, крыніца
@OliCharlesworth Гэта было б па меншай меры, не ў стане ў кампіляцыі. Я мяркую, што ОП проста заклапочаны падчас выканання памылак.
дададзена аўтар Gorpik, крыніца

7 адказы

<�Р> Праблема ў тым, калі дадаць новае поле ў структуры (я вызначана буду рабіць) і забудзьцеся абнавіць модульнае тэставанне, гэта поле не будзе пакрыта модульным тэставаннем.      <�Р> Маё пытанне: як вызначыць гэтую структуру (або клас) змяняецца </р>.      <�Р> Мая ідэя складалася ў тым, каб выкарыстоўваць static_assert (SizeOf (DataType1) == HARD_CODED_VALUE) [...]

Гэта не партатыўная рашэнне (як вы самі адзначылі).

<�Р> Любая добрая ідэя, як справіцца з гэтым?

Ды: Вы можаце пачаць з абнаўленнем тэсту?

Гэта значыць, не вырашыць, што павінна ісці ў структуры, а затым дадаць яго, а затым абнавіць тэсты (калі вы не забыліся).

Замест гэтага, тэсты абнаўлення для праверкі новых сериализованными дадзеных, <�моцны>, то пераканайцеся, што абноўленыя тэсты не , і толькі затым абнавіць код, каб тэсты праходзяць.

Такі падыход (запіс/абнаўленне модульных тэстаў у пачатку) была створана (часткова) для вырашэння менавіта гэтай праблемы.

Тэст-Першы падыход мае і іншыя перавагі, а таксама:

  • <�р> ён акуратна пазбягае YAGNI
  • мінімізуе заўчасную аптымізацыю

  • <�р> яна развіваецца натуральным чынам для адсочвання функцый-паўнаты вашага прыкладання/рэалізацыі.
3
дададзена
Я не рабіць TDD сябе. Усё, што я хачу сказаць, што, калі вы павінны ўнесці змены коды (і ўжо ёсць тэст для яго) значна больш арганізаваных, каб пачаць змены шляху абнаўлення тэстаў (таму што, калі вы хочаце змяніць, вы ўжо ясна, як новы спосаб паводзіць сябе павінен быць, перш чым пачаць змяняць код).
дададзена аўтар utnapistim, крыніца
Мне вельмі падабаецца ваша прапанова, але, на жаль, не магу пазначыць яго як адказ. Я працую ў камандзе, і я буду адчуваць цяжкасці, каб пераканаць кожны член каманды, каб пісаць тэсты да фактычнага кода. Наша практыка каманда модульнага тэставання, але не настолькі фанатычна TDD.
дададзена аўтар capone, крыніца

Дадаць каментар да вызначэння класа, каб нагадаць вам, што вы павінны наладзіць сериалайзер пры павелічэнні колькасці карыстальнікаў. Існуюць абмежаванні на тое, што кампутар можа зрабіць для вас - вось чаму прагляд кода важны. Няхай усе патчы будуць разгледжаны іншым праграмістам, мае строгі набор тэстаў і спадзявацца на лепшае.

Я ўпэўнены, што вы маглі б, напрыклад, напісаць ляск убудова, які будзе пераканацца, што канкрэтны метад спасылкі кожны член структуры, але вы сапраўды патрабуецца гэта і вы можаце інвеставаць свой час у гэта?

Тым не менш, у вас ёсць бонусныя балы за тое, каб разгрузіць столькі працы на кампутары, як гэта магчыма. Нават static_assert трук добры адзін. Калі вы абараняеце яго з наборам #ifdef s для аднаго канкрэтнага ABI і архітэктуры, дзе вы будуеце досыць часта, гэта можа зрабіць вялікую працу.

2
дададзена

Як пра што-то накшталт:

// DataType1_members.h

FIELD_DEF(std::string, field1);
FIELD_DEF(std::string, field2);

// DataType1.h

struct DataType1
{
#define FIELD_DEF(type, name) type name
    #include "DataType1_members.h"
#undef FIELD_DEF

    template
    void serialize(Archive & ar, const unsigned int version)
    {
#define FIELD_DEF(type, name) ar & name
    #include "DataType1_members.h"
#undef FIELD_DEF
    }
};

Такім чынам, вам трэба толькі дадаць поля ў адным месцы.

1
дададзена
Прычыны мне не падабаецца гэта (прабачце): гэта хаваць, ён выкарыстоўвае некалькі файлаў загалоўкаў для вызначэння інтэрфейсу ў структуры, інтэрфейс не з'яўляецца відавочнай і выкарыстоўвае макрасы для генерацыі кода (код, які вы бачыце, адрозніваецца ад кода вашай кампілятар і адладчык см).
дададзена аўтар utnapistim, крыніца
Я ведаю :(. Я бачыў, што гэта тлумачыцца Стэфан Т. Lavavej ў сваёй серыі аб перадавой STL. Гэта цікавы прекомпилятор трук і адзінае месца, я думаю, што гэта прымальна выкарыстоўваць, знаходзіцца ў ўжо стабільны кода бібліятэкі (код, які не зменіцца, ніколі не будзе адладжана і ўжо тэсціравалі) - таму што ў кодзе, які вы чакаеце, каб захаваць змены, яна імкнецца стаць падтрыманне кашмар (па прычынах, на мой каментар вышэй) Пішам развіцця/нестабільна. код, як гэта можа марнаваць больш часу на тэхнічнае абслугоўванне.
дададзена аўтар utnapistim, крыніца
@utnapistim: Гэты «узор» на самай справе параўнальна добра вядома, па меншай меры ў C; см X макрас </я> . Як C ++ ня адлюстраванне, гэта амаль адзіны спосаб DRY для такіх рэчаў, як серыялізацыі (AFAIK).
дададзена аўтар Oliver Charlesworth, крыніца
@utnapistim - Я згодны, што гэта не самы прыгожы код, але я не думаю, што гэта цяжка падтрымліваць. Вы можаце зрабіць гэта ў адным загалоўку. Глядзіце раздзел «Знешнія спасылкі» на старонцы вікі Oli звязаны з (у прыватнасці - drdobbs .com/CPP/The-х-макра/228700289 ). І калі вы змесціце яго прама над аб'явай структуры дадзеных, гэта не будзе цяжка знайсці поля, і з падлогай-кампетэнтнае аўтазапаўнення ён нават не мае значэння.
дададзена аўтар Asaf, крыніца

У вашых юніт-тэстаў, вы маглі б зрабіць чэк static_assert з чаканым структу памер:

static_assert( sizeof(DataType1)==16, "Structure changed. Update serialize method" );

Вы павінны ўсталяваць памер структуры (лік у чэку) на платформе (або толькі для адной платформы).

0
дададзена

Спосаб craay, каб не выкарыстоўваць членаў напрамую.

Стварэнне агрэгатнага variardic шаблону. Стварэнне шаблону члена дадзеных.

Шаблон Элемент дадзеных займае тэг структуры.

Override data_member::operator^( tag ) to return a reference to T. Mqybe do the same for the free operator^( data_member< tag, T >*, tag )

Цяпер вы можаце атрымаць элемент праз гэта ^ тэг() , які выглядае як доступ да сябра. Калі вы глабальны асобнік тэг вы можаце нават выдаліць () .

У вас таксама ёсць час кампіляцыі адлюстраванне ў членах вашых дадзеных, таму вы можаце напісаць for_each_member , і пісаць увесь код серыялізацыі адзін раз і выкарыстоўваць яго для кожнага структура .

Кантроль доступу і іншыя катэгорыі data_member можа быць зроблена ў агрэгаце шаблон.

Пазнака на аснове будаўніцтва дадзеных можа быць зроблена з дапамогай складанай і фантазіі агрэгатнага Канструктар.

У якасці альтэрнатывы вы можаце чакаць рэальнага адлюстравання, каб паказаць у C ++, верагодна, на працягу дзесяці гадоў.

У якасці альтэрнатывы вы можаце ператварыць ваш структура у картэж абгортку, і выкарыстоўваць нешта накшталт вышэй пераазначэння трук, каб атрымаць гэта ^ тэг працаваць на імя аснове доступ.

Калі мы маем структура Foo , які змяшчае Int х, у і двайны d мы хочам зрабіць гэта, мы маглі б зрабіць наступнае:

// boilerplate
template struct Tag {};
template
auto operator^(C&& lhs, Tag const&>)
  -> decltype( std::get( std::forward(lhs) )
{ return std::get( std::forward(lhs); }

struct foo:std::tuple< int, int, double > {};
Tag< foo, 0 > x;//another annoying part: need to manually number them
Tag< foo, 1 > y;//we can avoid this via an aggregate trick, but
Tag< foo, 2 > d;//even that isn't all that pretty
int main() {
  foo bar;
  bar^x = 7;
  bar^y = 3;
  bar^d = 3.14;
}

адзін (сур'ёзны) пытанне ў тым, што дзве зменныя-члены ў двух розных структура s адны і тыя ж «імёнаў» і канфлікт, калі яны маюць такое ж імя.

0
дададзена

Вы можаце проста дадаць статычную зменную «версію» для вашых структур і павялічыць яго, калі структура змянілася.

static int version = 1234;

Тады ў тэстах проста напісаць

static_assert( DataType1::version == HARD_CODE_VALUE );

Але вы ўсё роўна можаце забыцца абнавіць версію, калі структура змены, ці забыцца дадаць некаторыя з новых чальцоў, калі будзе абнаўляць тэст.

0
дададзена

У мяне была аналагічная праблема, і я знайшоў сваё рашэнне ў выкарыстанні наддува :: фьюжн. Тут вы можаце перабрацца ўсе члены вашай структуры. Такім чынам, няма неабходнасці рабіць больш ўручную. А таксама вы атрымаеце добры прыкмета часу кампіляцыі самааналізу, а таксама. Так лёгка раздрукаваць змесціва поўнай структуры, напрыклад. у лог-файл з дапамогай liitle кода шаблону.

0
дададзена