jspahrsummers / libextobjc

A Cocoa library to extend the Objective-C programming language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] Is it possible to do some kind of block argument overloading? (oslt)

nickynick opened this issue · comments

Hey Justin! I don't know if this is the right place, but I really would like to ask you about one tricky feature I'm struggling with :)

Is it possible to somehow have a block which would accept an argument of different types without complaining? The argument could be id, but might as well be double or even struct (e.g., CGPoint).

A solution I'm currently using is a macro-based one. I use a macro to wrap scalar/struct arguments in NSValue, using their encoded types, and then retrieve them back. However, this has a drawback of littering global namespace with defines, which may be undesired.

One thing I noticed is that you may do like this:

void (^block)() = ^{
    // do something
};

/**
 *  no syntax errors here 
 *  (because block invocation is actually done via a function 
 *  accepting variable number of parameters, right?)
 */
block(1);
block([NSObject new]);

Perhaps there's a way to retrieve these passed arguments inside the block? We could assume that we actually know which type we are currently expecting.

Maybe there's another clever approach I'm missing? I know you are an expert on all kinds of crazy things, so perhaps you could give me a clue :) Thanks a lot!

You could use a variable argument list (... in the declaration and implementation) along with the va_start, va_arg, and va_end macros. However, you definitely have to know how many arguments there are, and their types, if you decide to do that.

This is true, but for that I need at least one named argument. Which I cannot afford given the way things are now :)

From what I read about the blocks implementation, I understood that behind the scenes, the block descriptor itself is the named argument passed to the invocation function. Perhaps this could be a starting point, but simple va_start can't handle that.

You'd probably have to get deep within libffi to find something that can help if you really don't want any named arguments. I'm afraid I don't know much more than that.

It looks like I got something working :)

@interface Foo : NSObject

@property (nonatomic, assign) char *type;
@property (nonatomic, readonly) void (^bar)();

@end


@implementation Foo

- (void (^)())bar {
    if (strcmp(self.type, @encode(id)) == 0) {
        return ^(id a) {
            NSLog(@"%@", a);
        };
    } else if (strcmp(self.type, @encode(int)) == 0) {
        return ^(int a) {
            NSLog(@"%d", a);
        };
    } else if (strcmp(self.type, @encode(double)) == 0) {
        return ^(double a) {
            NSLog(@"%lf", a);
        };
    } else {
        return nil;
    }
}

@end
    Foo *foo = [[Foo alloc] init];
    foo.type = @encode(id);
    foo.bar(@42);
    foo.type = @encode(int);
    foo.bar(42);
    foo.type = @encode(double);
    foo.bar(42.0);

Surprisingly, this seems to work fine! I wonder how valid it is :D

you can't return a block that takes an "id" as parameter

#import <Foundation/Foundation.h>
@interface Foo : NSObject

@property (nonatomic, assign) char *type;
@property (nonatomic, readonly) void (^bar)();

@end


@implementation Foo

- (void (^)())bar {
    if (strcmp(self.type, @encode(id)) == 0) {
        NSString *star = @"I am a rock star";
        return ^(id a) {
            NSLog(@"%@", star);
            NSLog(@"%@", a);
        };
    } else if (strcmp(self.type, @encode(int)) == 0) {
        return ^(int a) {
            NSLog(@"wowo");
            NSLog(@"%d", a);
        };
    } else if (strcmp(self.type, @encode(double)) == 0) {
        return ^(double a) {
            NSLog(@"huihu");
            NSLog(@"%lf", a);
        };
    } else {
        return nil;
    }
}

@end
int main(int argc, char *argv[]) {
    @autoreleasepool {
            Foo *foo = [[Foo alloc] init];
            foo.type = @encode(id);
            int a = 2;
            [foo bar](a);
    }
}

the above has a segfault error

@liuyaouestc That's true. The above approach assumes you know the correct argument type before the call.