Порядок выполнения зависимостей в модуле Boost Unit Test Framework
Я пытаюсь установить зависимости в платформе Boost Unit Testing Framework. Я обнаружил, что этот поток tbat имеет пример того, как использовать метод test_unit::depen_on(). Пока все хорошо, я могу написать магию вокруг этого, чтобы сгладить это. Однако UTF не учитывает тестовые зависимости во время выполнения.
Сценарий: BOOST_AUTO_TEST_CASE A объявляется перед другим (B), и A depen_on() B Ожидаемый (желательный) результат: среда определяет сначала зависимость и запускает B, а затем A, если B преуспел. Фактический результат: A пропускается, потому что B, который еще не запущен, "провалился" (т. Е. Пока нет / ложный результат).
Теперь моя идея состояла в том, чтобы выполнить топологическую сортировку тестовых наборов / наборов, а затем запустить их в отсортированном порядке. Для этого я создал test_tree_visitor, чтобы пройтись по наборам и определить порядок члена m_members test_suite.
Однако m_members защищен и недоступен через методы. Поскольку я не могу изменить заголовки (это усложнит обновление до более новой версии и т. Д. И т. Д.), А макросы BOOST_* "жестко кодируют" класс как test_suite, я подумал о следующей хакерской атаке:
class member_accessible_test_suite : public test_suite
{
public:
const std::vector<test_unit_id> *get_members() const { return &m_members; }
};
class dependency_order_visitor : public test_tree_visitor
{
public:
virtual void visit( test_case const& tu)
{}
virtual bool test_suite_start( test_suite const& tu)
{
const member_accessible_test_suite *psuite(reinterpret_cast<const member_accessible_test_suite*>(&tu));
const std::vector<test_unit_id> *pmembers(psuite->get_members());
/* do something with pmembers */
return true;
}
virtual void test_suite_finish( test_suite const& tu)
{}
};
Смотрите разбавленную версию на Coliru
Итак, теперь на мои вопросы:
Библиотеки буста, как правило, хорошо спроектированы - я делаю фундаментальную ошибку из-за неправильного понимания дизайна модульного тестирования, требуя эту функцию?
Так как member_accessible_test_suite не имеет данных и добавляет только функции, является ли reinterpret_cast() безопасным или быстрым переулком на землю UB? В любом случае, я боюсь использовать такой ужасный хак в производстве.
Есть ли лучший способ, и если да, то в какой момент это превратилось в проблему XY?
1 ответ
Следующий способ кажется наиболее подходящим при работе с членами базового класса, которые не предоставляются каким-либо механизмом (но к ним необходимо получить доступ для некоторой близкой функциональности, а базовый класс нельзя изменять):
Ссылка: доступ к закрытым переменным-членам
Обоснование этой идеи относительно того, почему это работает, можно найти в 14.7.2p8 стандарта:
Обычные правила проверки доступа не применяются к именам, используемым для указания явных экземпляров. В частности, аргументы и имена шаблонов, используемые в деклараторе функции (включая типы параметров, типы возвращаемых данных и спецификации исключений), могут быть закрытыми типами или объектами, которые обычно недоступны, а шаблон может быть шаблоном-членом или функцией-членом, которая не будет обычно быть доступным.]
Я позволил себе перенести это на два макроса, которые могут снова пригодиться однажды.
Как и все эти обходные пути - используйте с умом!
/* The ROB_PRIVATE_MEMBER_INST() macro should be used for explicit instantiation of the template at the appropriate source/compilation unit
The ROB_PRIVATE_MEMBER_ACCESS() macro should be used for access to the variable where required
*/
#define ROB_PRIVATE_MEMBER_INST(CLASS, TYPE, MEMBER) \
template<typename T> \
struct CLASS##_##MEMBER##_rob_tag { \
typedef T CLASS::*type; \
friend type get(CLASS##_##MEMBER##_rob_tag); \
}; \
template<typename Tag, typename Tag::type M> \
struct CLASS##_##MEMBER##_rob_private \
{ \
friend typename Tag::type get(Tag) { return M; } \
}; \
template struct CLASS##_##MEMBER##_rob_private< \
CLASS##_##MEMBER##_rob_tag<TYPE> , &CLASS::MEMBER>; \
/**/
#define ROB_PRIVATE_MEMBER_ACCESS(CLASS, INSTANCE, TYPE, MEMBER) \
(INSTANCE.*get(CLASS##_##MEMBER##_rob_tag<TYPE>())) \
/**/