Ways to detect whether a C++ virtual function has been redefined in a derived class
Asked Answered
S

9

44

In brief: From a C++ base-class pointer which points to an instance of a derived class, how can one determine at run-time whether a non-pure virtual function (with an implementation in the base class) has been re-implemented in the derived class?

The context: I am writing a C++ library to solve certain classes of mathematical equation. The library provides an Equation class with several virtual functions, which library users use as a base class for the particular equation they wish to solve. The library also provides a Solver class, which takes an Equation * as a constructor parameter. The user then writes code along the lines of:

class MyEquation : public Equation
{ ... } // equation definition here

int main()
{
  MyEquation myEqn;
  Solver solver(&myEqn);
  solver.Solve();
}

If certain combinations of the virtual functions in Equation are not redefined in the derived equation class, certain computationally expensive parts of the algorithm run by the Solver object can be omitted. I would therefore like to know, in the constructor of Solver, which functions have been redefined, and which will instead run the default implementation in Equation.

  • I would like to make this transparent to users of the library so I am not looking for a solution where, for example, the user sets some flags in the constructor of their derived equation specifying which functions have been redefined.

  • One possible solution is for the default implementations of the virtual functions in Equation to set a private flag in the Equation class; the constructor of the Solver class can then clear this flag, run the virtual function, and check the flag value to see whether the implementation in Equation has been called. I would like to avoid this though, because simply setting the flag every time the virtual function is executed slows the algorithm down a good deal (the execution of these virtual functions contributes significantly to the run time of the program, and the default implementations simply return a constant).

Schweiz answered 19/1, 2011 at 21:58 Comment(6)
Are you sure setting a flag makes any difference at all? I would imagine that it's utterly negligible compared to the overhead of invoking a virtual function call.Mariselamarish
That seems to be highly architecture-dependent. For this program, on the 64-bit Xeon server that is the target machine, the overhead for virtual function calls seems to be practically zero. On my home computer (32-bit Athlon) however, as you suspected the function call overhead was much greater than the time to set a flag.Schweiz
Doesn't the need to determine if a virtual has an override strike you as totally orthoganal to polymorphism in the first place? It certianly does me. In other words, if you need to do this, your design is broken.Antecedents
@John: optimisation is frequently in direct opposition to good design. For most programs, the balance is strongly in favour of good design (possibly at the cost of marginal slow-down). For programs like this one which are <10KLOCs but which take several days to run, there is a bigger trade-off to be made.Schweiz
@Chris: I get that. But no program needs performance so bad that it's OK to write incorrect code. You simnply can't do what you're trying to do. At least not directly.Antecedents
@Chris: are your virtual functions really virtual functions anyway ? I mean do you have code in Equation base class that calls them from more complex composite methods ? If answer is no an interface based solution looks logical (and would probably be also more efficient). If yes you can probably get back to the interface based approach by splitting Equation between an inner (povided by users) and an outer wrapper object. Not enough details provided to suggest something more concrete.Gutty
D
21

You can't check for override of virtual function portably.

You need to bring the knowledge to the Solver, preferably via type as opposed to run-time flag.

One (type-based) way is to check for presence or absence of a interface, via dynamic_cast.

A probably better way is to provide overloads of solve function, in your case the Solver constructor.

Probably better advice could be given if you provided a more concrete description of the problem. It does remind of typical situation where someone (1) needs to solve some problem P, (2) envisions technical approach X as a solution to P, (3) discovers that X doesn't cut it, and (4) asks how to make X work for a vague description of P, or even for some unrelated problem Q. The details of original problem P will often suggest a much better solution than X, and the problems of making X work, irrelevant to solving P.

Dex answered 19/1, 2011 at 22:12 Comment(5)
Thanks. I agree in general with your last paragraph - it seems to me though that in some cases the answer to X (saying "No, because...") is a more useful thing to have on SO (and possibly more useful to the question-asker, long-term) than an answer to P would be.Schweiz
Can you provide more than your own authority on this matter (i.e. support your statement You can't check for override of virtual function portably with some standardese or other source)?Misery
It's 2019 now, but I have the same problem. And I can tell you why I need it: I have an assert that checks if a certain pointer isn't nullptr, because if it is then the user forgot to call the set_sink function before calling start_input_device, and gets "warned" with a nice assert instead of crashing somewhere. However, it is OK not to call set_sink (sometimes) when some virtual function (read_from_fd) is overridden. So, my assert must look like: assert(m_ibuffer || &read_from_fd != &InputDevice::read_from_fd), where the first read_from_fd is the function that will actually beConsonant
called and the second is the one that requires m_ibuffer to be non-zero. The problem now is that I get the error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say ‘&evio::InputDevice::read_from_fd’ But I obviously can't add the qualification of the first read_from_fd, as it is unknown!Consonant
You probably need to check out policy-based design pattern rather than the currently inheritance-based Equation class. And in Solver you should be able to choose which method based on the availability of certain functions, not whether they are overridden or not.Unharness
C
18

For future reference, it turns out that GCC provides this extension: http://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html which allows checking if a method was overridden with

(void*)(obj.*(&Interface::method)) != (void*)(&Interface::method)

ICC supports this extension officially, clang's docs don't mention it but the code works and even compiles without the warning.

MSVC doesn't support this, though, so there's that.

Also, it appears to not work with methods defined in the header (i.e. inline) in a separate library if you link to a different version of the library where the implementation has changed. If I interpret the standard correctly, this is undefined behaviour (changing the implementation that is) but if the implementation stays the same, then the address should be unique too. So don't do that with inline methods.

Chamonix answered 26/3, 2014 at 17:37 Comment(3)
Thank you, this is great! I was having a similar problem, where I provide an empty default implementation of a method, and can skip certain code if I know that the method has not been overridden. If this optimization requires a GCC extension, that's fine for me. BTW, the cast to void* didn't work for me, GCC only accepted a cast to the correct function type (with Interface* as the first parameter, as described in the document you linked to).Canter
Could someone explain how this is more correct than just comparing the function pointers? ie &obj.method != &Interface::method, which does not require any extension?Safire
For me, this code fails to compile with CLANG version 6.0.0 with the following error message: reference to non-static member function must be called. @galinette: Leaving out the cast to void* makes things even worse, because without the cast, GCC also refuses to compile this code.Apparently
S
7

After only finding results that link to gcc's PMF extension, I thought there must be a proper way to do so.

I found a solution without any hacks and is at least tested to work on gcc & llvm:

#include <iostream>
#include <typeinfo>

struct A { virtual void Foo() {} };
struct B : public A { void Foo() {} };
struct C : public A { };

int main() {
    std::cout << int(typeid(&A::Foo) == typeid(&A::Foo)) << std::endl;
    std::cout << int(typeid(&A::Foo) == typeid(&B::Foo)) << std::endl;
    std::cout << int(typeid(&A::Foo) == typeid(&C::Foo)) << std::endl;
    return 0;
}

http://ideone.com/xcQOT6

PS: I actually use it in a CEventClient system. So you derive your class from CEventClient and if it overrides an event method it will automatically 'link' the event.

Scrapple answered 18/5, 2014 at 19:18 Comment(3)
Instead of comparing typeids, you can also use std::is_same<decltype(&A::Foo),decltype(&B::Foo)>::value. Unfortunately it is still a compile-time check, rather than the run-time check the OP is asking for.Mccrory
Works for MSVC aswell.Hoedown
down-voted, as this does not answer the question at all. The question was how to detect whether Foo() has been overridden given only a pointer to base A (and without knowledge of classes B and C).Misery
S
3

Even though this probably is somehow possible, I would advice not doing it. You are:

  • Violating the OOP principles. If you are given a general class/interface, you should not check for implementation details. This increases dependency between classes and is exactly the opposite of what OOP was designed for.
  • Duplicating code. Considering that you use the constants that would be otherwise returned by the Equation class.
  • Obfuscating code. Many conditions with type checks will make your program look ugly.
  • Probably doing a premature optimization. There is almost no speed difference between executing a virtual function call and a condition. Have you run your profiler to check if it's the virtual functions that are the bottleneck? It is almost never the case in a well designed application/library.
Suzannesuzerain answered 19/1, 2011 at 22:16 Comment(2)
-[Have you run your profiler to check if it's the virtual functions that are the bottleneck]- Yes.Schweiz
I like to resolve a potential miscomprehension. The OP is not saying that the virtual function call (i.e. pointer indirection) is the performance hit. He states that he can omit running expensive algorithms if he knows that the derived class is using the base class‘ default implementations.Personate
B
2

It can't be done the way you're trying. There's no way for the base class's functions to know whether or not a virtual function they implement has been overridden without being explicitly told so.

It would seem you need to go back to the drawing board. Not exactly sure how I'd solve your design issue, but what you're trying now I know simply won't work.

Barrada answered 19/1, 2011 at 22:10 Comment(0)
W
2

And what if you didn't use virtual methods ?

At this point I am think of a template-base solution. It is possible (albeit not so easy) to use templates to detect whether a given class has a method with a certain signature, or not. Once identified, you can switch, at compile-time, between the lightweight scheme and the heavy scheme.

Note that I do not suggest that all the code be templated, the templated code will only cover the Template (Design Pattern) part of the solution, and the heavy-lifting can be done with regular functions to cut on dependencies.

Also, this can be completely transparent for the clients, providing you do not alter the signature of the methods.

Wimer answered 20/1, 2011 at 7:24 Comment(0)
O
2

I don't know how to do such detection, but did you consider use of static polymorphism instead? Optimizations can be done at compile-time if every virtual method of Equation is replaced with a template "policy" with default value.

//default implementation of a virtual method turned into a functor
//which optionally takes a reference to equation
class DefaultFunctor1
{
public:
    //...
    double operator()(double x) { return log(x); }
};
template<class F1 = DefaultFunctor1>
class Equation
{
public:
    typedef F1 Functor1;
    //...
private:
    F1 f1;
};
class Solver
{
public:
    //....
    template<class Equation>
    void solve(Equation& eq)
    {
        Loki::Int2Type<
                        Loki::IsSameType<Equation::Functor1, DefaultFunctor1>::value
                       > selector;
        //choose at compile time appropriate implementation among overloads of doSolve
        doSolve(selector);
    }
private:
    //.... overloads of doSolve here
};
int main()
{
    Equation<> eq;
    Solver solver;
    solver.solve(eq); //calls optimized version
    Equation<MyFunctor> my_eq;
    solver.solve(my_eq); //calls generic version
    return 0;
}
Ornamented answered 21/1, 2011 at 17:5 Comment(0)
G
1

Perhaps this helps. It alone doesn't answer your original question, but you can augment your base class (here, Foo) to use a certain interface if it is supplied or use a default method otherwise.

#include <iostream>

struct X {
    virtual void x () = 0;
};

struct Y {
    virtual void y () = 0;
};


struct Foo {
    virtual ~ Foo () {}

    bool does_x () {
        return NULL != dynamic_cast <X*> (this);
    }

    bool does_y () {
        return NULL != dynamic_cast <Y*> (this);
    }

    void do_x () {
        dynamic_cast <X&> (*this) .x ();
    }

    void do_y () {
        dynamic_cast <Y&> (*this) .y ();
    }
};

struct foo_x : public Foo, public X {
    void x () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }
};


struct foo_y : public Foo, public Y {
    void y () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }
};

struct foo_xy : public Foo, public X, public Y {
    void x () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }

    void y () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }
};

void test (Foo & f)
{
    std :: cout << &f << " "
        << "{"
        << "X:" << f .does_x () << ", "
        << "Y:" << f .does_y ()
        << "}\n";

    if (f .does_x ())
        f .do_x ();

    if (f .does_y ())
        f .do_y ();
}

int main ()
{
    foo_x x;
    foo_y y;
    foo_xy xy;
    test (x);
    test (y);
    test (xy);
}
Gracie answered 28/1, 2011 at 14:44 Comment(0)
K
-1

What about getting pointer to the base-class function on the first use and compare it with actual one

   class Base { virtual int foo(); }
   class Derived : public Base { virtual int foo(); }

   bool Xxx::checkOverride()
   {
       int (Base::*fpA)();
       int (Base::*fpb)();

       fpA = &Base::foo;
       fpB = &Derived::foo;

       return (fpA != fpB);
   }
Keniakenilworth answered 2/11, 2013 at 20:47 Comment(1)
Your function checkOverride() ist static: It doesn't check, if a given object's foo() method is equal to Base::foo() or not. Instead, it does this check for the hard coded Derived class only.Apparently

© 2022 - 2024 — McMap. All rights reserved.