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

11

11

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"

Schenck answered 9/10, 2013 at 10:26 Comment(10)
Look at the answer of this questionOchre
There's no "magic". Somewhere in the bind result type's operator() will be something like (ptr->*fun)(arg); you just don't see it because it's buried in the library implementation.Kathleenkathlene
It's not an "artificial restriction" any more than requiring a ; after a statement is an "artificial restriction." It's just the syntax of the language.Fret
" at end of the day it is a function which takes two argument first is reference to class D object itself (this) and second is 'int'." -- no, it isn't. At the end of the day it's a function that takes an argument value and a this value, not necessarily in that order. Non-static member functions might very well use a different calling convention from non-member functions. So it's not necessarily the same as calling a regular function with this as the first parameter, because this might be passed in a different register or stack slot from where the first parameter would normally be passed.Nomadic
@Mike Seymour: Bind doesn't make a call, it just make it boost function object. Any function with the signature arg0> D& and arg1> int can call that. This is what i say magic.Schenck
@VishnuKanwar: Indeed, bind itself doesn't call the function. I meant that the object returned by bind has an operator(), which calls the function. No magic; just a class with an overloaded operator.Kathleenkathlene
@MikeSeymour: agreed for bind.Schenck
what is the use of __cdecl, __stdcall, __fastcall, or __thiscall?Schenck
@VishnuKanwar That's some weird Microsoftism to change how arguments are passed to the function. I don't know much about it, but it's documented here.Kathleenkathlene
Why is this tagged C?Pecoraro
B
21

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...

Brannon answered 1/11, 2013 at 7:36 Comment(7)
seems like you have extracted mangled symbol name directly form binary using nm. This is not what we are looking for.Schenck
agreed, the question is not clear and this answer is calling it (except the second call, doesn't have the int value, in it...)Articulation
big +1 for telling that "this" as an invisible first parameter is an implementation detail and not specified by the standard. Plus providing a working solution.Prescription
@ams: I also feel this isthe best I could get here.Schenck
I feel like this might lead to an interesting IOCCC submission.Vickivickie
@TristanBrindle: I have awarded my bounty to this answer for understanding the question and giving enough though (mainly for EDIT part :) ). I would also like to thanks you for your time and efforts you put to compose this answer.Schenck
Its really cool.. was trying to do the same in VS for a simple C++ project. a)I have 1 function inside a class outside main. ->void fun(int x); b)Got the mangled name by dumpbin /symbols mytest.obj : ?fun@@YAXH@Z c)tried calling the mangled name from main function but i am not able to call .Is it because it is starting from Questionmark????????Modiste
K
14

I'm not quite sure what you're asking. There is no "artificial restriction" on calling functions via pointers to members; you just have to use the correct syntax:

(d.*f)(5);  // for objects or references
(p->*f)(5); // for pointers

bind doesn't do any "magic"; somewhere in its implementation, it does exactly that.

at end of the day it is a function which takes two argument

No, it's a member function that takes one argument, and is called on an object. While conceptually similar to a function taking two arguments, there is one big difference: member functions can be virtual, involving a run-time dispatch mechanism.

Kathleenkathlene answered 9/10, 2013 at 10:34 Comment(0)
M
4

The only way I see is to replace your typedef-ed function pointer by a class that behaves like a function.

Are you looking for something like this?

In the following code I used a macro to typedef a template which then operates like the function you described.

#include <iostream>
using namespace std;

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

        int data;
};

template<class Object, class Param, class Function>
class FunctionPointerHelper                                           
{                                                    
private:                                             
    Function m_function;                             
public:                                              
    FunctionPointerHelper(Function function) :                        
        m_function(function)                         
    {                                                
    }                                                
    void operator=(Function function)                
    {                                                
        m_function = function;                       
    }                                                
    void operator()(Object& object, Param param) const      
    {                                                
        (object.*m_function)(param);                 
    }                                                
};

#define TYPEDEF_NICE_FUNCTION(RESULT, CLASS, NEW_TYPENAME, PARAM) \
typedef RESULT ( CLASS::* NEW_TYPENAME_INTERMEDIATE)(PARAM) ; \
typedef FunctionPointerHelper<CLASS, PARAM, NEW_TYPENAME_INTERMEDIATE> NEW_TYPENAME;

TYPEDEF_NICE_FUNCTION(void, D, Func, int)

int main ( void ) 
{
    D d;

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

    return 1;
 }
Mezzosoprano answered 6/11, 2013 at 17:41 Comment(0)
A
3

Is there a way to call member function of a class without the class object on left hand side?

Two ways without assembly:

#include <iostream>
using namespace std;

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

    static void foo(D& d, int a)
    {
        d.foo(a);
    }

        int data;
};

void foo(D& d, int a)
{
    d.foo(a);
}

int main ( void ) 
{
    D d;

    D::foo(d, 5);
    foo(d, 5);

    return 0;
}
Articulation answered 6/11, 2013 at 18:15 Comment(0)
A
2

Yes, there is an abstraction in the C++11 standard called INVOKE which applies to Callable objects. Pointer-to-member-function (PTMF) objects are Callable and this is passed as the first (regular) argument.

There is no std::invoke function, although it has been proposed. To get a functor from any Callable object, you can use std::bind, which is a derivative of Boost.bind.

This works fine:

int main ( void ) 
{
    D d;

    auto f = std::bind( &D::foo, _1, _2 );
    f(&d, 5);
 }

http://ideone.com/VPWV5U

Akiko answered 9/10, 2013 at 10:26 Comment(2)
Could you please elobrate a bit further.Schenck
@VishnuKanwar What part? It's a standard facility, similar to Boost Bind as you illustrated, but the new auto keyword makes it easy to declare an easy-to-use local object.Akiko
R
2

but there has to be a method to avoid this artificial restriction imposed by c++

What do you mean by 'artificial restriction'? It's just the syntax as the language defines it. What's wrong with it? The 'magic' of bind() will use the ->* operator internally to call the function.

Regretful answered 9/10, 2013 at 10:35 Comment(3)
It's artificial in the sense that f(D, 5) is a reasonable syntax for calling through a pointer to member function, even though the language definition went in a different direction.Eleph
@PeteBecker I tend more to follow the opinion in Steve Jessop's comment, that it might not be exactly equivalent. 'artificial' in the sense of a C programmer, maybe.Ectoplasm
You have to be very careful when thinking about the properties of things that do not exist. There's no inherent reason that the language could not provide that syntax with the exact and obvious meaning, in which case it would be exactly equivalent.Eleph
T
1

Yes, it is true that with enough contortions you can evade the "new" member function syntax, but I have to ask: why would you? The syntax fosters the understanding that the member function is somehow invoked from the surrounding object. Given the existence of virtual functions this is actually (and must be) the case. It also automates the this pointer changes the calling of virtual functions (and support for virtual inheritance) requires.

For an illustration of how you can go about doing this look at the implementation of the FastDelegate library. It's implementation is required to know the binary structure of your compiler's member function pointers (of which there can be several varieties). That's the "hack" you're looking for. FastDelegate's delegates (ie closures) turn into two instructions at the call point: pulling the computed 'this' value into the proper place (based on calling convention) and indirectly jumping to the actual function's entry address.

This ends up looking like this:

fastdelegate::FastDelegate0<> functionObject;
SomeClass someInstance;

//fill in object and function information in function object
functionObject.bind(&someInstance,&SomeClass::someFunction);

//call function via object.  Equivalent to: someInstance->someFunction();
functionObject();

This is pretty similar to what boost::bind and friends are doing, but is generally faster (though, clearly, less portable).

Underneath the templates and operator overloading used here there's some math (inside the bind function) which figures out how to alter &someInstance into the this pointer needed by SomeClass::someInstance. It also finds the actual address of the underlying function and records both values for later. When the call takes place it forces the compiler to "do the right thing" by some trickery using ->*. But, if you really wanted to avoid even relying on the ->* operator you could, at that point, do some type casts and turn the pointer into a "__thiscall" function pointer (well, assuming you are on Windows):

The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under __thiscall, the callee cleans the stack, which is impossible for vararg functions. Arguments are pushed on the stack from right to left, with the this pointer being passed via register ECX, and not on the stack, on the x86 architecture.

So, what was it exactly you didn't like about: someInstance->someFunction()?

Trudietrudnak answered 2/11, 2013 at 0:47 Comment(2)
"So, what was it exactly you didn't like about: someInstance->someFunction()?" In some sense yes. I have made my question a bit clearer if you want to re-read it. Thanks.Schenck
I'm still at a loss as to why you want to do this. Are you trying to call some C++ library from c code? If so stop trying to do this and use a c++ wrapper that exports c callable functions.Trudietrudnak
T
0

Although I think its a kind of strange thing to want, call_member looks like what you want. (Needs C++11)

#include <iostream>

template<typename T, typename Ret, typename... Args>
Ret call_member(Ret (T::*mem)(Args...), T* obj, Args... args)
{
    return (obj->*mem)(args...);
}

struct S {
    void foo(int i) { std::cout << "Foo: " << i << '\n'; }
};

int main()
{
    S s;

    call_member(&S::foo, &s, 5);
    return 0;
}                                                                
Tax answered 9/10, 2013 at 10:26 Comment(2)
here, ->* is used in return (obj->*mem)(args...);Schenck
Aah, I had assumed you just wanted the syntactic sugar.Tax
J
0

Is there a way to call member function of a class without the class object on left hand side?

...

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?

In short, no. You cannot call a non-static class member function without somehow specifying the object on the left side of the call. If the class (for static functions) or the object (for non-static) is not specified, the compiler has no way of knowing which function you are trying to call. It's outside the current scope because it's inside a class or object. There are ways to "hack" code so you can write it to look like main() in your example. Some of the other answers give examples of just these kinds of hacks.

------------Using static functions-----------------

A function within a class can be called outside the class without specifying a class object. The way to do it is to create a typedef for a function pointer with a signature that matches the class function's, then creating such a pointer and assigning it the address of the class function. That function can then be called without needing to specify the class or an object on the left side.

The function must be static. ISO C++ does not allow taking the address of a bound member function to form a pointer to that function, i.e. you cannot create a pointer to a non-static member function from a class object. You may be able to find some non-ISO standard compliant compilers, but I don't have any guidance on that for you.

The parameter this to non-static class member functions is an implied parameter, and is always an implied parameter. You cannot manually specify it, but you can emulate it by passing a pointer to an object of the class.

So, for your code example:

  • void D::foo(int) should be static and have an added D* parameter: class D { static void D::foo(D*, int); };.
  • typedef void (D::* Func)(int); should be changed to accommodate the previous change and remove D::: typedef void (*Func)(D*, int);
Jordonjorey answered 2/11, 2013 at 0:39 Comment(3)
@VishnuKanwar I reread to find your question and added to my response to answer it.Jordonjorey
static member functions are independent of class object anyways. This is not what I am looking for; sorry.Schenck
Ok. Like I said in my comment, ISO C++ does not allow pointers to non-static object functions. You'll have to find yourself a compiler that does not conform to the ISO specifications. (Or write your own!)Jordonjorey
B
0

The standard is unusually clear and utterly unambiguous on this subject. The first sentence of section 5.2.2 (C++11) states:

There are two kinds of function call: ordinary function call and member function call.

And that's that -- they're different things, and you can't mix them up.

Brannon answered 4/11, 2013 at 8:2 Comment(0)
C
0

Here is a version of your program that works. Pointers to class methods require a bit of convoluted invocation as shown below:

#include <iostream>
using namespace std;

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

        int data;
};


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

int main ( void ) 
{
    D d;
    D *pThis = &d;

    Func f = &D::foo;

    /* invoke function pointer via a "class" pointer */
    (pThis->*f)(5);

    /* invoke function pointer via a "class" object */
    (d.*f)(5);

    return 1;
}

The output from this program is as pasted below:

~/prj/stackoverflow
# g++ funcptr.cpp 

~/prj/stackoverflow
# ./a.out 
D
D

The compiler provides the "this" pointer to the method invocation via a class method pointer. The "this" pointer will be the invoking object.

Sure there is a way. Make foo a static class method and change the signature of foo to take a "this" pointer.

From:

void foo(int a) {}

To:

static void foo(D *pthis, int a) {}

To call foo w/o a . or ->, the following will work

int main(void)
{
    D d;
    int a = 0;
    D::foo(&d, a); /* call foo, w/o using . or -> and providing your own this pointer */
    return 0;
}
Cauline answered 6/11, 2013 at 4:43 Comment(1)
Thanks. But I am not looking for a working program. Please re-read the problem statement.Schenck

© 2022 - 2024 — McMap. All rights reserved.