5

I need to use a member function pointer that takes in an argument of base class that used in other code. Well, simply I want do to [something] like the example below. This code works fine, but I wonder if such cast is always safe? I cannot do dynamic or static cast here.

#include <cstdio>                                                   

class C
{                                                           
public:                                                             
        C () : c('c') {}                                            
        virtual ~C() {}                                             

        const char c;                                               
};                                                                  

class D : public C
{                                                
public:                                                             
        D () : d('d') {}                                            
        virtual ~D() {}                                             

        const char d;                                               
};                                                                  

class A 
{                                                           
public:                                                             
        A () {}                                                     
        virtual ~A() {}                                             

        void f( C& c ) { printf("%c\n",c.c); }                      
        void g( D& d ) { printf("%c %c\n",d.c,d.d); }               
};                                                                  

int main (int argc, char const* argv[])                             
{                                                                   
        void (A::*pf)( C& c ) = &A::f;                              
        void (A::*pg)( D& d ) = reinterpret_cast<void (A::*)(D&)>(&A::f);

        A a;                                                        
        C c;                                                        
        D d;                                                        

        (a.*pf)( c );                                               
        (a.*pg)( d );                                               

        return 0;                                                   
}                                                              
4
  • 3
    You say the code works fine, but it doesn't even compile for me. Commented May 31, 2011 at 18:48
  • 1
    It compiles and (appears to) work with a reinterpret_cast; I guess the question is, is that safe? Commented May 31, 2011 at 18:51
  • Cold it be that f and g are in fact virtual? If so you can override them in some class derived from A. Much better than casting. Commented May 31, 2011 at 19:15
  • Arght, sory, i copied wrong version. I mean reintepret_cast ofc. Commented May 31, 2011 at 20:59

4 Answers 4

2

What you are trying to do cannot be done legally in C++. C++ does not support any kind of co-variance or contra-variance on function parameter types, regardless of whether this is a member function or a free function.

In your situation the proper way to implement it is to introduce an intermediate function for parameter-type-conversion purposes

class A 
{                                                           
public:          
  ...                                                   
  void f( C& c ) { printf("%c\n",c.c); }                      
  void f_with_D( D& d ) { f(d); }
  ...
};          

and make your pointer point to that intermediate function without any casts

void (A::*pg)( D& d ) = &A::f_with_D;

Now

A a;
D d;                                                        
(a.*pg)( d );

will ultimately call a.f with C subobject of object d as argument.

EDIT: Yes, it will work with function overload (if I understand your question correctly). You just have to keep in mind that with function overload in order to direct the inner call to the proper version of the function you'll have to use an explicit cast

class A 
{                                                           
public:          
  ...                                                   
  void f( C& c ) { printf("%c\n",c.c); }                      
  void f( D& d ) { f(static_cast<C&>(d)); }
  ...
};          

Without the cast you'll end up with A::f(D&) calling itself recursively.

Sign up to request clarification or add additional context in comments.

3 Comments

Unfortunately there is a lot of Ds classes and I want compiler to choose proper one. Will it work with function overload?
No it wont. So conclusion is that I need completely different solution.
It works, thanks. Currently only in test code not production one, but there is a hope:) In production code I get error: invalid static_cast from type ‘<unresolved overloaded function type>’. Maybe there is something else messing around (static_cast is done by library).
1

No, your example does not work fine.
First, you can only use dynamic_cast to cast between related class-types, not something else.
Second, even if you replace that dynamic_cast with a reinterpret_cast or C-style cast (which I assume you meant), then I get the following output:

c
c

Not really what you wanted.

Why that even works and doesn't horribly crash is because it is "safe" to cast back and forth between member-function-pointers, no information will be lost.
Why it still prints something is because the compiler sees no errors with the types, but the assembly doesn't care about types, it only cares about addresses, so it will still call A::f, because that is the pointer you saved, regardless of the type.

Interestingly, this still works even if you unrelate the classes (D doesn't inherit from C), again because assembly doesn't care about types. Changing the functions in A in the following way:

void f( C& c ) { printf("f(C& c): %c\n",c.c); }
void g( D& d ) { printf("g(D& d): %c\n",d.d); }

leads to the following output:

f(C& c): c
f(C& c): d

"How does that work? D doesn't even have a c member!". Well, again because of addresses. Both variables are at the same offset from the this pointer, namely +0. Now lets put another member in C (class simplified):

struct C{
        C () : c('c') {}
        int i; // mean
        const char c;
};

And try that again, output:

f(C& c): c
f(C& c): ╠

Yep, there we go. C::c is now at the offset +4 (+0 + sizeof int), and printf reads from there. In D, there is no such offset and printf reads from uninitialized memory. Accessing uninitialized memory, on the other hand, is undefined behaviour.

So, to finally come to the conclusion: No, this is not safe. :)

6 Comments

Well, the result of reinterpret_cast is what really what i want. pg is pointer to function f, so it should call function f. What else could it be?
@qba: Then why did you call it pg and have a g function in your A? If you want function f to be called, why did you include g in your example code?
Another example how bad is copy&paste:P I was testing c++ with those functions, and i forgot to remove g.
Okay, then what exactly do you want? If every D inherits from C anyways, why do you need different function pointers?
Because there is a lot of D classes, and I need one function to handle them all. Its only test code for large project, with external libraries which I cant modify.
|
1

The compiler should reject the code with the dynamic_cast you have written. (I think it was a typo and you meant a reinterpret_cast considering your introduction text).

With reinterpret_cast, you aren't in one of the well defined cases (which mostly involve converting to another type and then back to the original one). So we are in the unspecified realm for the result of the cast, and in the undefined one for the behavior when calling the result of the cast.

Comments

0

You need to use a reinterpret_cast to make it work. It should be safe (see remarks) in this case, but it may fail if multiple inheritance is used because pointers will need to be adjusted when passing a D as a C. The compiler needs to know that this must happen, which is not possible in this case (calling pg with a d would just skip this step, the member function would get the unmodified address of a D object).

Remarks: I said safe - well, actually it is undefined behaviour due to reinterpret_cast'ing a type to an unrelated type and using that type but it should nevertheless work on most compilers. Please, please don't do it in production code.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.