Есть шаблон в некотором устаревшем тестовом фреймворке, который зависит от двухфазного поиска Visual С++, вызывающего головные боли при переносе на другие компиляторы. Есть множество решений, которые я знаю, чтобы исправить проблему, но все они требуют "обширных" структурных изменений.
Хотя я уверен, что этого не происходит, мне любопытно, может ли быть "легкий" взлом, который получает желаемое поведение в совместимых с стандартами компиляторах с очень небольшим набором необходимых изменений.
Рисунок показан в этом примере:
#include <cstdio>
// global "system" function to test; generally something like `fopen` in a real test
const char* GetString() { return "GLOBAL"; }
// provides no overrides of the standard system functions being tested
struct NoOverrides {};
// set of functions overriding the system functions being tested
struct TestOverrides {
// if this were `fopen` this might be a version that always fails
static const char* GetString() { return "OVERRIDE"; }
};
// test case
template <typename Overrides>
struct Test : private Overrides {
void Run() {
// call to GetString is not dependent on Overrides
printf("%s\n", GetString());
}
};
int main() {
// test with no overrides; use the system functions
Test<NoOverrides> test1;
test1.Run();
// test with overrides; use test case version of system functions
Test<TestOverrides> test2;
test2.Run();
}
Идея состоит в том, что существуют глобальные функции, обычно что-то определенное в заголовке системы (например, функция ANSI C или функция, предоставляемая ОС). Затем существует тип, который определяет группу альтернативных версий этого как статические функции-члены. Тест может наследовать либо от типа с этими альтернативными версиями, либо с типом, который не имеет альтернатив.
С нарушением двухфазного поиска Visual С++ безоговорочные вызовы тестируемых системных функций задерживаются до создания шаблона. Если тип переопределения TestOverrides
является базовым типом типа Test
, то статическая версия члена GetString
найдена. С другими компиляторами, которые правильно реализуют двухфазный поиск, версия бесплатной функции найдена во время начального синтаксического анализа и уже разрешена к моменту создания шаблона.
Я хорошо знаю некоторые относительно навязчивые решения этой проблемы. Можно было бы сделать тип NoOverrides
на самом деле иметь оболочки, которые вызывают бесплатные функции, а затем сделать вызов GetString
квалифицированным параметром шаблона Overrides
, что является моим первым инстинктом. Пример:
#include <cstdio>
const char* GetString() { return "GLOBAL"; }
// wrappers to invoke the system function versions
struct NoOverrides {
static const char* GetString() { return ::GetString(); }
};
struct TestOverrides {
static const char* GetString() { return "OVERRIDE"; }
};
template <typename Overrides>
struct Test {
void Run() {
// call to GetString is made dependent on template type Overrides
printf("%s\n", Overrides::GetString());
}
};
int main() {
// test with the default overrides; use the system functions
Test<NoOverrides> test1;
test1.Run();
Test<TestOverrides> test2;
test2.Run();
}
Очевидно, что существуют рабочие решения для решения двухфазного поиска. Хорошее количество этих тестов может быть довольно сложным и потребует много работы для преобразования в структуру, подобную той, которую я только что предоставил. Мне любопытно, есть ли другое решение, требующее меньше структурных изменений кода, о котором я не думаю.