Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

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

Open
nickynick opened this issue Apr 23, 2014 · 9 comments

Comments

@nickynick
Copy link

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!

@jspahrsummers
Copy link
Owner

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.

@nickynick
Copy link
Author

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

@nickynick
Copy link
Author

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.

@jspahrsummers
Copy link
Owner

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.

@nickynick
Copy link
Author

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

@yao2030
Copy link

yao2030 commented Apr 26, 2014

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

@yao2030
Copy link

yao2030 commented Apr 26, 2014

#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);
    }
}

@yao2030
Copy link

yao2030 commented Apr 26, 2014

the above has a segfault error

@nickynick
Copy link
Author

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants