Es gibt ein Muster in einigen Legacy-Test-Framework-Code, der auf Visual C++ gebrochen zweiphasigen Look-up, verursacht Kopfschmerzen bei der Portierung auf andere Compiler. Es gibt zahlreiche Lösungen, die ich kenne, um das Problem zu beheben, aber alle erfordern "umfangreiche" strukturelle Änderungen.zwang unqualifizierte Namen zu abhängigen Werten
Während ich mir ziemlich sicher bin, dass es nicht gibt, bin ich neugierig, ob es einen "einfachen" Hack geben kann, der das gewünschte Verhalten in standardkonformen Compilern mit einem sehr kleinen Satz notwendiger Änderungen bringt.
Das Muster wird in diesem Beispiel zu sehen:
#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();
}
Die Idee ist, dass es globale Funktionen, in der Regel etwas in einem System-Header definiert (wie eine ANSI-C-Funktion oder OS bereitgestellte Funktion). Es gibt dann einen Typ, der eine Reihe alternativer Versionen davon als statische Elementfunktionen definiert. Ein Test kann entweder vom Typ mit diesen alternativen Versionen oder von einem Typ ohne Alternativen geerbt werden.
Mit der zweiphasigen Suche von Visual C++ werden die unqualifizierten Aufrufe der zu testenden Systemfunktionen bis zur Instanziierung der Vorlage verzögert. Wenn der Überschreibtyp TestOverrides
ein Basistyp des Typs Test
ist, wird die statische Elementversion von GetString
gefunden. Bei anderen Compilern, die Zweiphasen-Lookup ordnungsgemäß implementieren, wird die Version der freien Funktion während der ersten Analyse gefunden und ist bereits zu dem Zeitpunkt aufgelöst, zu dem die Vorlage instanziiert wird.
Mir sind einige relativ intrusive Lösungen für dieses Problem bekannt. Eine wäre, die NoOverrides
Art tatsächlich Wrapper zu haben, die die freien Funktionen aufrufen und dann den Anruf zu GetString
qualifizieren, um den Overrides
Vorlagenparameter, der mein erster Instinkt ist. Beispiel:
#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();
}
Offensichtlich gibt es funktionierende Lösungen für die Zweiphasen-Suche. Eine gute Anzahl dieser Tests kann ziemlich komplex sein und würde eine Menge Arbeit erfordern, um eine Struktur wie die, die ich gerade bereitgestellt habe, zu verwenden. Ich bin neugierig, ob es eine andere Lösung gibt, die weniger strukturelle Änderungen an dem Code erfordert, an den ich nicht denke.
Was SFINAE? Z.B. Fügen Sie in 'Test' etwas hinzu, wie' template decltype (T :: GetString()) GetString (int); declltype (:: GetString()) GetString (kurz); ' –
dyp
@dyp: Ja, das könnte funktionieren. Obwohl ich es als eine noch aufdringlichere Änderung betrachten würde, als nur die statischen Wrapper in 'NoOverrides' hinzuzufügen, vor allem angesichts der Tatsache, wie unbequem der durchschnittliche C++ - Programmierer (meiner Erfahrung nach) mit Konzepten wie SFINAE ist. –
habe ich sogar ausprobiert: '-fms-extensions' [mit clang] (http://clang.llvm.org/docs/UsersManual.html#microsoft-extensions) aber leider haben sie das falsch verstanden (es druckt' GLOBAL' zweimal mit Ihrem Testprogramm). Also ja, Portierung wird ein Problem sein. –