Приведение к дочернему объекту для тестирования - соответствует ли этот код стандарту? - PullRequest
2 голосов
/ 03 апреля 2020

TL; DR

Является ли этот вызов функции / преобразования законным, соответствующим стандарту кодом C ++ 11/14?
Если нет, то был бы, если бы не было виртуальных функций (если std::is_standard_layout<Module> стало правдой)?
( NB: работает на каждом компиляторе, который я до сих пор тестировал ... )

class Module
{
protected:
    virtual float protectedVirtualFunction(float f) { return f*f*f; }
    float protectedFunction(float f) { return f*f; }
};

class ModuleTester : public Module // consists of ONLY aliases
{
public:
    using Module::protectedFunction;
    using Module::protectedVirtualFunction;
};

int main()
{
    Module module;  //assume this is a pre-existing instance
    // Is this legal?
    ModuleTester* testerMask = static_cast<ModuleTester*>(&module); 
    testerMask->protectedFunction(4.4f);
    testerMask->protectedVirtualFunction(4.4f);
}

Дополнительная информация

Обычно я стремлюсь протестировать классы publi c API только при написании UnitTests. В некоторых случаях - например, когда вы имеете дело с унаследованным кодом, который вы не можете изменить - просто удобнее получать доступ к закрытым членам.

Итак, если мы не можем изменить дизайн (DI, развязка ...) Я вижу следующие решения:

  1. Сделать приватных участников публичными c (скомпрометировать всю инкапсуляцию)
  2. Старый трюк: #define private public в тестовом контексте
  3. Шаблон "Tester" (как в примере кода)

Стандартный способ использования этого шаблона "Tester" будет таким, и это должно быть допустимо:

    ModuleTester tester;
    tester.protectedFunction(4.4f);
    tester.protectedVirtualFunction(4.4f);

Тем не менее, иногда у меня есть существующий экземпляр, и было бы здорово, если бы я мог просто применить эту «маску тестера» поверх него, чтобы получить доступ.

Я предполагаю, что как только я выполню виртуальные функции и остановлюсь Будучи «стандартным макетом», строго говоря, может быть «неопределенное поведение». Однако , если я определяю псевдонимы только в производном классе, я не вижу, как это может go быть неправильным, если я использую один и тот же компилятор для Module и ModuleTester.

РЕДАКТИРОВАТЬ: Я нашел похожий метод, который не использует явное приведение указателя ({ ссылка }). Это работает, возможно, менее читабельно и, вероятно, не меняет статус «легальности».

(module.*&ModuleTester::protectedVirtualFunction)(4.4f);

Другие вопросы SO, которые дают хорошие результаты: reinterpret_cast от объекта к первому члену , Как выполнить модульное тестирование защищенного метода в C ++?

1 Ответ

1 голос
/ 03 апреля 2020

Это было бы неопределенным поведением, поскольку &module не указывает на подобъект объекта ModuleTester:

8.5.1.9 Stati c cast [expr.stati c .cast]
11 Значение типа «указатель на cv1 B», где B - тип класса, может быть преобразовано в значение типа «указатель на cv2 D», где D - производный класс ( Пункт 13) из B, если cv2 - это та же квалификация cv, что и, или более высокая квалификация cv, чем cv1. Если B является виртуальным базовым классом D или базовым классом виртуального базового класса D, или если не существует действительного стандартного преобразования из «указателя на D» в «указатель на B» (7.11), программа является некорректной , Значение нулевого указателя (7.11) преобразуется в значение нулевого указателя типа назначения. Если значение типа «указатель на cv1 B» указывает на B, который на самом деле является подобъектом объекта типа D, результирующий указатель указывает на включающий объект типа D. В противном случае поведение не определено.

#define private public также не очень хорошая идея:

2 Единица перевода не должна #define или #undef имен, лексически идентичных ключевым словам, идентификаторам, перечисленным в Таблице 4, или к токенам атрибутов, описанным в 10.6.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...