- PVSM.RU - https://www.pvsm.ru -
При unit-тестированиии кода рано или поздно встает вопрос тестовых данных. И если в одном случае достаточно просто несколько жестко зашитых переменных, то в других случаях необходимы сколько-нибудь большие и случайные данные. В управляемом мире нет проблем с генерацией пользовательских типов (взять тот же Autofixture), но мир C++ зачастую вызывает боль и страдание(поправьте меня, если это не так). Не так давно я познакомился с замечательной библиотекой boost::di и под ее влиянием у меня начала созревать идея библиотеки, которая позволила бы C++ программистам генерировать пользовательские типы данных, забитых случайными значаниями, и это не потребовало бы предварительного их описания. Получилось что-то вроде:
struct dummy_member{
float a;
int b;
};
struct dummy{
explicit dummy(dummy_member val, std::string c) : val_(val), c_(c) {}
private:
dummy_member val_;
std::string c_;
};
int main(int argc, char* argv){
auto d = datagen::random<dummy>();
return 0;
}
Ссылка на код (https://github.com/ukhegg/datagen [1]). Библиотека header-only,C++14. Всех интересующихся прошу под кат.
Естественно поддерживается генерация встроенных типов(char,wchar_t и прочее). При этом целочисленные типы генерируются просто как набор битов, а float и double — как сумма случайного целочисленного(int32_t и int64_t соответственно) и случайного значений в интервале от -1 до 1. Для генерации bool значения используется сравнение двух случайных целых.
std::cout << "The answer to the question of everything is:" << datagen::random<int>() << std::endl;
Для генерации пользовательских типов за основу была взята та же идея, что и в boost::di (спасибо ее автору), а именно возможность написания универсального типа any_type, неявно конвертируемого в любой другой(за редким исключения). Добавив НЕМНОГО шаблонов, получилась штука, генерирующая пользовательские типы, используя следующие средства:
struct dymmy
).struct dummy_member
). Для генерации объектов на основе определенной пользователем процедуры необходимо частично или полностью специализировать шаблон
template<> struct datagen::value_generation_algorithm<TType> {
TType get_random(random_source_base&);
};
Это добавляет возможность выносить некоторые параметры генерации типов в члены этого класса, что в свою очередь позволяет влиять на генерацию типов. Например, алгоритм генерации строк из std выглядит так:
namespace datagen{
template <class CharType, class Traits, class Allocator>
struct value_generation_algorithm<std::basic_string<CharType, Traits, Allocator>>{
using string_t = std::basic_string<CharType, Traits, Allocator>;
string_t get_random(random_source_base& r_source){...};
size_t min_size{0};
size_t max_size{30};
std::basic_string<CharType> alphabet{"abcd...6789"};
};
}
В библиотеке поддерживаются ограничители на генерируемые значения, например:
std::cout << "The answer to the question of everything is:" << random<int>(between(42,42)) << std::endl;
Есть 2 вида ограничителей:
value_generation_algorithm<T>
.При это они могут быть использованы 2 способами:
scoped_limit
и применение его к набору типов. Тогда ограничитель применяется для всех указанных типов для всей глубины генерируемого дерева типов на протяжении жизни scoped_limit-а.Для создания пользовательских ограничителей необходимо объявить структуру/класс ограничителя и реализовать одну или обе функции:
struct dummy_algorithm_limit{};
struct dummy_value_limit{};
namespace datagen{
namespace limits {
void adjust_algorithm(random_source_base&, dummy_algorithm_limit const& l, value_generation_algorithm<dummy>& a){
// здесь можно подправить параметры генерации dummy
}
void adjust_value(random_source_base&, dummy_value_limit const& l, dummy& a){
//здесь можно подправить сгенерированное значение dummy
}
}
}
Ограничения применяются в следующем порядке:
Источником энтропии в библиотеке является класс random_source_impl, использующий <random>
. Но есть возможность переопределить это, предоставив на этапе компиляции специализацию структуры random_source_instance<int>
.
На сегодня реализована(собственно то, что необходимо мне в работе) генерация следующих контейнеров из stl:
пары типов из boost:
Ограничители для них:
Тестировалось на компиляторе msvc-14.0, требует c++14. К сожалению, gcc ведет себя немного по-другому, в следствие чего код библиотеки на собрался под mingw(gcc-6.3.0), но думаю те, кто имеет с ним постоянный контакт, смогут быстро это поправить.
Библиотека лежит в открытом доступе https://github.com/ukhegg/datagen [1]. Идеи и реализации новых типов приветствуются.
Автор: Николай
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/252824
Ссылки в тексте:
[1] https://github.com/ukhegg/datagen: https://github.com/ukhegg/datagen
[2] Источник: https://habrahabr.ru/post/326576/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.