Link to home
Start Free TrialLog in
Avatar of nietod
nietod

asked on

Determined if a virtual function is overloaded

Given a base class pointer to an object that may be of a derived class, is there a way to determine if the object has overidden a particular virtual function without calling that function.  (And without calling some sort of virtual querry function that the programmer hardcodes to indicate if the function has been overidden--I want something automatic.)  I have been fooling with trying to get a pointer to member for the virtual function, but so far nothing I've tried has been successful--syntax errors.
Avatar of Answers2000
Answers2000

I believe this is compiler specific

Depending on the compiler you use, you may be able to look at the object data and see.  I don't know a generic way to do this.
Avatar of jkr
Hmm, this should work if you check the vtable entries, e.g.:

if ( &CMyClass::VirtMethod == &CBaseClass::VirtMethod)
{
// not overridden...
}

Avatar of nietod

ASKER

I definitely want something portable and legal.  No virtual table exploration.  
Avatar of nietod

ASKER

jkr,
That might work  if I needed to know if a particlar class, like CMyClass, had overidden a function.  (I don't know if it works for that even.)  But my case is slightly different.  I need to know if an object is of a class that does so.  So I have a pointer to an object (might be of type CMyClass, might be something else) and any attempt to get a pointer to this virtual function yields a syntax error.  Could be I haven't found the right syntax, could be it doesn't exist.
jkr what you say is a good idea
if I understand nietod right he has something like
class A
{...
     virtual SomeType SomeFunction(SomeParameters);
.}
class B:public class A
{...
     virtual SomeType SomeFunction(SomeParameters);
.}
class C:public class A
{...
.}

GlobalFunction(A* Ptr)
{
   // here he wants to know if function is overwritten
  // I did not try but I know no reason why it should not work
  if(&Ptr->SomeFunction==&A::SomeFunction)
  {
  // not overwridden...
  }
}
and that has nothing to do with virtual table exploration


Avatar of nietod

ASKER

That's exactly what I was saying--only 10 times clearer.

The problem is that  "&Ptr->SomeFunction" doesn't compile.  I get "illegal operaton on a bound member function."  Various parenthesis didn't help either.  That error message suggests that this may be more of a language constaint than a syntax issue.  But I'm not positive yet.
only a guess:
 sometimes by mistake i write
if(ptr->SomeFunc)
insted of
if(prt->someFunc())
the compiler compiles that with no error
perhaps
if(Ptr->SomeFunc==A::SomeFunc)
works

an other guess
which part of the condition lets the compiler fail ?
&Ptr->SomeFunc
or
&A::SomeFunc
maybe its on A::SomeFunc
The compiler may be can't Interpret that
A::SomeFunc has no instanciation and its virtual

perhaps the Following then works:
GlobalFunction(A* Ptr)
{
   A Dummy;
  if(&Ptr->SomeFunction==&Dummy.SomeFunction)
  {
  // not overwridden...
  }
}

PS: Sorry for answerring but I am on vacation for the next three weeks and if that above works and you ask me to answer you have a open question for above three weeks - I am not sure how long the period is until the question is auto deleted
Well, though i am late, what i said is absolutely portable and legal (simply pointers to member functions!!!)
BTW Pointers to members can only be used in conjunction with instances of the class (for an example, see 'http://www.codeguru.com/win32/dyndllclass.shtml')
Doesn't work.
Ask Yonat...
Avatar of nietod

ASKER

jkr, your solution doesn't work because I don't knwo what class I'm working with.  Seee my response above.

I suspect that norbert's isn't any better, but I will try to be sure.

Alex, you sound very confident do you know something I don't?  (well, probably lots but...)
nietod, unless you don't have the header files for both the derived and the base class, you know about them. Check out the example URL, it definitively works ;-)
To the 'object' problem:
Pointers to members can only be used with a 'clumsy' syntax, e.g.

class CMyClass
{
public:
 void VoidMemberTakingNothing(){};
};

typedef void ( CMyClass::*PMYCLASSMETHOD)();
CMyClass c;

PMYCLASSMETHOD p = &CMyClass::VoidMemberTakingNothing;

(c.*p)();

(But better see the URL, it's elaborated a bit more there... ;-)

>> Alex, you sound very confident do you know something I don't?
Huh?  I made two comments: that the currently suggested solution doesn't work and that you should ask Yonat because she knows more about C++ than any other EE expert.  True, I'm very confident about both statements but you do know them to be true.
nietod,
I have worked out a solution this way. I have compiled and executed it with a VC++ compiler and it works. Here is the code

#include <stdio.h>
class oo{
public:
      virtual int a(){return 0;}
};
typedef int (oo::*f1)();
f1 baseptr=&oo::a;
class pp:public oo{
public:
      int a(){return 0;}
};
typedef int (pp::*f2)();
f2 childptr=&pp::a;

main(){
oo *ptr=new oo;
if(baseptr==ptr->a)
      puts("DADDY");
ptr=new pp;
if(childptr==ptr->a)
      puts("KID");
return 0;
}

Hope it helps. Thanks
pagladasu
Doesn't work right.

Try the following:

    ptr = new pp;

    if (baseptr == ptr->a)
        puts("DADDY");

    if (childptr == ptr->a)
        puts("KID");

Both cases evaluate as true.
Avatar of nietod

ASKER

Alex, that's exactly what I was going to say.  Two pointer to members are  the same when they evaluate to the same member, not necessarily a member with the same "value".  Thus in these examples they all evaluate to the same virtual function member.  (Same offset in the virutal function table.)
Did you try comp.lang.c++.moderated?  That group is full of members of the C++ ISO commitee and even Bjarne frequents it.
Well, i checked my suggestion (still a bit different ;-), but it doesn't work either...
Just again - i thought of using something like this:

#include <stdio.h>

class CSimple
{
public:
    CSimple ()  {};
    ~CSimple    ()  {};

    virtual void    VirtMeth    ()  {   printf  (   "base virtual method\n");};
};

class CDerivedOnly: public  CSimple
{
public:
    CDerivedOnly    ()  {};
    ~CDerivedOnly   ()  {};
};

class CDerivedAndOverridden:    public  CSimple
{
public:
    CDerivedAndOverridden   ()  {};
    ~CDerivedAndOverridden  ()  {};

    virtual void    VirtMeth    ()  {   printf  (   "derived virtual method\n");};
};


void    main    (   void)
{
    CSimple                 cs;
    CDerivedOnly            cdo;
    CDerivedAndOverridden   cda;

     cs.VirtMeth    ();
    cdo.VirtMeth    ();
    cda.VirtMeth    ();

    if  (   &CSimple::VirtMeth  ==  &CDerivedOnly::VirtMeth)
            printf  (   "virtual method NOT overridden\n");
     else
            printf  (   "virtual method overridden\n");
           
    if  (   &CSimple::VirtMeth  ==  &CDerivedAndOverridden::VirtMeth)
            printf  (   "virtual method NOT overridden\n");
     else
            printf  (   "virtual method overridden\n");
}

As i said, doesn't work, i just wanted to contribute it as an addition ...
I'm inclined to say that there is no portable way of doing it.
I'm inclined to agree...
Basically, I think the problem lies in the fact that there is no clear-cut direct way to obtain the address of a virtual function.
I don't know how to do this, so I'll use my usual strategies in cases like this:
1. Explain why the usual solutions do not work (some C++ mechanics).
2. Throw more objects and classes at the problem.
3. Question the question (my usual "do you really want to do that?" routine).

1. Why the usual solutions do not work.
A pointer to a virtual member function is usually implemented as an offset in the vtable, so it's not really a pointer at all. The only thing you can do with this offset, is use it with a specific object to invoke the member function, but not to get the function's address. Non-portably, you can get the address of the vtable for a specific object, add the offset, and check what's there. But I know of no portable way to do this.

2. Throw more objects and classes at the problem
You can replace the member function with a function object ("functor" or "command object").

3. Question the question
Why do you need to know if the function is overridden? Are you violating the Liskov substitution principle?
The "legal" answer for this is "I really need reflection, because I'm writing a library for XXX". So what's XXX? Is it possible to do without reflection? (tradeoffs, tradeoffs…)

Avatar of nietod

ASKER

Thanks,  

The reason I am asking this is I have a case where derived classes can handle a group of "notifications" from  base class by implenting a virtual function.  If they chose to handle the notification (overide the virutal function) then there is some "common tasks" that need to be performed at the start and end of the processing, but if they don't choose to override the function, those task should not be performed.  

I can let the overide function call procedures to perform these common tasks, but this is for a library and If a programmer forgot...it would be bad.  In addition, with this sort of approach, a derived class could not call a base class's version of the function to do some of the processing as it would call the "common code" again.  

I want to try to make this as idiot proof as possible as there will be many idiots using the library--and I'll be one of them.

I did not think of using a functor for this because this actually is for a functor (of sorts--I guess it is a "fancy functor").  It has posibilities, but I may just "give up" and require the programmer to be a little more careful.
This seems appropriate for a functor. Something like:
if (itsFunctor) {
    InitStuff();
    itsFunctor();
    CleanupStuff()
}

The user-programmer writes the a functor and passes it to the constructor or some other way.

But here is a semi-portable code (it will probably work as-is on many compilers, but it may just as well may break on others - I would advise against using it).

template <class BaseClass>
void* GetVirtualFuncPtr(    // non portable!
    const BaseClass& object,
    unsigned memberFuncIdx)
{
    // Find the pointer to the vtable
    // (assume it's at the beginning of the object and that
    // its length is the same as size_t)
    size_t vptr = *( reinterpret_cast<const size_t*>(&object) );

    // Look at the vtable as a simple array of function pointers
    typedef void (*FuncPtr)();
    FuncPtr* vtable = reinterpret_cast<FuncPtr*>(vptr);

    return vtable[memberFuncIdx];
}


template <class BaseClass, class DerivedClass>
bool OverridesVirtualFunc(
    const BaseClass& baseObject,
    const DerivedClass& object,
    unsigned memberFuncIdx)
{
    static void* basefptr = GetVirtualFuncPtr(baseObject, memberFuncIdx);
        // static is only an optimization
   
    const BaseClass& derivedObject = object;
        // ensures DerivedClass is derived from BaseClass,
        // and also handles multiple inheritence
   
    void* fptr = GetVirtualFuncPtr(derivedObject, memberFuncIdx);
    return (fptr != basefptr);
}


class Base
{
public:
    virtual void Foo()
    {
        int x = 7;
        ++x;
    }
};

class NotOverrides : public Base
{
};

class Overrides : public Base
{
public:
    void Foo()
    {
        int y = 42;
        --y;
    }
};


#include <cassert>

int main()
{
    const unsigned cBaseFooIndex = 0; // fragile whenever Base changes
   
    Base b;
    NotOverrides no;
    Overrides o;

    assert( false == OverridesVirtualFunc(b, b, cBaseFooIndex) );
    assert( false == OverridesVirtualFunc(b, no, cBaseFooIndex) );
    assert( true == OverridesVirtualFunc(b, o, cBaseFooIndex) );

    return 0;
}

>> Look at the vtable as a simple array of function pointers
Are there any compilers which break this assumption?
Probably. Also, this may work for some compilation options and break for others. Here are a few reasons for this to break, off the top of my head:

- The vtable may contain the RTTI stuff, and not only the pointers to the virtual functions. If the length of the RTTI stuff is constant, then you can cover up for it by adjusting the memberFuncIdx, though.
- The vtable may be near/far while regular pointers to functions are far/near. (this is not a dead issue - it is still important in many embedded systems. It may also be important in 64-bit architectures.)
- The vtable is in a different component (like a DLL or something) and so contains a special kind of reference which is different from a normal pointer. (OK, I'm letting my imagination run wild...)

In any case, the standard does not guarntee anything about the vtable, so you can't rely on it being one thing or another.
Avatar of nietod

ASKER

After considerable thought--well not THAT considerable, I've given up on this approach, I've decided to place the burden on getting this right on the programmers that use the library.  (That's what programming docs are for anyways.)

Since, Norbert's answer(s) was not right, I'm rejecting it.  Althought I'm not using Yonat's answers, I think they were the best, so I'd like you to answer.

They reason I'm not using that approach was that I am trying to consolidate things to reduce the number of classes and types in my library (where reasonable) in an effort to give VC a hand in compiling.  (My goal is to get it to compile the library in less time than it took to write it!)  So I had collected a bunch of function pointers that perform related tasks (fucntion pointers left over from my Pre C++ days) into a single "Functor" class that contained several virutal functions.  I don't want to add real functors to this class as, I'm leaving on vacation for a week and I'm hopping it can compile the 80,000 lines or so, while I'm gone.  
ASKER CERTIFIED SOLUTION
Avatar of yonat
yonat

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Todd, just an idea, see if you can modify this to do what you need.

class Base
{
public:
    void DoSomething(); // Do not override!

private:
    virtual void ActualOperation() {} // Users can override this one.
};

void Base::DoSomething()
{
    // Do startup processing...
    ActualOperation();
    // Do cleanup processing...
}

Users will provide customized private ActualOperation() methods.
Avatar of nietod

ASKER

I have that design in numerious places  (that is probably the way a majority of my virtual functions are used)  The problem is--and this is the first time I've encountered it--the Start-up and clean-up need to be done only if the ActualOperation() does something  (The base class version does nothing.)
Insert an intermediate class that users will derive from.
Avatar of nietod

ASKER

yeah, with another virtual function for them to actually use rather than the first one.

Of course, that's one more class in an effort to minimize the number of classes...