Skip to content

Instantly share code, notes, and snippets.

@enigmaticape
Created November 22, 2012 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enigmaticape/4131386 to your computer and use it in GitHub Desktop.
Save enigmaticape/4131386 to your computer and use it in GitHub Desktop.
sample code for a better performSelector (see comments for link)
/* lots of easy to spot sentinel values */
variadicFunction( 0x42,
DEADBEEF, OOOOF642, DEADBEEF,
B_yes, FACEF00D,
b_true, FACEF00D,
(char)0x42, CAFED00D,
aString, DEADBEEF,
DEADCAFEBEEFD00D );
int hax0rSomeInts( int n, ... ) {
va_list args;
va_start( args, n );
int sum = 0;
for( int i = 0; i < n; i++ ) {
sum += *((int*)args);
args += sizeof(int);
}
va_end( args );
return sum;
}
int DEADBEEF = 0xefbeadde; // DE AD BE EF
int FACEF00D = 0x0df0cefa; // FA CE F0 0D
float OOOOF642 = 123.000; // 00 00 F6 42
BOOL B_yes = YES; // 01
bool b_true = true; // 01
void* CAFED00D = (void*)0x0dd0feca; // CA FE D0 0D
long long DEADCAFEBEEFD00D // DE AD CA FE BE EF D0 0D
= 0x0dd0efbefecaadde;
NSString * aString = @"A string";
- (NSValue *) invokeSelector:(SEL)selector, ... {
va_list args;
va_start( args, selector );
NSMethodSignature * signature
= [self methodSignatureForSelector:selector];
NSInvocation * invocation
= [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:selector];
NSUInteger arg_count = [signature numberOfArguments];
for( NSInteger i = 0; i < arg_count - 2; i++ ) {
/* we now need to check a few things,
firstly whether we have a float
*/
if( strcmp( [signature getArgumentTypeAtIndex: 2 + i ], "f") == 0 ) {
/* If we do have a float, we need to get the value,
and then convert it back to a float ...
*/
double doublvalue = va_arg( args, typeof(double) );
float floatvalue = (float)doublvalue;
/* ... before we copy it into position
*/
[invocation setArgument:&floatvalue atIndex: 2 + i ];
/* Now we increment the pointer past the
argument so it points at the next one
*/
args += sizeof( double );
}
else {
/* if it isn't a float, we want to find out
what size it is, bearing in mind the whole
promotion thingamajig
*/
NSUInteger size;
NSGetSizeAndAlignment( [signature getArgumentTypeAtIndex: 2 + i ],
&size,
NULL );
NSUInteger actual_size = size >= 4 ? size : 4;
/* since our single byte promoted values will be in
the first byte, this should be OK, just pass the
pointer and let NSInvocation copy one (or however many)
bytes
*/
[invocation setArgument:args atIndex: 2 + i ];
/* But we *do* need to make sure we increment the
pointer by enough bytes
*/
args += actual_size;
}
}
/* and in theory, that should be enough for us
to do the invocation
*/
[invocation invoke];
va_end( args ); // As far as we know, this is a no op. Hmmm.
/*
for the moment, we'll just return an NSValue again
since there's no particulalrly nice way to return an
arbitrary type
*/
NSValue * ret_val = nil;
NSUInteger ret_size = [signature methodReturnLength];
if( ret_size > 0 ) {
void * ret_buffer = malloc( ret_size );
[invocation getReturnValue:ret_buffer];
ret_val = [NSValue valueWithBytes:ret_buffer
objCType:[signature methodReturnType]];
free(ret_buffer);
}
return ret_val;
}
- ( void ) invokeSelector:( SEL )selector
withReturnValue:( void* ) retval, ... {
va_list args;
va_start( args, selector );
/* all the same stuff
....
....
*/
va_end( args ); // As far as we know, this is a no op. Hmmm.
/*
OK, this time, we'll copy the value straight out, like this,
and NSValue is all gone.
*/
if ( [signature methodReturnLength] > 0 ) {
[invocation getReturnValue:retval];
}
}
TestObj * testobj = [[TestObj alloc] init];
SEL selector
= @selector(printString:aBOOL:aBool:aChar:anInt:andIncrement:);
float aFloat = 123.456;
int anInt = 42;
BOOL aBOOL = YES;
bool aBool = YES;
char aChar = 'z';
NSString * aString = @"The answer is";
NSValue * result;
float floatresult;
float anotherfloat = 0;
NSLog(@"%@ : %@ : %@ : %c : %i : (%f)",
aString,
aBOOL ? @"YES" : @"NO",
aBool ? @"true" : @"fale",
aChar,
anInt,
aFloat
);
result = [testobj invokeSelector:selector,
aString,
aBOOL,
aBool,
aChar,
anInt,
aFloat
];
[result getValue: &floatresult];
NSLog(@"%f", floatresult);
[testobj invokeSelector:selector withReturnValue:&anotherfloat,
aString,
aBOOL,
aBool,
aChar,
anInt,
aFloat
];
NSLog(@"%f", anotherfloat);
#import "TestObj.h"
@implementation TestObj
-(float) printString:(NSString*) aString
aBOOL:(BOOL) aBOOL
aBool:(bool) aBool
aChar:(char) aChar
anInt:(int) anInt
andIncrement:(float) aFloat
{
NSLog(@"%@ : %@ : %@ : %c : %i : (%f)",
aString,
aBOOL ? @"YES" : @"NO",
aBool ? @"true" : @"false",
aChar,
anInt,
aFloat
);
return aFloat + 1.0;
}
@end
typedef struct {
/* offset into reg_save_area where
the next available general purpose
argument is located
*/
unsigned int gp_offset;
/* offset into reg_save_area where
the next available floating point
argument is located
*/
unsigned int fp_offset;
/* pointer to start of any arguments
which were passed on the stack
*/
void *overflow_arg_area;
/* pointer to the area of memory to which
the registers have been saved by the
variadic function's prolougue.
*/
void *reg_save_area;
} va_list[1];
void variadicFunction(int n, ... ) {
va_list args;
va_start(args, n);
/* do
some
stuff
*/
va_end( args );
}
int sumSomeInts( int n, ... ) {
va_list args;
va_start( args, n );
int sum = 0;
for( int i = 0; i < n; i++ ) {
sum += va_arg( args, int );
}
va_end( args );
return sum;
}
@enigmaticape
Copy link
Author

From a short series on doing mo better performSelector type stuff, explanatory post on the Enigmatic Ape blog at http://www.enigmaticape.com/blog/better-performselector-evil-fun-with-va_args/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment