Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

forcing unqualified names to be dependent values

Tags:

c++

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.

like image 368
Sean Middleditch Avatar asked Feb 11 '14 20:02

Sean Middleditch


1 Answers

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):

  • With wrappers, if you have many Override classes with many overridden functions, you would have to always provide wrappers for all the functions you don't want to override. Here, there is just one global list of functions that might be overridden, 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.
  • You do not need to reproduce the function signature for the wrapper of the system function
  • You do not have to change the code inside Run() to use the functions from the inherited class.
  • I don't know whether in your original complex code for VC++ Test can actually inherit from multiple Override classes. But if it can, the wrapping solution will not work since you don't know how to qualify the method (you don't know from which inherited class it comes). Here, you can easily call the 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:

  • define PossibleOverridesList with all the functions that might be overridden (as virtual member functions), redirecting to the original system function.
  • change member methods in the override classes from static to virtual.
  • let all override and the 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();
}
like image 163
Oguk Avatar answered Oct 20 '22 13:10

Oguk