Member function pointer cast question

Discussion in 'C++' started by WaterWalk, Sep 25, 2007.

  1. WaterWalk

    WaterWalk Guest

    Hello. I thought I understood member function pointers, but in fact I
    don't. Consider the following example:
    class Base
    {
    public:
    virtual ~Base() {}
    };

    class Derived : public Base
    {
    public:
    void test()
    {
    printf("Derived::test()\n");
    }
    };


    typedef void (Derived::*funcD) ();
    typedef void (Base::*funcB) ();

    int main()
    {
    funcD fd = &Derived::test;
    funcB fb = static_cast<funcB>(fd);
    Derived d;
    Base *pb = &d;
    (pb->*fb)(); // will print "Derived::test()" !
    return 0;
    }

    To my surprise, this code works! However, if the Base class doesn't
    have any virtual functions, the above code won't compile, or cause
    runtime error. At the same time, in section 15.5.1 of BS's "The C++
    Programming Language", it states that a pointer to base class member
    can be assigned to a pointer to derived class member, but now vice
    versa.

    I'm lost. Please help me figure out how it works. And show me where in
    the c++ standard allows this kind of usage, if you like. Thanks in
    advance.
     
    WaterWalk, Sep 25, 2007
    #1
    1. Advertising

  2. WaterWalk

    red floyd Guest

    WaterWalk wrote:
    > Hello. I thought I understood member function pointers, but in fact I
    > don't. Consider the following example:
    > class Base
    > {
    > public:
    > virtual ~Base() {}
    > };
    >
    > class Derived : public Base
    > {
    > public:
    > void test()
    > {
    > printf("Derived::test()\n");
    > }
    > };
    >
    >
    > typedef void (Derived::*funcD) ();
    > typedef void (Base::*funcB) ();
    >
    > int main()
    > {
    > funcD fd = &Derived::test;
    > funcB fb = static_cast<funcB>(fd);

    Don't have my copy of the Standard handy, but I believe this cast leads
    to UB.

    > Derived d;
    > Base *pb = &d;
    > (pb->*fb)(); // will print "Derived::test()" !
    > return 0;
    > }
    >
    > To my surprise, this code works!

    UB can do anything, including working "as expected".
     
    red floyd, Sep 25, 2007
    #2
    1. Advertising

  3. WaterWalk

    Jim Langston Guest

    "WaterWalk" <> wrote in message
    news:...
    > Hello. I thought I understood member function pointers, but in fact I
    > don't. Consider the following example:
    > class Base
    > {
    > public:
    > virtual ~Base() {}
    > };
    >
    > class Derived : public Base
    > {
    > public:
    > void test()
    > {
    > printf("Derived::test()\n");
    > }
    > };
    >
    >
    > typedef void (Derived::*funcD) ();
    > typedef void (Base::*funcB) ();
    >
    > int main()
    > {
    > funcD fd = &Derived::test;
    > funcB fb = static_cast<funcB>(fd);
    > Derived d;
    > Base *pb = &d;
    > (pb->*fb)(); // will print "Derived::test()" !
    > return 0;
    > }
    >
    > To my surprise, this code works! However, if the Base class doesn't
    > have any virtual functions, the above code won't compile, or cause
    > runtime error. At the same time, in section 15.5.1 of BS's "The C++
    > Programming Language", it states that a pointer to base class member
    > can be assigned to a pointer to derived class member, but now vice
    > versa.
    >
    > I'm lost. Please help me figure out how it works. And show me where in
    > the c++ standard allows this kind of usage, if you like. Thanks in
    > advance.


    It's undefined behavior. I understand why it works, but it's not guaranteed
    to work in all implementations. And next version of the compiler, it may
    not work anymore.
     
    Jim Langston, Sep 25, 2007
    #3
  4. WaterWalk

    WaterWalk Guest

    On Sep 25, 1:13 pm, WaterWalk <> wrote:
    > Hello. I thought I understood member function pointers, but in fact I
    > don't. Consider the following example:
    > class Base
    > {
    > public:
    > virtual ~Base() {}
    >
    > };
    >
    > class Derived : public Base
    > {
    > public:
    > void test()
    > {
    > printf("Derived::test()\n");
    > }
    >
    > };
    >
    > typedef void (Derived::*funcD) ();
    > typedef void (Base::*funcB) ();
    >
    > int main()
    > {
    > funcD fd = &Derived::test;
    > funcB fb = static_cast<funcB>(fd);
    > Derived d;
    > Base *pb = &d;
    > (pb->*fb)(); // will print "Derived::test()" !
    > return 0;
    >
    > }
    >
    > To my surprise, this code works! However, if the Base class doesn't
    > have any virtual functions, the above code won't compile, or cause
    > runtime error. At the same time, in section 15.5.1 of BS's "The C++
    > Programming Language", it states that a pointer to base class member
    > can be assigned to a pointer to derived class member, but now vice
    > versa.
    >
    > I'm lost. Please help me figure out how it works. And show me where in
    > the c++ standard allows this kind of usage, if you like. Thanks in
    > advance.


    I made a mistake. Base needn't to have virtual functions to make the
    code "work".
     
    WaterWalk, Sep 25, 2007
    #4
  5. WaterWalk

    WaterWalk Guest

    On Sep 25, 1:13 pm, WaterWalk <> wrote:
    > Hello. I thought I understood member function pointers, but in fact I
    > don't. Consider the following example:
    > class Base
    > {
    > public:
    > virtual ~Base() {}
    >
    > };
    >
    > class Derived : public Base
    > {
    > public:
    > void test()
    > {
    > printf("Derived::test()\n");
    > }
    >
    > };
    >
    > typedef void (Derived::*funcD) ();
    > typedef void (Base::*funcB) ();
    >
    > int main()
    > {
    > funcD fd = &Derived::test;
    > funcB fb = static_cast<funcB>(fd);
    > Derived d;
    > Base *pb = &d;
    > (pb->*fb)(); // will print "Derived::test()" !
    > return 0;
    >
    > }
    >
    > To my surprise, this code works! However, if the Base class doesn't
    > have any virtual functions, the above code won't compile, or cause
    > runtime error. At the same time, in section 15.5.1 of BS's "The C++
    > Programming Language", it states that a pointer to base class member
    > can be assigned to a pointer to derived class member, but now vice
    > versa.
    >
    > I'm lost. Please help me figure out how it works. And show me where in
    > the c++ standard allows this kind of usage, if you like. Thanks in
    > advance.


    It seems that this "technology" is useful. When I browse the wxWidgets
    source code, I find it's event mechanism may use pointer-to-member-
    function this way. When it needs to register an event handler, it'll
    cast the member function pointer to base member function pointer,
    store it somewhere, and later call it.

    So I wonder whether this is undefined behavior. If it is, then
    wxWidgets may be built on a weak base.(Maybe I am wrong here.)
     
    WaterWalk, Sep 25, 2007
    #5
  6. WaterWalk

    James Kanze Guest

    On Sep 25, 7:13 am, WaterWalk <> wrote:
    > Hello. I thought I understood member function pointers, but in fact I
    > don't. Consider the following example:
    > class Base
    > {
    > public:
    > virtual ~Base() {}
    > };


    > class Derived : public Base
    > {
    > public:
    > void test()
    > {
    > printf("Derived::test()\n");
    > }
    > };


    > typedef void (Derived::*funcD) ();
    > typedef void (Base::*funcB) ();


    > int main()
    > {
    > funcD fd = &Derived::test;
    > funcB fb = static_cast<funcB>(fd);
    > Derived d;
    > Base *pb = &d;
    > (pb->*fb)(); // will print "Derived::test()" !
    > return 0;
    > }


    > To my surprise, this code works!


    It's guaranteed by the standard. At least one major GUI
    framework used something like this for its callbacks. (But it
    surprised me as well when I first encountered it, and I had to
    verify in the standard that it was well defined.)

    > However, if the Base class doesn't have any virtual functions,
    > the above code won't compile, or cause runtime error.


    It should work, regardless. At least one major compiler (VC++),
    however, uses a broken implementation of pointers to member functions
    by
    default; if you're using VC++, you probably need the /vmg option.

    > At the same time, in section 15.5.1 of BS's "The C++
    > Programming Language", it states that a pointer to base class
    > member can be assigned to a pointer to derived class member,
    > but not vice versa.


    There's an implicit conversion of Derived::* to Base::*. You
    need a static_cast to go the other direction, and when using the
    resulting Base::*, it's undefined behavior unless the Base
    object is actually a Derived.

    > I'm lost. Please help me figure out how it works.


    How it works is the implementors problem. Normally, a pointer
    to member function will be a struct with quite a bit of
    information: whether the function is virtual or not, the actual
    address of the function, if it's not, or its index in the
    vtable, if it is, any offset or correction needed for the this
    pointer, etc. And calling through a pointer to member involves
    evaluating all this information. Alterantively, an
    implementation may use a "trampoline"; when you take the address
    of a pointer to member, it creates a small function which does
    whatever is necessary to call the function, given the address of
    the object. And conversions like the above involve modifying
    the information or generating a new trampoline.

    > And show me where in the c++ standard allows this kind of
    > usage.


    §5.2.9/10:

    An rvalue of type "pointer to member of D of type cv1 T"
    can be converted to an rvalue of type "pointer to member
    of B" of type cv2 T, where B is a base class (clause 10)
    of D, if a valid standard conversion from "pointer to
    member of B of type T" to "pointer to member of D of
    type T" exists (4.11), and cv2 is the same
    cv-qualification as, or greater cv-qualification than,
    cv1.69) The null member pointer value (4.11) is
    converted to the null member pointer value of the
    destination type. If class B contains the original
    member, or is a base or derived class of the class
    containing the original member, the resulting pointer to
    member points to the original member. Otherwise, the
    result of the cast is undefined. [ Note: although class
    B need not contain the original member, the dynamic type
    of the object on which the pointer to member is
    dereferenced must contain the original member; see 5.5.
    --end note ]

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Sep 25, 2007
    #6
  7. WaterWalk

    Pete Becker Guest

    On 2007-09-25 06:11:35 -0400, James Kanze <> said:

    >
    > There's an implicit conversion of Derived::* to Base::*.


    Typo. That should be: There's an implicit conversion of Base::* to
    Derived::*. It goes the opposite way from ordinary pointer conversions.

    > You
    > need a static_cast to go the other direction, and when using the
    > resulting Base::*, it's undefined behavior unless the Base
    > object is actually a Derived.
    >

    --
    Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
    Standard C++ Library Extensions: a Tutorial and Reference
    (www.petebecker.com/tr1book)
     
    Pete Becker, Sep 25, 2007
    #7
  8. WaterWalk

    WaterWalk Guest

    On Sep 25, 6:11 pm, James Kanze <> wrote:
    > On Sep 25, 7:13 am, WaterWalk <> wrote:
    >
    >
    >
    > > Hello. I thought I understood member function pointers, but in fact I
    > > don't. Consider the following example:
    > > class Base
    > > {
    > > public:
    > > virtual ~Base() {}
    > > };
    > > class Derived : public Base
    > > {
    > > public:
    > > void test()
    > > {
    > > printf("Derived::test()\n");
    > > }
    > > };
    > > typedef void (Derived::*funcD) ();
    > > typedef void (Base::*funcB) ();
    > > int main()
    > > {
    > > funcD fd = &Derived::test;
    > > funcB fb = static_cast<funcB>(fd);
    > > Derived d;
    > > Base *pb = &d;
    > > (pb->*fb)(); // will print "Derived::test()" !
    > > return 0;
    > > }
    > > To my surprise, this code works!

    >
    > It's guaranteed by the standard. At least one major GUI
    > framework used something like this for its callbacks. (But it
    > surprised me as well when I first encountered it, and I had to
    > verify in the standard that it was well defined.)
    >
    > > However, if the Base class doesn't have any virtual functions,
    > > the above code won't compile, or cause runtime error.

    >
    > It should work, regardless. At least one major compiler (VC++),
    > however, uses a broken implementation of pointers to member functions
    > by
    > default; if you're using VC++, you probably need the /vmg option.
    >
    > > At the same time, in section 15.5.1 of BS's "The C++
    > > Programming Language", it states that a pointer to base class
    > > member can be assigned to a pointer to derived class member,
    > > but not vice versa.

    >
    > There's an implicit conversion of Derived::* to Base::*. You
    > need a static_cast to go the other direction, and when using the
    > resulting Base::*, it's undefined behavior unless the Base
    > object is actually a Derived.
    >
    > > I'm lost. Please help me figure out how it works.

    >
    > How it works is the implementors problem. Normally, a pointer
    > to member function will be a struct with quite a bit of
    > information: whether the function is virtual or not, the actual
    > address of the function, if it's not, or its index in the
    > vtable, if it is, any offset or correction needed for the this
    > pointer, etc. And calling through a pointer to member involves
    > evaluating all this information. Alterantively, an
    > implementation may use a "trampoline"; when you take the address
    > of a pointer to member, it creates a small function which does
    > whatever is necessary to call the function, given the address of
    > the object. And conversions like the above involve modifying
    > the information or generating a new trampoline.
    >
    > > And show me where in the c++ standard allows this kind of
    > > usage.

    >
    > §5.2.9/10:
    >
    > An rvalue of type "pointer to member of D of type cv1 T"
    > can be converted to an rvalue of type "pointer to member
    > of B" of type cv2 T, where B is a base class (clause 10)
    > of D, if a valid standard conversion from "pointer to
    > member of B of type T" to "pointer to member of D of
    > type T" exists (4.11), and cv2 is the same
    > cv-qualification as, or greater cv-qualification than,
    > cv1.69) The null member pointer value (4.11) is
    > converted to the null member pointer value of the
    > destination type. If class B contains the original
    > member, or is a base or derived class of the class
    > containing the original member, the resulting pointer to
    > member points to the original member. Otherwise, the
    > result of the cast is undefined. [ Note: although class
    > B need not contain the original member, the dynamic type
    > of the object on which the pointer to member is
    > dereferenced must contain the original member; see 5.5.
    > --end note ]
    >
    > --
    > James Kanze (GABI Software) email:
    > Conseils en informatique orientée objet/
    > Beratung in objektorientierter Datenverarbeitung
    > 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


    Thanks. That makes all clear.
     
    WaterWalk, Sep 26, 2007
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Newsgroup - Ann
    Replies:
    5
    Views:
    611
    John Carson
    Jul 30, 2003
  2. glen stark
    Replies:
    2
    Views:
    711
    Ron Natalie
    Oct 10, 2003
  3. slide_o_mix
    Replies:
    0
    Views:
    425
    slide_o_mix
    Oct 15, 2003
  4. Fraser Ross
    Replies:
    4
    Views:
    1,057
    Fraser Ross
    Aug 14, 2004
  5. somenath
    Replies:
    2
    Views:
    161
    somenath
    Aug 29, 2013
Loading...

Share This Page