0

I am facing problems with passing function pointer as arguments.

The declaration of pointer to function type :

typedef void(*cbk_fct)(void);

The class Operation has a constructor that accept a cbk_fct as argument the declaration is as follows:

class Operation
{
 private:
 cbk_fct m_fct_ptr;

public:
 Operation(cbk_fct fct_ptr);
};

Operation::Operation(cbk_fct fct_ptr):
m_fct_ptr(fct_ptr)
{

}

Now the class User will call the Constructor of Operation

class User
{
 public:
 User();
 void userOperation();
};

void User::userOperation()
{
 cout << "User operation"<<endl;
}

User::User()
{
 Operation op(userOperation); // This version doesnt work
}

This call will give following error:

no matching function for call to 'Operation::Operation(<unresolved overloaded function type>)'|
no known conversion for argument 1 from '<unresolved overloaded function type>' to 'cbk_fct {aka void (*)()}'

However if I declared the function to be passed as parameter outside the class it will be accepted

void UserOperationNotInClass()
{
 cout << "User operation"<<endl;
}

User::User()
{
 Operation op(UserOperationNotInClass); // This version works
}

Clearly the error message mention that it is not able to convert from '' to 'cbk_fct, but from where comes this type 'unresolved overloaded function type'

Mouin
  • 1,025
  • 4
  • 19
  • 33

2 Answers2

1

A non static member function is not the same thing as a normal function. Specifically, it can access its object this pointer which would not make sense for a normal function. So the compiler is correct when saying that there is no know conversion from the former to the latter.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

Unlike free functions, there isn't an implicit conversion from a member function to pointer to member function, so you can never supply userOperation. You have to use &User::userOperation. The error message could be better in that regard.

Having fixed that, &User::userOperation has the type void (User::*)(void), which is not the same as void (*)(void) (your alias cbk_fct). You always need a User to call userOperation, and there is nowhere for that. You will have to change Operation if you want it to include member functions.

The (C compatible) C++98 way would be to include a void * in both the function type, and the members of Operation, and pass this (or any other object pointer), implicitly converting to void *.

typedef void * context_ptr;
typedef void (* action_ptr)(context_ptr);

class Operation
{
 private:
 action_ptr m_action;
 context_ptr m_context;

public:
 Operation(action_ptr action, context_ptr context);
};

Operation::Operation(action_ptr action, context_ptr context):
m_action(action), m_context(context)
{}

class User
{
 static void callUserOperation(context_ptr);
 public:
 User();
 void userOperation();
};

void User::callUserOperation(context_ptr context)
{
    User * user = reinterpret_cast<User *>(context);
    user->userOperation();
}

void User::userOperation()
{
 cout << "User operation"<<endl;
}

User::User()
{
 Operation op(&User::callUserOperation, this);
}

In C++11, you can use std::function to hold any function object of a particular signature, and construct it from a lambda capturing this and calling userOperation

using action_ptr = std::function<void(void)>;

class Operation
{
 private:
 action_ptr m_action;

public:
 Operation(action_ptr action);
};

Operation::Operation(action_ptr action):
m_action(action)
{}

class User
{
 public:
 User();
 void userOperation();
};

void User::userOperation()
{
 cout << "User operation"<<endl;
}

User::User()
{
 Operation op([this]{ userOperation(); });
}
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • It seems you missed the `User::` in the definition of `callUserOperationIt` apart of this it seems fine I will try it and update the status – Mouin Jan 02 '19 at 13:43
  • One question in the 1st example the `reinterpret_cast` is not dangerous in this case ? – Mouin Jan 02 '19 at 21:27
  • No, because *you know* that `callUserOperation` is always associated with a `User *` that has been converted to `void *`. You've just declined to inform the complier directly – Caleth Jan 02 '19 at 21:36