Search code examples
c++boostcdecl

Is there a way to call member function without .* or ->* operator


Below method of calling D::foo function via pointer-to-member function will generate error: must use .* or ->* to call pointer-to-member function in 'f (...)' .. of course that is not how we call pointer-to-member functions.

The correct way of calling is (d.*f)(5); OR (p->*f)(5);

My question is, 'Is there a way to call member function of a class without the class object on left hand side? I wonder if we could pass class object (this) as regular argument?

In my mind, at end of the day (at assembly/binary level) all member functions of a class are normal functions which should operate on n + 1 arguments where (+1 is for this)

If we talk about D::foo function below, at assembly/binary level it should operate on two arguments:

  1. The class object itself (pointer to class D object called this)
  2. and the int.

so, is there a way (or hack) to call D::foo with class object passed to it as function argument instead of using . or -> or .* or ->* operators on class object?

Sample Code:

#include <iostream>
using namespace std;

class D {
    public:
        void foo ( int a ) {
            cout << "D" << endl;
        }

        int data;
};


//typedef void  __cdecl ( D::*  Func)(int);
typedef void ( D::*  Func)(int);

int main ( void ) 
{
    D d;

    Func f = &D::foo;
    f(&d, 5);

    return 1;
 }

One method is using boost bind i.e

(boost:: bind (&D::foo, &d, 5)) ();

EDIT: "Please note I am not looking for a version of this program which works, I know how to make it work"


Solution

  • You really want to call a member function without using . or ->? Really, really? Well, okay...

    Evil.h:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    struct MyStruct
    {
    #ifdef __cplusplus
        MyStruct();
    
        void method(int);
    #endif
    };
    
    #ifdef __cplusplus
    }
    #endif
    

    Evil.cc:

    #include <iostream>
    
    #include "evil.h"
    
    MyStruct::MyStruct() { std::cout << "This is MyStruct's constructor" << std::endl; }
    
    void MyStruct::method(int i) { std::cout << "You passed " << i << std::endl; }
    

    Evil.c:

    #include "evil.h"
    
    int main()
    {
        struct MyStruct my_struct;
        _ZN8MyStructC1Ev(&my_struct); /* MyStruct::MyStruct() */
    
        _ZN8MyStruct6methodEi(&my_struct, 3); /* MyStruct::method(int) */
    
        return 0;
    }
    

    This happens to work for my combination of gcc and g++ on Linux, but needless to say it relies on the platform ABI, and violates the C89 standard in calling functions of the form underscore-capital letter. It almost certainly won't work with virtual functions, and I'm not inclined to try. It may also be the most evil thing I've ever written. But still...


    EDIT: To quote the OP:

    In my mind, at end of the day (at assembly/binary level) all member functions of a class are normal functions which should operate on n + 1 arguments where (+1 is for this)

    While it's true that every compiler since CFront has done it this way, that's just an implementation detail. The C++ standard is at pains not to specify how member functions should implemented, just how they should behave.

    Because it's an implementation detail, different platforms do it in different ways. This goes beyond just name mangling. For example, the calling convention used on Linux specifies that this is passed as the first argument; other implementations (Borland, IIRC?) pass this as the last argument.

    So, if you want to treat member functions as ordinary functions with an extra this, then you have to restrict yourself to a particular ABI. This post serves an example of how you might do that (or rather, an example of why you really shouldn't do that!)

    so, is there a way (or hack) to call D::foo with class object passed to it as function argument instead of using . or -> or .* or ->* operators on class object?

    A platform-specific, disgusting dirty hack...