There's a pattern in some legacy test framework code that depends on Visual C++'s broken two-phase look up, causing headaches when porting to other compilers. There are numerous solutions that I know of to fix the issue but all require "extensive" structural changes.
While I'm fairly certain that there isn't, I am curious if there may be an "easy" hack that gets the desired behavior in standards-compliant compilers with a very small set of required changes.
The pattern is seen in this example:
#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();
}
The idea is that there are global functions, usually something defined in a system header (such as an ANSI C function or OS-provided function). There is then a type that defines a bunch of alternate versions of this as static member functions. A test can inherit either from the type that has these alternate versions or a type that has no alternatives.
With Visual C++'s broken two-phase lookup, the unqualified calls to the system functions being tested are delayed until template instantiation. If the overrides type TestOverrides
is a base type of the Test
type, then the static member version of GetString
is found. With other compilers that properly implement two-phase lookup, the free function version is found during initial parsing and is already resolved by the time the template is instantiated.
I'm well aware of some relatively intrusive solutions to this problem. One would be to make the NoOverrides
type actually have wrappers that call the free functions and then make the call to GetString
qualified to the Overrides
template parameter, which is my first instinct. Example:
#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();
}
Clearly there are working solutions to deal with two-phase lookup. A good number of these tests can be fairly complex and would take a lot of work to convert to use a structure like the one I just provided. I'm curious if there is another solution that requires less structural changes to the code that I'm not thinking of.
Here's my least intrusive version without SFINAE. Tested with Apple LLVM 5.1 and g++ 4.8.2. This is only one realization of the general idea described below the code.
#include <cstdio>
// global "system" function to test; generally something like `fopen` in a real test
const char* GetString() { return "GLOBAL"; }
// list all global functions that could possibly be overridden by function pointers
// (initialize them with the original function) and provide template overriding function.
struct PossibleOverridesList
{
decltype(&::GetString) GetString = &::GetString;
template<typename O>
void setOverrides() {
O::setMyOverrides(this);
};
};
// provides no overrides of the standard system functions being tested
// (setOverrides method does nothing)
struct NoOverrides { static void setMyOverrides(PossibleOverridesList* ol){}; };
// set of functions overriding the system functions being tested
// (setOverrides method sets the desired pointers to the static member functions)
struct TestOverrides {
// if this were `fopen` this might be a version that always fails
static const char* GetString() { return "OVERRIDE"; }
static void setMyOverrides(PossibleOverridesList* ol) { ol->GetString = &GetString; };
};
// test case (inheritance doesn't depend on template parameters, so it gets included in the lookup)
struct Test : PossibleOverridesList {
void Run() {
printf("%s\n", GetString());
}
};
int main() {
// test with no overrides; use the system functions
Test test1;
test1.setOverrides<NoOverrides>();
test1.Run();
// test with overrides; use test case version of system functions
Test test2;
test2.setOverrides<TestOverrides>();
test2.Run();
}
My idea is the following: Since my understanding is that you do not want to change the code inside Run()
, there is no way to look up the unmodified name from a template class. Therefore, some (callable) placeholder object must be in scope when GetString
is looked up, inside which we can still decide which function to call (if GetString()
inside Run()
binds to the global GetString()
once, we cannot do anything later to change it). This could be a function in the surrounding namespace or a function/function object/function pointer in a (non-template) base class. I chose the latter here (with the class PossibleOverridesList
) to stay as close to your original code as possible. This placeholder by default calls the original version of the function but can be modified by classes similar to your Override classes. The only difference is that those Override classes need the additional method setMyOverrides(PossibleOverridesList*)
which sets the function pointers in the placeholder class accordingly.
The changes in main are not strictly necessary. In my code, you have to call setOverrides<...OverriderClass...>()
instead of specifying the OverriderClass in the declaration. But it should be easy to write a wrapper template class that inherits from Test and preserves the original syntax by internally calling the setOverrides
method with its template argument on itself during construction.
The code above has several advantages over the solution of providing wrappers in the NoOverrides
class (your suggestion the your question):
PossibleOverridesList
. It's easy to spot if you forgot one, because trying to set that member in the setMyOverrides
method of the corresponding Override class will not compile. So there are only two places to change something if you add another overridden method in any Override class.Run()
to use the functions from the inherited class.setOverrides()
method multiple times on the Test object to replace certain functions successively.Of course there are also limitations, for example if the original system function is not in the ::
namespace, but maybe they are not more strict than for the wrapper solution. As I said above, there are probably very different implementations that use the same concept, but I think having some form of default placeholder for the overridden methods is inevitable (although I would love to see a solution without it).
EDIT:
I just came up with an even less intrusive version that only needs the additional PossibleOverridesList
class, but no changes to Run()
, main()
. Most importantly, there is no need for the setOverrides
methods and the Test<Override>
templated inheritance from the original code is preserved!
The trick here is to use virtual methods instead of static ones and exploit virtual inheritance. This way, the function call to GetString()
in Run()
can bind to the virtual function in PossibleOverridesList
(because that inheritance does not depend on the template parameter). Then at run time, the call is dispatched to the most derived class, i.e. in the override class. This is only unambiguous because of the virtual inheritance, so, in fact, only one PossibleOverridesList
object is present in the class.
So, in summary, the minimal changes in this version are:
PossibleOverridesList
with all the functions that might be overridden (as virtual member functions), redirecting to the original system function.static
to virtual
.Test
classes inherit virtually from PossibleOverridesList in addition to any other inheritance. For NoOverride
it is not strictly necessary, but nice for a consistent pattern.Here's the code (again, tested with Apple LLVM 5.1 and g++ 4.8.2):
#include <cstdio>
// global "system" function to test; generally something like `fopen` in a real test
const char* GetString() { return "GLOBAL"; }
// list all global functions that could possibly be overridden by function pointers
struct PossibleOverridesList
{
virtual const char* GetString() {return ::GetString();};
};
// provides no overrides of the standard system functions being tested
struct NoOverrides : virtual PossibleOverridesList { };
// set of functions overriding the system functions being tested
struct TestOverrides : virtual PossibleOverridesList {
// if this were `fopen` this might be a version that always fails
virtual const char* GetString() { return "OVERRIDE"; }
};
// test case (inheritance from first class doesn't depend on template parameter, so it gets included in the lookup)
template <typename Override>
struct Test : virtual PossibleOverridesList, Override {
void Run() {
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With