2016-07-20 1 views
0

Я ищу хороший способ использовать Catch для тестирования шаблонного класса. У меня есть что-то, что почти работает:Тестирование класса шаблонов C++ с использованием платформы Catch

#define RUN_ALL(fn, params) \ 
fn<uint8_t, bool>(params); \ 
fn<uint8_t, char>(params); \ 
fn<uint16_t, bool>(params); \ 
fn<uint16_t, char>(params); \ 
fn<uint32_t, bool>(params); \ 
fn<uint32_t, char>(params); \ 
fn<uint64_t, bool>(params); \ 
fn<uint64_t, char>(params); 

template<typename A, typename B> 
void test_number_one() { 
    REQUIRE(...) 
} 

TEST_CASE("Foo::Foo() works nicely", "[SmallGraph]") { 
    RUN_ALL(test_number_one) 
} 

Эта установка будет работать только до первого отказа, что хорошо, потому что это весьма вероятно, что все 8 случаев не получится точно так же. Однако было бы неплохо узнать, какой набор аргументов шаблона используется при возникновении сбоя. Моя идея заключается в том, чтобы сделать это:

#define RUN_ALL_P(fn, params) \ 
INFO("Testing <uint8_t, bool>"); \ 
fn<uint8_t, bool>(params); \ 
INFO("Testing <uint8_t, char>"); \ 
fn<uint8_t, char>(params); \ 
INFO("Testing <uint16_t, bool>"); \ 
fn<uint16_t, bool>(params); \ 
... 

Однако, я не могу использовать более чем одну INFO в RUN_ALL, так как это создает код с дублирующим идентификатором.

FOO.cpp:270:3: error: redefinition of 'scopedMessage270' 
    RUN_ALL(test_number_one); 

Любые идеи для решения проблемы, которая не требует все функции тестирования одной и той же подписи (RUN_ALL(test_number_one) на линии 270. появляется)?

(Я бы также приветствовал указатели на статьи о тестировании кода шаблона с помощью CATCH, а также предложения по поиску таких статей без получения результатов об общей обработке исключений - т. Е. Try/catch.)

+0

Короткий ответ: Используйте функторы: http://stackoverflow.com/questions/38494276/c-single-function- pointer-for-all-template-экземпляры – Zack

ответ

1

Проблема с вашим макросом заключается в том, что при ее расширении она расширяется до одной строки. Хотя я не знаю вашу рамку теста в использовании, то очевидно, что макрос делает что-то сравнимое с этим:

struct M { M(char* msg) { puts(msg); } }; // just an example class... 
#define INFO(m) M scopedMessage##__line__(msg) 

Таким образом, вы получите несколько экземпляров scopedMessage270, если вы используете макрос RUN_ALL в строке 270 .. .

Вы можете решить проблему, заменив макрос шаблоном. К сожалению, вы не можете использовать это с шаблонными функциями, так что вам придется сделать классы ваши тестовые примеры шаблонов, тоже:

template <template <typename T, typename TT > class Test > 
struct All 
{ 
    template <typename ... Parameters> 
    static void run(Parameters ... parameters) 
    { 
     Test<uint8_t, bool>::run(parameters ...); 
     Test<uint8_t, char>::run(parameters ...); 
     Test<uint16_t, bool>::run(parameters ...); 
     Test<uint16_t, char>::run(parameters ...); 
     Test<uint32_t, bool>::run(parameters ...); 
     Test<uint32_t, char>::run(parameters ...); 
     Test<uint64_t, bool>::run(parameters ...); 
     Test<uint64_t, char>::run(parameters ...); 
    } 
}; 

template<typename A, typename B> 
struct test_number_one 
{ 
    static void run() 
    { 
     // log test name 
     // run the test 
    } 
}; 
template<typename A, typename B> 
struct test_number_two 
{ 
    static void run(int n) 
    { 
     // log test name and parameter value 
     // run the test 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    All<test_number_one>::run(); 
    All<test_number_two>::run(12); 
    All<test_number_two>::run(10); 
} 

Как сейчас, в шаблоне все строки кода остаются на отдельных линиях, вы можете разместить в между любыми лесозаготовками, как вам нравится:

template <typename ... Parameters> 
static void run(Parameters ... parameters) 
{ 
    INFO("uint8_t, bool"); 
    Test<uint8_t, bool>::run(parameters ...); 
    INFO("uint8_t, char"); 
    Test<uint8_t, char>::run(parameters ...); 
// ... 
1

@Aconcagua абсолютно правильно. Мое решение аналогично, но использует функторы (как предложено @R Саху --- C++ Single function pointer for all template instances)

template<template<typename, typename> class TestFunctor, typename... Parameters> 
void testAllTypes(Parameters... parameters) { 

    INFO("Testing <uint8_t, bool>"); 
    TestFunctor<uint8_t, bool>()(parameters...); 
    INFO("Testing <uint8_t, char>"); 
    TestFunctor<uint8_t, char>()(parameters...); 

    // ... 
} 

template<typename A, typename B> 
struct testDefaultConstructor { 
    void operator()() { 
    mallGraph<A, B> sg; 
    REQUIRE(sg.numVertices() == 0); 
    REQUIRE_FALSE(sg.edgecountIsValid()); 
    REQUIRE_FALSE(sg.adjacencyMatrixIsValid());   
    } 
}; 


TEST_CASE("SmallGraph::SmallGraph() initializes instance data as expected", "[SmallGraph]") { 
    testAllTypes<testDefaultConstructor>(); 
} 
+0

Помимо возможности использовать как своего рода указатель на функцию состояния в шаблонах, под капотом нет никакой реальной разницы между оператором() и любой другой функцией ... Ваша точка: внешняя templated функция лучше, чем мой класс шаблона ... – Aconcagua

Смежные вопросы