简体   繁体   中英

Setting a pointer to a non-static member function

I'm trying to setup a function pointer that is set during execution based on a set of user parameters. I would like to have the function pointer point to a non-static member function but I can't find how to do it.

The examples I've seen say this can only be done with static member function only or use global variables in straight C.

A simplified example follows:

    class CA
    {
    public:
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(double f(const double), double x) {
            char cTemp[256];
            sprintf_s(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
    };

The implementation part is

CA cA;
cA.setA(1.0);
cA.setB(2.0);

double (*p)(const double);

if(true) {
    p = &cA.getA;  //'&' : illegal operation on bound member function expression
} else {
    p = cA.getB;  //'CA::getB': function call missing argument list; use '&CA::getB' to create a pointer to member
                  //'=' : cannot convert from 'double (__thiscall CA::* )(const double)' to 'double (__cdecl *)(const double)'
}

cA.print(p, 3.0);

So how do I get p to point to either 'getA' or 'getB' so that it is still useable by 'print'.

From what I have seen, the suggestions are to use boost or std::bind but I've had no experience with either of these. I'm hoping that I don't need to dive into these and that I'm just missing something.

Compiler MSVC++ 2008

Don't forget that a member function accepts an implicit this parameter: therefore, a member function accepting a double can't be the same thing as a non-member (free) function accepting a double .

// OK for global functions
double (*p)(const double);

// OK for member functions
double (CA:*p)(const double);

Also the way you invoke them is different. First of all, with member functions, you need an object to invoke them on (its address will eventually be bound to the this pointer in the function call). Second, you need to use the .* operator (or the ->* operator if you are performing the call through a pointer):

p = &CA::getA;
CA cA;
(cA.*p)();

Consistently, you will have to change your definition of function print() :

    #include <iostream>

    void print(double (CA::*f)(const double), double x) 
    {
        // Rather use the C++ I/O Library if you can...
        std::cout << "Value = " << (this->*f)(x);
    };

So finally, this is how you should rewrite your main() function:

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    double (CA::*p)(const double);

    if (true) // Maybe use some more exciting condition :-)
    {
        p = &CA::getA;
    } 
    else {
        p = &CA::getB;
    }

    cA.print(p, 3.0);
}

Compilation Issue

This answer focuses on the compilation issue presented in the question. I would not recommend implementing this as a solution.

Pointers to member functions are best dealt with with typedef s and a macro.

Here's the macro for calling a member function:

#define CALL_MEMBER_FN(object, ptrToMember)  ((object).*(ptrToMember))

Source: [33.6] How can I avoid syntax errors when calling a member function using a pointer-to-member-function? , C++ FAQ .

This saves you having to remember the ugly (object).*(ptrToMember) syntax any time you wish to call a member function by pointer.

In your class , declare a typedef called CAGetter , this will make variable declaration much simpler:

class CA
{
public:    
    typedef double (CA::*CAGetter)(const double x);

Then you can declare your print() function quite simply:

    void print(CAGetter f, double x)

The body is also simple, clear and concise:

    {
        std::cout << "value = " << CALL_MEMBER_FN(*this, f)(x) << '\n';
    }

Sample usage:

CA a;
a.setA(3.1);
a.setB(4.2);

// Using a variable...
CA::CAGetter p = &CA::getA;
a.print(p, 1);

// without a variable
a.print(&CA::getB, 1);

// Calling the functions from outside the class...
std::cout << "From outside (A): " << CALL_MEMBER_FN(a, p)(10) << std::endl;
std::cout << "From outside (B): " << CALL_MEMBER_FN(a, &CA::getB)(10) << std::endl;

Design Issue

Passing a pointer to a member function into a method of an instance of the same class is a design smell (you wouldn't normally pass a member variable to a method, this is no different). There is not enough information in this question to address the underlying design issue but this problem could probably be solved with separate print() methods, a member variable or with inheritance and polymorphism.

You can either use pointer to method:

class CA
{
    public:
        typedef double (CA::*getter)( double );
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", (this->*f)(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = &CA::getA;  
    } else {
            p = &CA::getB; 
    cA.print( p, 3.0 );
}

Or use boost::bind

class CA
{
    public:
        typedef boost::function<double( double )> getter;
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = boost::bind( &CA::getA, &cA, _1 );
    } else {
            p = boost::bind( &CA::getB, &cA, _1 );
    }
    cA.print( p, 3.0 );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM