typedef (void (^blockType)());
I need cast blocks with different argument types into a same type blockType
, and invoke it as the original type later. But there is a issue while casting block type.
The following code works well with any argument type, ...
((blockType)^(BOOL b) {
NSLog(@"BOOL: %d", b);
})(YES); // >> BOOL: 1
((blockType)^(int i) {
NSLog(@"int: %d", i);
})(1); // >> int: 1
((blockType)^(double f) {
NSLog(@"double: %f", f);
})(1.0 / 3); // >> double: 0.333333
((blockType)^(NSString *s) {
NSLog(@"NSString *: %@", @"string");
})(1.0 / 3); // >> NSString *: string
except float:
((blockType)^(float f) {
NSLog(@"float: %f", f);
})(1.0f); // >> float: 0.000000
((blockType)^(float f) {
NSLog(@"float: %f", f);
})(1.0f / 3); // >> float: 36893488147419103232.000000
but it is ok without casting:
(^(float f) {
NSLog(@"float without casting: %f", f);
})(1.0 / 3); // >> float without casting: 0.333333
how to explain and resolve it?
It appears to be a taint of the good old C language. Consider the following code (we can say it is kind of 'translation' of your Obj-C block with issues to C as far as blocks are related to function pointers (see here)):
void test()
{
void (*pEmpty)();
pEmpty = functionFloat;
pEmpty(1.0f / 3);
}
void functionFloat(float f)
{
printf("float: %f", f);
}
If you call test
you will see the same result as when you invoke your 'sick' block. Compiler will provide just a warning about incompatible pointers and will let you run. But if you change
void (*pEmpty)();
to
void (*pEmpty)(void);
there will be a compile-time error. Same will happen if you add void
explicitly to your void-blocks, e.g. (void (^)(void)
instead of (void (^)()
.
The reason for such behavior explained in the C Standard:
The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.
§6.7.6.3-14 (p.134)
Thus, as it doesn't mean that there are no parameters but rather no info about them, cast passes fine.
The problem with unexpected output is the following:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
§6.3.2.3-8 (p.56)
and
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
§6.5.2.2-9 (p.82)
So, it seems that the solution here is just like @jtbandes said: don't mess block types and re-design this part of code to avoid such casts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With