Macro in Obj-C libraries

Some Obj-C libraries expose API by macro (e.g. ReactiveCocoa / extobjc):

 RAC(self, loggedIn) = [[NSNotificationCenter.defaultCenter
    rac_addObserverForName:UserDidLogOutNotification object:nil]
    mapReplace:@NO];

In this code snippet, RAC is a macro defined by ReactiveCocoa which use other macros:

#define RAC(TARGET, ...) \
metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
    (RAC_(TARGET, __VA_ARGS__, nil)) \
    (RAC_(TARGET, __VA_ARGS__))

It’s unpractical to manually expand the macro for every API call.
As this issue also apply to Oxygen and I wonder what might be the best solution? ( Use cpp pre-processor or maybe writing custom Cirrus Aspect? )

Thanks.

Can you show the full thing? I need to see what it really does before I can recommend a workaround.

Hi Carlo,
The library is ReactiveCocoa ( https://github.com/ReactiveCocoa/ReactiveCocoa ) which use macros from extobjc ( https://github.com/jspahrsummers/libextobjc )

An inline method, or a method call aspect might work. This particular macro seems to abuse variadic arguments to the max though.

#define RAC_(TARGET, KEYPATH, NILVALUE) \
[[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)]

a simple method that takes a selector could work too though, If I read this right.

I end up with a helper class:

public static class RAC
{
	public static RACSignal RAC(NSObject target, NSString keypath, RACSignal signal) {
		new RACSubscriptingAssignmentTrampoline(target) nilValue(null).setObject(signal) forKeyedSubscript(keypath);
	}
}

So

RAC(self, createEnabled) = [RACSignal 
    combineLatest:@[ RACObserve(self, password), RACObserve(self, passwordConfirmation) ] 
    reduce:^(NSString *password, NSString *passwordConfirm) {
            return @([passwordConfirm isEqualToString:password]);
}];

Can be wrote as something like:

RAC.RAC(self, "createEnabled", 
    RACSignal.combineLatest( {} ) 
              reduce ( (password, passwordConfirm) => {
	          return passwordConfirm == password;
}));

But it failed to compiled. The method declaration of RACSignal::combineLatest:reduce is:

public static ReactiveCocoa.RACSignal combineLatest(this ReactiveCocoa.RACSignal @this, Foundation.INSFastEnumeration signals) reduce(delegate rtl.id() reduceBlock);

Pls note that reduce take “delegate rtl.id()” as argument. So there’s no way to pass arguments to the block since the signature mismatch.

Indeed not. As to your other thread it’s to do with the special block, I’ll have a look at that.