Хранение указателей на статические переменные - PullRequest
0 голосов
/ 07 июня 2018

Я создал инфраструктуру модульного теста для c ++, которую я хочу портировать на C позже, и я столкнулся с проблемой, когда модульное тестирование просто не запускается.Модульные тесты создаются в файлах .cpp, и только один файл .cpp должен запускать все тесты.

Чтобы упростить задачу, обычно создается тест:

main.cpp

#define UNIT_TEST_IMPL // or whatever
#include "unit_test.hpp"

int main()
{
    for(const auto& test : tests_queue)
        test->run();
    return 0;
}

unit_test.hpp

#pragma once

struct Base
{
protected:
    Base() = delete;
    Base(Base* ptr);

public:
    virtual void run() = 0;
};

#if defined(UNIT_TEST_IMPL)

#include <vector>

std::vector<Base*> tests_queue;

Base::Base(Base* ptr)
{
    tests_queue.push_back(ptr);
}

#endif

test.cpp

#include "unit_test.hpp"

#include <iostream>

struct Test : Base
{
    Test()
        : Base(this)
    {}

    void run() override
    {
        std::cout << "new test" << std::endl;
    }

};

struct Test2 : Base
{
    Test2()
        : Base(this)
    {}

    void run() override
    {
        std::cout << "new test2" << std::endl;
    }
};

static Test test;
static Test2 test2;

Вопрос: почему не выполняются тесты, определенные в test.cpp (еслиЯ создаю тесты в файле main.cpp, они отлично работают)?Я предполагаю, что проблема заключается в том, как я храню указатели базы, но я не знаю.Компилятор g ++ 6.4.0

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Мои ставки переходят в какую-то форму Статическая инициализация Fiasco

Не уверен в этом, но вы не можете рассчитывать на глобальные переменные (переменные Test1 и Test2, определенные в test.cpp, инициализируютсяBefor Main запускает.

Как правило, избегайте глобальных переменных, если можете (и в этом случае, вероятно, можете).

Но в основном ваша программа не должна делать никаких предположений относительно инициализациипорядок глобальных / статических переменных. Здесь вы предполагаете, что две глобальные переменные инициализируются перед главной. Возможно, они не

0 голосов
/ 07 июня 2018

static-initialization-order-fiasco в действии:

Порядок инициализации глобальных единиц преобразования не указан, поэтому, если test, test2 будут созданы до tests_queue, более поздняя инициализация уничтожитрегистрация.

Одно из возможных исправлений:

#pragma once

struct Base
{
protected:
    Base();

public:
    virtual ~Base() = default;
    virtual void run() = 0;
};

#if defined(UNIT_TEST_IMPL) // Should be defined only once.

#include <vector>

std::vector<Base*>& get_tests_queue()
{
    static std::vector<Base*> tests_queue;
    return tests_queue;
}

Base::Base()
{
    get_tests_queue().push_back(this);
}

#endif

, поэтому ваш main.cpp будет:

#define UNIT_TEST_IMPL // or whatever
#include "unit_test.hpp"

int main()
{
    for(const auto& test : get_tests_queue())
        test->run();
}

Ваши юнит-тесты будут неизменными:

#include "unit_test.hpp"

#include <iostream>

struct Test : Base
{
    void run() override { std::cout << "new test" << std::endl; }
};

struct Test2 : Base
{
    void run() override { std::cout << "new test2" << std::endl; }
};

static Test test;
static Test2 test2;

Демо

...