Joe Susnick
Joe Susnick

Reputation: 6772

Is it possible to use a single typedef to define a block that takes no parameters and returns a block?

Similar/Follow-up to this question: Syntax to define a Block that takes a Block and returns a Block in Objective-C

I understand that this wouldn't be something that I would want to do but I want to understand why I can't use a single typedef to define:

typedef void (^XYZSimpleBlock)(void);
typedef XYZSimpleBlock (^ComplexBlock)(void);

Why is the above valid but this is not valid?

typedef void(^)(void) (^ComplexBlock)(void);

If I go by a similar answer I found I can try and parse it like a compiler but I am a bad compiler.

Following C's left-right-rule wouldn't the first identifier be ComplexBlock?

typedef void(^)(void) (^ComplexBlock)(void);
// parser reads left right and finds ComplexBlock first

Then it would go right and find the close of the declaration, then left until it find the caret, then right to interpret the parameters list.

That brings us to

 typedef void(^)(void) (^ComplexBlock)(void);
//       | unhandled   |compiled/interpreted| 

What is happening at this point that it can't read the void(^)(void) as a return type?

The thing that's confusing to me is that using a typedef is basically just a #define so shouldn't it be basically the same as above?

typedef void (^XYZSimpleBlock)(void);
same as:
void(^)(void)    
so:
typedef XYZSimpleBlock (^ComplexBlock)(void);
should be the same as:
typedef void(^)(void) (^ComplexBlock)(void);

I'm missing something here...

Upvotes: 1

Views: 311

Answers (1)

Ken Thomases
Ken Thomases

Reputation: 90521

The correct syntax is:

typedef void (^(^ComplexBlock)(void))(void);

As you construct declarations of complex types like this, it can help to start with the simpler type and substitute "expressions" for the identifier.

So, for example, declaring a function that takes no arguments and returns void:

void a_func(void);

Now, we want to declare a function pointer a_func_ptr which is a pointer to the same type of function. We want the expression *a_func_ptr to be of the same type as a_func, so we drop that expression, in parentheses, into the above and get:

void (*a_func_ptr)(void);

If we want a block pointer instead of a function pointer, we use ^ instead of *:

void (^a_block_ref)(void);

(One reason why people find block syntax frustrating is that one never actually writes the expression ^a_block_ref to "dereference" a block variable, so making that leap is non-obvious.)

Taking the above and making a typedef out of it (rather than a variable declaration) is how you get your:

typedef void (^XYZSimpleBlock)(void);

But, for a moment, let's go back to the function pointer a_func_ptr. Suppose you want to declare a function func_returns_func that returns a function pointer. You drop the function call expression in where a_func_ptr is. That is, you drop func_returns_func() in:

void (*func_returns_func())(void);

Now, when declaring functions, you need to use void for no arguments, so you make that:

void (*func_returns_func(void))(void);

Note that you do not gather or isolate the return type to the left of the identifier. It surrounds the identifier.

Now you change the * to a ^ to get a function which returns a block reference instead of a function pointer:

void (^func_returns_block(void))(void);

Let's make a pointer to a function that returns a block reference. Drop (*ptr_to_func_returns_block) in for func_returns_block and get:

void (^(*ptr_to_func_returns_block)(void))(void);

Again, replace * with ^ to get a reference to a block which returns a block reference:

void (^(^block_returns_block)(void))(void);

And there we are. Just change the name and that's how you get:

void (^(^ComplexBlock)(void))(void);

Upvotes: 3

Related Questions