Can I typecast callback functions?
I like to write a library that registers and calls certain callback functions.
int MyCaller::add_callback_function(int (*callback_function)(), byte behaviour) {
this->callback = callback_function;
}
void MyCaller::run() {
int result = this->callback();
}
// ....
int get_number() { return 42; }
MyCaller my_caller;
my_caller.add_callback_function(get_number, 0);
my_caller.run();
However, I would also like to support callback_functions that output a boolean.
bool get_bool() { return true; }
MyCaller my_caller;
my_caller.add_callback_function(get_number, 0);
my_caller.run();
If I try this, my code seems to run fine, but gcc will give a warning: invalid conversion from 'bool (*)()' to 'int (*)()' [-fpermissive].
To avoid this warning, I overloaded my add_callback_function with a slightly different signature:
void MyCaller::add_callback_function(bool (*callback_function)(), byte behaviour) {
// ...
}
However, I'm lost from this point onward. I expected that I could cast any boolean to an integer, and call the original method:
void MyCaller::add_callback_function(bool (*callback_function)(), byte behaviour) {
this->add_callback_function(static_cast<int (*)()>(callback_function), byte behaviour);
}
However, this gives an error: invalid static_cast from type 'bool (*)()' to type 'int (*)()'.
So I seem to be doing something wrong.
Is there a clean way to do this?
For bonus points: My code stores an array of callback functions, and I like to be able to optionally add a context parameter (to e.g. allow calling methods besides functions). I plan to store all these callbacks (with different signatures) as a int (*)(), and typecast them to their proper form before calling them. Would that be the best way, or are there other (perhaps better) ways to handle this?
The alternative I can think of is to only support one very specific callback signature, but that forces users to write wrappers. I like to put that burden on the library.
You can cast one function pointer type to another, but you can only call it if you cast it to the real function pointer type. You can only store them as the wrong signature.
Calling them any other way results in undefined behavior. The most likely symptom will depend on your platforms current calling convention. But really, just don't do it.
The easiest correct solution is to store a
std::function<int()>
this isn't a function pointer, but can store any callback (including a function pointer) that can be copied, moved, and invoked with zero arguments and returns something compatible with an int (which a bool qualifies as).
std::function can also store objects with state. For example:
struct Foo {
int count = 0;
int callback(){ return ++count; }
};
Foo foo;
std::function<int()> f = [&foo]{ return foo.callback(); };
naturally you become responsible for lifetime. Look up "C++ lambdas" to understand the above syntax.
Related
I'd like to build a C++ library which is usable from C as well.
This is the header file I want to be able to compile in C:
typedef void (*log_function_t)(const char *);
typedef void (*delay_function_callback_t)(uint32_t);
typedef void (*delay_function_t)(uint32_t, delay_function_callback_t);
extern "C" void core_init(log_function_t logFunction, delay_function_t delayFunction);
However, since I'm writing the library in C++, it would be nice to work with std::function objects instead of function pointers, so I'd like to call functions like this:
using LogFunction = std::function<void(const char*)>;
using DelayFunctionCallback = std::function<void(uint32_t)>;
using DelayFunction = std::function<void(uint32_t, DelayFunctionCallback)>;
void setLogFunction(const LogFunction& logFunction);
void setDelayFunction(const DelayFunction& delayFunction);
Calling the setLogFunction works just fine, but when I try to call setDelayFunctionit doesn't work.
void core_init(log_function_t logFunction, delay_function_t delayFunction)
{
Utility::getInstance().setLogFunction(logFunction);
Utility::getInstance().setDelayFunction(delayFunction);
}
It says: Reference to type 'const DelayFunction' (aka 'const function<void (unsigned int, function<void (unsigned int)>)>') could not bind to an lvalue of type 'delay_function_t' (aka 'void(*)(unsigned int, void (*)(unsigned int))')
Obviously I understand why it doesn't work, but I have a feeling that it should be possible to solve and I'm just not experienced enough to solve it.
What you're asking seem to be passing a function pointer from C to C++ where the function takes a std::function as argument. I'm afraid this is not possible just as C can't pass a function pointer that takes a std::vector as argument.
When calling Utility::getInstance().setDelayFunction(delayFunction), the ctor of a specialized std::function (i.e. DelayFunction) is matched to construct from a function pointer. However, the match fails because the ctor (of DelayFunction) accepts as its 2nd argument a specialized std::function (i.e. DelayFunctionCallback) , rather than a function pointer (i.e. delay_function_callback_t).
I think the problem lies in the implementation of std::function, which encapsulates the function pointer and erases the latter's type. (See How is std::function implemented?) As a result, a C++ std::function is a different type than a plain-C function pointer.
To workaround this, you could relax the C++-ishness a bit and declare DelayFunction as accepting void(*)(unsigned) instead. I.e., in the C++ file:
using LogFunction = std::function<void(const char*)>;
using DelayFunction = std::function<void(unsigned, delay_function_callback_t)>;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
EDIT: Re. the comment on calling the DelayFunction object from C++, instead of passing a lamba function as the callback (which would fail with the workaround above, since the lambda function can only construct a DelayFunctionCallback, not a delay_function_callback_t), it might be easier to implement the callback as a static member function and use it directly:
Utility::getInstance().delay(delay, (delay_function_callback_t)&Utility::next);
BTW, if Utility is going to store the std::function objects internally, then it may be more efficient to pass-by-value, since the LogFunction and DelayFunction objects will always be constructed anyway (i.e. they are rvalue in core_init).
A void(*)() is fundamentally different from a std::function<void()>.
You can get closer with a void(*)( void* ), void*; a std function has both callable-ness and state, a function pointer only has callable-ness. (std function also carries RTTI and how-to-cleanup-state and how-to-copy-state).
Now you can convert a void(*)() into a std function that is stronger; but not the other way. And the arguments to a function are converted the other way when the call happens.
struct callback {
void* state;
void(*action)(int32_t);
void(*cleanup)(void*);
void*(*copy)(void*);
};
that is the rough C equivalent of a std::function<void(int32_t)>.
Today, I'm working on a callback pass from C++ to Objective-c method.
Finally, I worked it out, but a few code confuse me.
In Objective-c, people ordinary use block to implement a callback, a block declare looks like this:
returnType (^blockName)(parameterTypes)
I also learned about C++ callback, a same type callback defined like this:
returnType (*funcName)(parameterTypes)
When I passed a callback from C++ to Objective-c, compiler warning me:
"Cannot initialize a parameter of type 'void (^)(int)' with an rvalue of type 'void (*)(int)"
Finally, I changed ^ to *, it works. I wondering to know, what's the difference between ^ and * in definition, is that have the same behavior?
This is a block:
returnType (^blockName)(parameterTypes)
This is a function pointer:
returnType (*funcName)(parameterTypes)
They aren't compatible.
Standard C and C++ do not have callable blocks. Apparently, however, some compilers for those languages do implement the feature as an extension. Some of the docs I see suggest that it's an Apple extension to Objective C, too, but inasmuch as Objective C is little used outside the Apple world, that may be a distinction without a difference.
In whatever language, if you implement a callback as a function, then you must present that callback to the intended caller via a function pointer, and the caller must be prepared to accept it in that form. On the other hand, if you implement a callback as a block then you must present it to the caller via a block pointer, and the caller must be prepared to accept that form. The two are not interchangeable, and they have different syntaxes, which you have presented. It would be possible to design a mechanism that could accept both forms, however, via separate parameters or altogether separate registration functions.
Objective-C blocks (^) are incompatible with function pointers (*).
Compared to function pointers, they have the benefit of being able to capture values and variables from the surrounding context (they are closures).
It's easy enough to write a wrapper that calls a function pointer from a block:
typedef int (*funptr)(int);
typedef int (^funblock)(int);
int use_block_callback(int y, funblock fb)
{
return fb(y);
}
int use_funptr_callback(int y, funptr f)
{
return use_block_callback(y, ^ int (int x) { return f(x); });
}
int add_one(int x) { return x + 1; }
int foo(int x)
{
return use_funptr_callback(23, add_one);
}
// Or
int (*some_pointer)(int) = add_one;
int (^some_block)(int) = ^ int (int x) { return some_pointer(x); }
("Programming with Objective-C" about blocks.)
I have a situation where a function can only take "int" (can't change this) and I need it in a different situation. let me directly write the code
bool foo(int dev)
{
...
...
return true/false;
}
I need to pass :
mClassPointer->dev()
mClassPointer[index]->dev()
dev() //(function)
and obviously dev //(variable)
mClassPointer is pointer to class.
dev() is a member function of a class , return an Integer.
If you have a function that needs to handle different datatypes in different situations (as is vaguely implied in your question), then perhaps you need to look into templates.
You may be able to do that by changing the argument to a void*.
Be very careful with this and read this thread carefully, espc. the post by Loki Astari:
error: cast from 'void*' to 'int' loses precision
If the function only accepts an int then I don't know if this is possible. Read a discussion in this thread if you are thinking of casting your pointers to int and passing. May not work on certain platforms: Converting a pointer into an integer
I want to use boost::any to store heterogeneous function pointers. I get an exception when I try to use boost::any_cast to recast to the function pointer.
Is what I want to do even allowed?
.h:
typedef void(*voidFunction)(void);
struct functionInfo{
CString functionName;
boost::any functionPointer;
};
void foo();
int foo2(int a);
.cpp
void foo()
{
;//do something
}
int foo2(int a)
{
;//do something
}
void main()
{
vector<functionInfo> functionList;
functionInfo myInfo;
myInfo.functionName = _T("foo");
myInfo.functionPointer = &foo;
functionList.push_back(myInfo);
myInfo.functionName = _T("foo2");
myInfo.functionPointer = &foo2;
functionList.push_back(myInfo);
voidFunction myVoidFunction = boost::any_cast<voidFunction>(functionList[0].functionPointer);
}
----EDIT----
Ok, you are right, the reason why it acted like this is because foo is a class member function.
Meaning:
void MyClass::foo();
myInfo.functionPointer = &MyClass::foo;
so I needed to typedef:
typedef void(MyClass::*voidClassFunction)(void);
voidClassFunction myVoidFunction = boost::any_cast<voidClassFunction>(functionList[0].functionPointer);
Is what I want to do even allowed?
Absolutely. As long as you cast it back to exactly the type you gave it.
And that's your problem. You don't. foo2 is not a voidFunction. Therefore, you cannot cast it to one.
The purpose of boost::any is to have a void* that is guaranteed to either work correctly according to the C++ standard or throw an exception. The C++ standard allows the conversion of any (non-member) pointer type to a void*. It also allows conversion of a void* back to a type, provided that the type being provided is exactly the same type as the original. If it's not, welcome to undefined behavior.
boost::any exist to prevent undefined behavior by storing type information with the void*. It will rightly throw an exception when you attempt to cast something to the wrong type. As you do here. boost::any is not a way to pretend that types don't exist and to pretend that you can turn anything into something else. It's just a type-safe typeless container. You still need to know what you actually put there.
There is no way to store a list of functions with arbitrary argument lists and call them with the same argument list. The user must provide a function or functor with the right argument list that you expect. boost::bind is a way to adapt a function/functor for a particular argument list, but the user must explicitly use it.
The best you can do is have a list of specific function parameter sets that you accept, stored in a boost::variant object. You can use a visitor to figure out which particular function to call.
I am trying to understand what this means, the code I am looking at has
in .h
typedef void (*MCB)();
static MCB m_process;
in .C
MCB Modes::m_process = NULL;
And sometimes when I do
m_process();
I get segmentations fault, it's probably because the memory was freed, how can I debug when it gets freed?
It defines a pointer-to-function type. The functions return void, and the argument list is unspecified because the question is (currently, but possibly erroneously) tagged C; if it were tagged C++, then the function would take no arguments at all. To make it a function that takes no arguments (in C), you'd use:
typedef void (*MCB)(void);
This is one of the areas where there is a significant difference between C, which does not - yet - require all functions to be prototyped before being defined or used, and C++, which does.
It introduces a function pointer type, pointing to a function returning nothing (void), not taking any parameters and naming the new type MCB.
Let's take an example
typedef void (*pt2fn)(int);
Here, we are defining a type pt2fn. Variables of this type point to functions, that take an integer as argument and does not return any value.
pt2fn kk;
Here, kk is a variable of type pt2fn, which can point to any function that takes in an integer as input and does not return any value.
Reference:https://cs.nyu.edu/courses/spring12/CSCI-GA.3033-014/Assignment1/function_pointers.html
The typedef defines MCB as the type of a pointer to a function that takes no arguments, and returns void.
Note that MCB Modes::m_process = NULL; is C++, not C. Also, in C, the typedef should really be typedef void (*MCB)(void);.
I'm not sure what you mean by "the memory was freed". You have a static pointer to a function; a function cannot be freed. At most, your pointer has been reset somewhere. Just debug with a memory watch on m_process.
It's a function pointer. You get a SEGMENTATION FAULT because you are trying to make a call to a function which address is invalid (NULL).
According to your specific sample, the function should return no value (void) and should receive no parameters ().
This should work:
void a()
{
printf("Hello!");
}
int main(int arcg, char** argv)
{
m_process = a;
m_process(); /* indirect call to "a" function, */
// Hello!
}
Function pointers are commonly used for some form of event handling in C. It's not its only use though...