Reputation: 5758
I have a class method:
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context, void(*sendFailure)() = NULL);
The sendFailure is an optional function pointer, in my C++ class in one instance I have:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", HBCMaxBrightnessLimitSendFailure);
HBCMaxBrightnessLimitSendFailure is declared in the class as a private method:
void HBCMaxBrightnessLimitSendFailure();
When I build this application I get error:
lightingsystemcomponent.cpp(969): error C3867: 'Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure': function call missing argument list; use '&Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure' to create a pointer to member
I also tried:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", HBCMaxBrightnessLimitSendFailure());
This changed the error to:
lightingsystemcomponent.cpp(969): error C2664: 'bool Lighting::SystemComponent_impl::sendRequest(DataFieldList &, DataFieldList &,std::string,void (__cdecl *)(void))' : cannot convert argument 4 from 'void' to 'void (__cdecl *)(void)'
Expressions of type void cannot be converted to other types
I've had a bit of a move around and tried to implement the proposal put forward by Spencer, new prototype:
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context,
std::function<void()> sendFailure = NULL);
In the sendRequest function:
void LightingEngine::sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context,
std::function<void()> sendFailure) {
if ( sendFailure != NULL ) {
sendFailure();
}
}
Of course this isn't all the code, just enough to illustrate the new layout and call.
When I try to build this I get:
error C2064: term does not evaluate to a function taking 0 arguments
Here is an example of the class thats calling this routine and the callback function I'm passing:
void component::onFailure() {
...Do something...
}
void component::update() {
... Some set-up to build paramlist ...
LightingEngine* engine = getLightingEngine();
assert(engine != nullptr);
engine->sendRequest(¶mList, &responseList, "Some context text", [this]()->void { onFailure(); });
}
Upvotes: 0
Views: 229
Reputation: 2214
A nonstatic class member function is different from a non-class function, in that it requires an instance of the class to work. In your declaration
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context, void(*sendFailure)() = NULL);
sendFailure
is declared as a non-member function. If it has to be this way, you will never be able to bind to a nonstatic member function.
The simplest course would be to declare HBCMaxBrightnessLimitSendFailure
as static
or even move it out of the class altogether, but that will only work if it doesn't depend on the class's state.
Presumably you have MANY DIFFERENT failure functions that get passed in at different times. If there's only the one, don't pass in a function pointer. Instead pass in a bool:
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
bool sendFailure = false)
{
// ...
if (sendFailure) HBCMaxBrightnessLimitSendFailure();
// ...
}
You can use a pointer-to-member function but you need an instance of the class. If sendRequest
and HBCMaxBrightnessLimitSendFailure
are in the same class, use this
.
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context,
void(lightingsystemcomponent::*sendFailure)()= 0);
{
if (sendFailure !=0) this ->*sendFailure();
}
...
sendRequest (request, response, ctxt, &HBCMaxBrightnessLimitSendFailure);
The most flexible way to do it (at the cost of a little overhead) is to redeclare the sendFailure
parameter as a std::function
:
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context, std::function<void()> sendFailure);
You would then call sendRequest
in a class method using a lambda:
DataFieldList request;
DataFieldList response;
sendRequest (request, response, "context", [this]()->void
{ HBCMaxBrightnessLimitSendFailure(); })
If the function parameter needs to be a bare function, e.g. in order to pass to a C API call, things get messy. A lambda with captures will never work. Such a "stateful" lambda is a class created on the fly with the function pointer and the capture values as data members. And you can't pass a C++ class into a C function.
But you need an instance of the class to use the member function! Arrgh! The only thing for it is a global instance of the class to access from within the function:
class lightingsystemcomponent
{
static lightingsystemcomponent*api_context;
static void HBCMaxBrightnessLimitSendFailure()
{
api_context-> something();
}
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context, void(*sendFailure)() = NULL);
};
// ...
api_context = this;
sendRequest(request, response, "context", HBCMaxBrightnessLimitSendFailure());
This is analogous to the stuff a stateful lambda does for you, but you can pass it to a C library at the cost of some elegance.
Upvotes: 4
Reputation: 5331
You could declare sendFailure
as a member function pointer.
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context,
void(Lighting::SystemComponent_impl::*sendFailure)() = nullptr);
This would allow you to write a really long line:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", &Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure);
Another option is to use std::function
.
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context,
const std::function<void()> &sendFailure = nullptr);
And call sendRequest
like this:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", [this]{ HBCMaxBrightnessLimitSendFailure() });
The idiomatic "C++ way" of doing callbacks is to use a function template. This is similar to using std::function
but with far superior performance
template <typename Func>
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context,
Func sendFailure);
The only minor issue with this is that you'd have to have another overload for when sendFailure
isn't passed. For this reason, it's probably not ideal for your scenario.
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context);
Then call sendRequest
like this:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", [this]{ HBCMaxBrightnessLimitSendFailure() });
Upvotes: 4
Reputation: 3973
If you change your void(*sendFailure)()
function pointer to an std::function<void(void)>
, then I think you can do what you want:
void printFunc(int a, std::function<void(void)> f)
{
std::cout << a << '\t';
if (f)
f();
else
std::cout << "f is null" << std::endl;
}
void testFunc()
{
std::cout << "testFunc" << std::endl;
}
class A
{
private:
void privatePrint() { std::cout << "privatePrint " << a << std::endl; }
int a;
public:
A(int i) : a { i } {}
void print()
{
printFunc(a, [this]() {this->privatePrint();} );
}
};
class B
{
private:
static void staticPrint() { std::cout << "staticPrint" << std::endl; }
int b;
public:
B(int i) : b{ i } {}
void print()
{
printFunc(b, staticPrint);
}
};
struct C
{
void operator()() { std::cout << "operator()" << std::endl; }
};
int main()
{
printFunc(1, testFunc);
A a{ 2 };
a.print();
B b{ 3 };
b.print();
printFunc(4, nullptr);
C c;
printFunc(5, c);
}
Here I pass a non-class function, a non-static member function (wrapped in a lambda), a static member function, a nullptr
, and a functor to the same printFunc
and they all work. Output is:
1 testFunc
2 privatePrint 2
3 staticPrint
4 f is null
5 operator()
Upvotes: 1
Reputation: 1235
Edit:
If you can change the the signature of sendRequest and are using a fairly modern c++ (11 or higher), i would opt for std::function
.
#include <functional>
bool sendRequest(DataFieldList& requestParams,
DataFieldList& responseParams,
std::string context, std::function<void()> sendFailure = nullptr);
Then you can call it with a lambda like proposed:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", [&](){HBCMaxBrightnessLimitSendFailure();});
or without lambda but with bind:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", std::bind(&Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure,this));
Old post:
Try what the compiler tells you
&Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure
so:
sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", &Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure);
Upvotes: 0
Reputation: 5075
A member function cannot be directly converted to a function pointer since it has an implicit parameter for the object calling it (you see it as the this
pointer in the member). Working around this is not as easy, as I thought it is see here.
What's the best solution is, depends on your use case. If both caller and callee are member functions and also really depend on the object they are called from (that is the same for both), add another member function pointer as parameter. If your passed member function does not depend on its object, make it static and you have no problem. For everything in between it gets tricky.
Upvotes: 0