Проблема: в C++ приходится явно регистрировать unit test функции, перед их выполнением. Другой пример: обработчики UI событий приходится регистрировать явно. При добавлении новых функций в первом или втором примере, их нужно явно вписывать в то место, где они все регистрируются.
Данный код показывает основную идею для создания автоматических регистраторов на примере регистрации unit test функций.
Для начала, сразу приведу пример использования:
// file test1.cpp
#include "autoreg.hpp"
#include <iostream>
register_func foo()
{
std::cout << "test foo\n";
return REGISTER(&foo);
}
// file test2.cpp
#include "autoreg.hpp"
#include <iostream>
register_func bar()
{
std::cout << "test bar\n";
return REGISTER(&bar);
}
// file main.cpp
#include "autoreg.hpp"
int main()
{
suite_obj().execute(); // <- Всё! Здесь выполняются тесты из файлов test1.cpp и test2.cpp
// без необходимости их явного регистрирования
}
Соответственно, расширяется этот код новыми тестами элементарно. Пишем новую тест функцию, в конце которой стоит return REGISTER(...), и этого достаточно, чтобы она добавилась в общий набор тестов.
А теперь код autoreg.hpp, который делает это возможным:
#if !defined(AUTOREG_HPP_2974920)
#define AUTOREG_HPP_2974920
#include <vector>
#include <algorithm>
#include <boost/function.hpp>
#include <boost/bind.hpp>
typedef boost::function<void ()> test_func_type;
class suite
{
public:
void add(test_func_type test)
{
tests_.push_back(test);
}
void execute()
{
std::for_each(tests_.begin(), tests_.end(), boost::mem_fn(&test_func_type::operator()));
}
private:
std::vector<test_func_type> tests_;
};
inline
suite & suite_obj()
{
static suite s;
return s;
}
class register_func {};
template <typename T, T func>
class register_impl : public register_func
{
public:
register_impl()
{
suite_obj().add(func);
}
};
template <typename T>
class func_type
{
public:
template <T func>
class func_type_param
{
public:
static register_impl<T, func> s_reg;
};
};
template <typename T>
template <T func>
register_impl<T, func> func_type<T>::func_type_param<func>::s_reg;
template <register_func (*F)(), typename T>
register_func make_register_func(T func)
{
return func_type<T>::func_type_param<F>::s_reg;
}
#define REGISTER(func) make_register_func<func>(func);
#endif // AUTOREG_HPP_2974920
Вся фишка заключается в генерации уникальных статических переменных для каждого теста. В конструкторах соответствующих уникальных классов производится необходимая регистрация (процесс которой в данном случае предельно упрощён). Идея содрана из библиотеки win32gui-lib (
http://www.torjo.com/win32gui/).