1

I work on a project with extremely low unit test culture. We have almost zero unit testing and every API is static.

To be able to unit test some of my code I create wrappers like

class ApiWrapper {

    virtual int Call(foo, bar) {
        return ApiCall(foo, bar);
    }
};

Now in my functions instead:

int myfunc() {
    APiCall(foo, bar);        
}

I do:

int myfunc(ApiWrapper* wrapper) {
    wrapper->Call(foo, bar);        
}

This way I am able to mock such functionality. The problem is that some colleagues complain that production code should not be affected from testability needs - nonsense I know, but reality.

Anyway, I believe that I read somewhere at some point that compilers are actually smart about replacing unused polymorphic behavior with a direct call ... or if there is no class that overrides a virtual method it becomes "normal".

I experimented, and on gcc 4.8 it does not inline or directly call the virtual method, but instead creates vt.

I tried to google, but I did not find anything about this. Is this a thing or do I misremember ... or I have to do something to explain this to the linker, an optimization flag or something?

Note that while in production this class is final, in the test environment it is not. This is exactly what the linker has to be smart about and detect it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gsf
  • 6,612
  • 7
  • 35
  • 64
  • 1
    I tink you might be looking for this: http://stackoverflow.com/questions/733737/are-inline-virtual-functions-really-a-non-sense – NathanOliver Dec 04 '15 at 19:11
  • 2
    It's not clear to me why you need the wrappers. – Anon Mail Dec 04 '15 at 19:14
  • to be able to mock the functionality when I test my code that depends on it – gsf Dec 04 '15 at 19:15
  • 'override final' could help –  Dec 04 '15 at 19:15
  • 1
    I'm not sure I agree with the closure. This question is not whether the call can be inlined, it's about whether the compiler can replace a virtual call with a direct call. It's the same answer (only when the compiler is certain about the object's type, so in the general case **no**) but it's not the same question, which is really the criteria for closure. – Oak Dec 04 '15 at 19:20
  • 1
    @MarkB I do not think the question is the same, even the answer might be. If we had this question before it would of save me some googling – gsf Dec 04 '15 at 19:20
  • Describe what you tried with gcc 4.8 and what didnt seem to work. Best do so in a new question with the gcc tag. And add a minimal compileable example. – Martin Ba Dec 04 '15 at 19:25
  • @MarkB this question is about link time optimization, the other is about compile time optimization, I fixed the terminology in the question – gsf Dec 04 '15 at 20:12
  • Yes, it is technically possible and compilers do substitute direct calls with indirect calls when they can. The optimizer would have to know the actual type of `wrapper` at compile time. Which generally requires that the function gets inlined. Chips are down when it lives in another TU, it almost always does, you'd then need whole-program optimization. Debuggability is the primary concern and the most likely reason your team members dislike this, a debugger usually can't resolve `wrapper` to the actual class object. Very painful when debugging code you don't know well enough. – Hans Passant Dec 05 '15 at 10:57

1 Answers1

1

The C++ compiler will only replace a polymorphic call with a direct call if it knows for certain what the actual type is.

So in the following snippet, it will be optimized:

void f() {
  ApiWrapper x;
  x.Call();  // Can be replaced
}

But in the general case, it can't:

void f(ApiWrapper* wrapper) {
  wrapper->Call();  // Cannot be replaced
}

You also added two conditions to your question:

if there is no class that overrides a virtual method it becomes "normal".

This will not help. Neither the C++ compiler nor the linker will look at the totality of classes to search whether any inheritor exists. It's futile anyway, since you can always dynamically-load an instance of a new class.

By the way, this optimization is indeed performed by some JVMs (called devirtualization) - since in Java land there's a class loader which is aware of which classes are currently loaded.

in production this class is final

That will help! Clang, for example, will convert virtual calls to non-virtual calls if the method / method's class is marked final.

Oak
  • 26,231
  • 8
  • 93
  • 152