Search code examples
objective-cfunctionobjective-c-blocks

What is the required syntax to pass a block to a pure C function?


I have a pure C function, to which I would like to pass a block (a closure?). As per Apple, the block should always be the last parameter to a function.

double pureCfunctionWithABlockParameter( int ignore, double ignore2, void (^myVoidBlockWithoutParameters)(void) ) {

myVoidBlockWithoutParameters(); /

return 0.0;    
}

Next is the Objective C code to call the C function:

- (void) testBlockFunctionality {

declare and define the block:

   void (^myBlock1)(void) ;

    myBlock1=^(void){ NSLog(@"myBlock1 just logs this message to the console");}; 

Attempt to invoke the block directly, without parentheses. This doesn't work. Xcode warns result is unused. Block's message is NOT logged to console.

    myBlock1;   

Now attempt to invoke the block directly, this time with parentheses. This works as intended. No Xcode warnings, and the block's message IS logged to console.

    myBlock1(); 

Now call the function, passing the block as parameter, WITHOUT parentheses. This works as intended, but the syntax is not consistent with the previous invocation of the block.

    double someNumber;
    someNumber= pureCfunctionWithABlockParameter(0, 1, myBlock1 );     

Now call the function, again passing the block as a parameter, this time WITH parentheses. This doesn't work, it won't even compile, as Xcode gives a: "Passing 'void' to parameter of incompatible type 'void (^)(void)'" message.

    someNumber= pureCfunctionWithABlockParameter(0, 1, myBlock1());   

At the end of it all, I am actually looking to have a block defined that gets passed an int parameter, like this:

void(^block)(int)

But I cannot progress to that because of what I think is a syntax issue.

I've looked in Apple's Block Programming Topics, and even K&R C, but no luck.


Solution

  • The question has caused some confusion, because blocks (in the question's sense) are not a feature of standard C. Apple added them as an extension to its C and C++ compilers when it added them to Objective C, but they are not a C thing outside the Apple ecosystem. I confess that I've no experience actually using them, but as far as I can tell from the docs, such as these, the syntax was chosen so as to be the same for C, C++, and Objective C. Indeed, some sources claim that details of the syntax were chosen specifically to avoid the possibility of conflict with C++.

    From a C perspective, accepting a block as a parameter and calling a block received that way are thoroughly analogous to accepting a function pointer and calling the pointed-to function, respectively. Your example C function appears to be correct.

    Similar applies to declaring and and working with blocks, in all three languages -- it is analogous to declaring and working with function pointers. I am confident that this was an intentional design consideration. Thus

       void (^myBlock1)(void) ;
    

    indeed declares myBlock1 as a block taking no parameters and returning nothing, but does not define its value. Having elsewhere set a valid value for it, such as is demonstrated in the question, the OP observes

    Attempt to invoke the block directly, without parentheses. This doesn't work. Xcode warns result is unused. Block's message is NOT logged to console.

        myBlock1;
    

    , as indeed should be expected. That's a statement expression evaluating to the value of the block, not to the result of executing the block. It is analogous to

    int myInt = 1;
    
    myInt;  // specifically, analogous to this
    

    To execute a block, one provides a postfix argument list in parentheses (even if the list is empty), just like when calling a function through a function pointer:

    Now attempt to invoke the block directly, this time with parentheses. This works as intended. No Xcode warnings, and the block's message IS logged to console.

        myBlock1();
    

    The presence or absence of an argument list is what disambiguates whether one is accessing the block's value or calling it.

    The confusion is about passing a block to a function (or method):

    Now call the function, passing the block as parameter, WITHOUT parentheses. This works as intended, but the syntax is not consistent with the previous invocation of the block.

        double someNumber;
        someNumber= pureCfunctionWithABlockParameter(0, 1, myBlock1 );
    

    Yet, contrary to the assertion in the question, that syntax as completely consistent, both internally consistent with other aspects of block syntax and usage, and consistent with analogous function pointer syntax and usage. That passes the block to the function, identifying the block by its name. The block itself is passed, not the result of executing it, because no argument list for it is provided.

    At the end of it all, I am actually looking to have a block defined that gets passed an int parameter, like this:

    void (^block)(int)
    

    But I cannot progress to that because of what I think is a syntax issue.

    A C function accepting and using such a block might look like this

    void pass_2(void (^do_something)(int)) {
        do_something(2);
    }
    

    Given variable block declared as shown above, and assigned a valid block as its value, that function could be called like so:

    pass_2(block);
    

    Just as we recognize that function pass_2 is called by the presence of an argument list, we recognize that the value of variable block is passed as an argument -- not called -- by the absence of an argument list.