Sign in to follow this  
shukapi

func-ptr to base-class ver of virtual func (C++)

Recommended Posts

Hi. I'm trying to get a function pointer to a base-class version of a virtual function.


#include <iostream>


#include <iostream>

class Cfoo {
  public:

    virtual ~Cfoo() {}

    virtual void func() {
        std::cout << "foo";
    }

};


class Cbar : public Cfoo {
  public:

    void func() {
        std::cout << "bar";
    }

};




int main() {
    
    Cfoo foo;
    Cbar bar;
    
    std::cout << "\nfoo.func() = ";
    foo.func(); // prints "foo".

    std::cout << "\nbar.func() = ";
    bar.func(); // prints "bar".

    std::cout << "\nbar.Cfoo::func() = ";
    bar.Cfoo::func(); // prints "foo". <------- (1) This calls the version of func() that I am after.
    
    void (Cfoo::* foo_func_fp)();

    foo_func_fp = &Cfoo::func;
    std::cout << "\nbar.*&Cbar::func = ";
    (bar.*foo_func_fp)(); // prints "bar".
    
    foo_func_fp = &Cbar::Cfoo::func;
    std::cout << "\nbar.*&Cbar::Cfoo::func = ";
    (bar.*foo_func_fp)(); // prints "bar".


    void (Cbar::* bar_func_fp)();
    
    bar_func_fp = &Cbar::func;
    std::cout << "\nbar.*&Cbar::func = ";
    (bar.*bar_func_fp)(); // prints "bar".
    
    bar_func_fp = &Cbar::Cfoo::func;
    std::cout << "\nbar.*&Cbar::Cfoo::func = ";
    (bar.*bar_func_fp)(); // prints "bar". <------- (2) This is where I want to call the above (1) version of func().
    
    std::cout << "\n\n";
    
    return 0;
    
}


I am after a member function pointer that I can call on bar that will call the function that prints "foo". The function call at (1) calls the function I am after. Since I can directly call this function for a bar, shouldn't I be able to get the address of that function? NOTE: There should be a function pointer exactly like this in the Cfoo vtable, pointing to the function that prints "foo", and manually extracting this with ugly hacks would give me the result I am after. Thanks for all assistance. Details: lang = C++ compiler = g++ os = linux arch = amd64

Share this post


Link to post
Share on other sites
you have found one hell of an interesting issue.
I've tried to find a way to get a pointer to the base function (without using V-table voodoo) but there just doesn't seem to be one.

From what I've read, the C++ spec says that you should be able to reinterpret_cast the pointer to a compatible base-function pointer, but it seems that the compiler implementors left the standards commitee in the dust, as this resolution was slow to arrive.

VC++ 2005 and G++ 4.0 do not provide the expected behaviour.

the only way (which I have found) to acheive what you need is taking the Cbar instance, casting to a Cfoo pointer, then calling a void(*Cfoo::func)() fptr on the Cfoo pointer.

eg.
typedef void (*foo_func_ptr)(void);
foo_func_ptr fp = &Cfoo::func;

Cbar bar;
Cfoo *fooPtr = static_cast<Cfoo*>(&bar);
(fooPtr->*fp)();
I'm not even sure this comes close to the functionality which you require, as this just looks to me like forcing the bar instance to an ancestor type (ie. losing type information to force to compiler to call the right base function)

Share this post


Link to post
Share on other sites

#include <iostream>

class foo
{
public:
foo(): func(*this) { }

struct func_type
{
func_type(foo& this_ptr) : m_this(&this_ptr) { }

virtual void operator()()
{
std::cout << "foo" << m_this->i;
}

protected:
foo* m_this;
};

int i;
func_type func;
};


class bar : public foo
{
public:
bar(): func(*this) { }

struct func_type : foo::func_type
{
func_type(bar& this_ptr) : foo::func_type(this_ptr) { }

virtual void operator()()
{
bar* this_ptr = static_cast<bar*>(m_this);
std::cout << "bar " << this_ptr->i << ' ' << this_ptr->j;
}
};

int j;
func_type func;
};

int main()
{
foo f;
bar b;
foo::func_type foo::*func = &foo::func;
(f.*func)();
(b.*func)();
}



Its still hackish but its not as bad as relying on positions of vtables etc....

Share this post


Link to post
Share on other sites
Thanks for looking into it

I found another website entry talking about this issue:
http://www.thescripts.com/forum/thread62475.html
So I'm not holding out much hope for a solution anymore.

Although uglier, I think what I'm going to end up doing is simply making the virtual function do nothing but call a non-virtual function. Then I can simply use the pointer to the non-virtual function.

From what the post above is saying, maybe there isn't even an appropriate VTable entry in the same format as the non-virtual function pointer.



IIRC, I read from an article by Bjarne that multiple inheritance can be done efficiently with only a single vtable lookup because, when calling a sub-class function, the pointer to a member sub-class of a multiply inheriting class need not be the same as the pointer the the main class itself.

Because of this, the explanation said, sometimes the function references consist of both a regular function-pointer and an offset which details the position of the sub-class within the main class.

The article:
http://citeseer.ist.psu.edu/stroustrup99multiple.html
http://citeseer.ist.psu.edu/rd/31338903%2C329006%2C1%2C0.25%2CDownload/http://citeseer.ist.psu.edu/cache/papers/cs/15957/http:zSzzSzwww.cs.colorado.eduzSz%7EdiwanzSz5535-00zSzmi.pdf/stroustrup99multiple.pdf
I don't think this is the proper site for this document, but it's the first one I found.

I may not have the above 100%, been a while since I read the article.

I'm not at my dev PC at the moment, but it may be interesting to just check the sizes of member function pointers vs regular function pointers to see whether this extra offset info is there. Might do this tomorrow.

Anyway, to conclude: if the function pointer structure for a member function pointer is _allowed_ to have a different structure than a standard pointer, it may mean that obtaining the address for a sub-class of a multiply inherited class might not be possible if you are directly specifying which version of a virtual function is to be used .. god I'm confused.

Might re-read the article tomorrow also.

Share this post


Link to post
Share on other sites
There is no solution for this problem in standard C++. A pointer to a member function id dependant upon the class you're using to call it - if the function is vritual, you have no way to call the base class version of the method, unless you feed the invocation with a base class instance.

The good news is that you can somehow use a similar mechanism by not using virtual functions - however, that's quite dangerous. VC++ 2005 allows this:

struct foo
{
void func() { std::cout << "foo" << std::endl; }
};
struct bar : public foo
{
// shadow the foo version of foo.
void func() { std::cout << "bar" << std::endl; }
};
int main()
{
typedef void (foo::pmfn_t)();
pmfn_t pmfn;
bar my_bar;

pmfn = &foo::func;
(my_bar.*pmfn)(); // should display "foo"

// UGLY HACK
pmfn = (pmfn_t)(&bar::func);
(my_bar.*pmfn)(); // should display "bar"
}

Anyway, that's really a bad solution, and you should try to understand why you need this - because it's very likely that you have a design issue here. You should design a software with the language in mind. If your language doesn't support your design, then you failed somewhere (it's not a big failuer, but hey, if you can't code your design... [smile]).

Regards,

Share this post


Link to post
Share on other sites
thanks Emmanuel, but I really need the virtual-ness of the class.


the problem I have is kinda annoying in that the solution should be so much easier.


PROBLEM:
--------
I simply want to be able to create library files that supply the object code for template functions with specific template parameters.


DIFFICULTY:
-----------
Unfortunately, C++ templates are designed to be inline. There are ways around this but the intention is clear.

IMHO, I think this is a limited perspective, since if templates were not intended to be solely inline (and thus were more consistent with regular code), virtually all code could be done as templates.

For example, my quaternion, dynamic octree and other vector libraries all have template parameters for the base scalar (and yes I do use more than one version in the same application binary), but their object code resides in libraries, one per set of template parameters.


SOLUTION:
---------
Putting object code for the member functions (for specific template parameters) in a library is achieved by (hacky) having a source file with a single function that obtains function pointers to all the member functions (with the specified template parameters). This forces the compiler to generate an explicit (non-inline) version, which becomes available as a weak link from the object file

NOTE: A weak link simply means multiple copies of object code for the same function don't collide - I'd prefer global links but the compiler has no means to specify this.


FURTHER PROBLEM:
----------------
This worked well enough, until I came to the problem of virtual members, which is what this post is all about.




Other simpler solutions would be:
1) To simply use inlines and forget about making libraries. But all the reasons for having library files apply to my project.
2) To attempt to avoid using templates for abstract classes.

However, the above solutions would make the project and code much uglier and less consistent than the two quick hacks I otherwise need have made it (extracting function pointers to template member functions, and creating non-virtual member functions which are called by the virtual member functions, and provide the functionality that was previously in the virtual function).




PS: I re-read the article by Bjarne I posted in my last post. What I was remembering was that each vtable entry requires an extra word for an offset in case of MI. However there's nothing to say that his implementation is what's used in g++ (or MSVC). And the vtable entry's function pointer should be just like a regular function pointer, so MI issues probably have nothing to do with my problem.

This stuff is tricky to describe, so if you want clarification on anything feel free to ask.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this