Storing functions as objects in NSDictionary

Discussion in 'iOS Programming' started by l0uismustdie, Jun 28, 2010.

  1. l0uismustdie macrumors regular

    l0uismustdie

    Joined:
    Nov 30, 2009
    Location:
    Edinburgh, UK
    #1
    I am wondering if anyone has any ideas how I might be able to store a function as the object in an NSDictionary. I know in python this can be done with something like:
    Code:
    dictName={"key",function_name}
    
    But I am not sure how something like this could be accomplished in objective c.

    Thanks in advance.
     
  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    It'll be somewhere between amazingly difficult and completely impossible. I'm leaning towards the latter.
     
  3. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #3
    C function or selector?

    Since selectors can be made from strings

    SEL NSSelectorFromString(NSString *aSelectorName);
    and
    NSString *NSStringFromSelector( SEL aSelector );

    so...

    Code:
    NSDictionary *selectorDict = [NSDictionary dictionaryWithObjectsAndKeys: NSStringFromSelector(@selector(addObject:)), @"addObjectKey", nil];
    
    //And then later...
    [object perforSelector: NSSelectorFromString( [selectorDict objectForKey: @"addObjectKey"] )];
    
    Though off the top of my head I cannot think of any specific practical use of doing this...
    For C-Functions I would guess that it can be done with NSValue and function pointers.
     
  4. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #4
    Whilst the above will work it would also probably be sensible to store the instance you intend to call the selector on. Without an object to call on the selector on it's own is not all that much use. If you want to wrap up the entire thing as a single object you could use an NSInvocation and encode that with a NSCoder.

    I must admit I assumed that we were talking about C functions as the OP said "function", not method.
     
  5. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #5
    Because he was interested in storing it in an NS collection class I kinda assumed he MIGHT be talking about selectors instead of C functions.

    HYPOTHETICALLY------
    lets say we have a function named and defined like
    int add(int a, int b) { return (a+b); }

    We can define a pointer to it like..

    int (*function)(int a, int b) = &add;

    Now if we put this pointer into an instance of NSValue and stored it in an array it would sorta be like putting the function add(int a, int b) into that array.

    Now you can certainly make C arrays of these, and reference them by index. So I would think you could store the "address" of a function for later use... though I cannot think of a reason why off the top of my head. Other than to implement object oriented programming in C.
     
  6. Thomas Harte macrumors 6502

    Joined:
    Nov 30, 2005
    #6
    No need for the NSCoder — just put the NSInvocation directly in the dictionary.

    Separately, to build on what jared_kipe was saying (ummm, sort of), a SEL is a C type (almost certainly a typedef of some sort of pointer, but I can't find any guarantee), so you should be able to take a pointer to one and store that in an NSValue.
     
  7. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #7
    I think this discussion would probably benefit from an explanation from the OP as to why he wants to do this. I would bet that there is something the OP hasn't thought of that would get us around the whole function in collection class problem.
     
  8. bredell macrumors regular

    Joined:
    Mar 30, 2008
    Location:
    Uppsala, Sweden
    #8
    Isn't this what blocks is for? From what I understand blocks are used to encapsulate code as objects.

    Code:
    NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
    	^(double p) { return (p + 2); }, @"addTwo",
    	^(double p) { return (p * 3); }, @"mulThree",
    	^(double p) { return sqrt(p); }, @"root",
    nil];
    	
    double x = 2.5;
    for (id key in dict) {
    	double (^func)(double) = [dict objectForKey:key];
    	NSLog(@"%@: %f", key, func(x));
    }
     
  9. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #9
    Ok so I cheat, but you could either put the NSPointerArray into a dictionary with a key, or just use the NSPointerArray instead of a dictionary.

    Code:
    #import <Foundation/Foundation.h>
    int add(int a, int b){ return (a+b); }
    int (*function)(int a, int b) = NULL;
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    	
    	NSPointerArray *pointerArray = [NSPointerArray pointerArrayWithWeakObjects];
    	[pointerArray addPointer: &add];
    	
    	function = [pointerArray pointerAtIndex:0];
    	int a,b;
    	a = 2;
    	b = 5;
    	NSLog(@"a = %d, b = %d, function(a,b) = %d", a, b, (*function)(a,b) );
    	
        [pool drain];
        return 0;
    }
    
    C function put into an array! *BAM*
     
  10. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
  11. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #11
    Don't know why I didn't take my own advice and look into NSValue, mainly because I've only used NSValue with point and @encode.

    So slight modification to prior code
    Code:
    #import <Foundation/Foundation.h>
    int add(int a, int b){ return (a+b); }
    int (*function)(int a, int b) = NULL;
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    	
    	NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
    						  [NSValue valueWithPointer: &add], @"addFunctionKey", nil];
    
    	function = [[dict objectForKey: @"addFunctionKey"] pointerValue];
    	int a,b;
    	a = 2;
    	b = 5;
    	NSLog(@"a = %d, b = %d, function(a,b) = %d", a, b, (*function)(a,b) );
    	
        [pool drain];
        return 0;
    }
    
     
  12. l0uismustdie thread starter macrumors regular

    l0uismustdie

    Joined:
    Nov 30, 2009
    Location:
    Edinburgh, UK
    #12
    Thanks everyone for your posts. I suppose I should give out some more info..sorry the first post was so vague. It looks like I am actually trying to store a method in the dictionary. Excuse my lapse in terminology...I am new to objective c and actually rarely do OO programming. Here is the full situation:
    I am essentially mimicking a symbol table. So I have a dictionary of what would represent the symbol table, let's call it symboltable. This stores memory address as the key and the symbol as the object. Let's say for example I come across the symbol "boinc_init". What I want to be able to do is look for this symbol in a dictionary of known_symbols which contains each symbol as a key and the method it should run as a value.
    I know this can easily be done by some conditional statements...I am just trying to be fancy.
    So I have a virtual machine class that contains all of my methods. Let's say that I have a method "dummy" declared. If I find "boinc_init" at my current location inside my symboltable dictionary I want to run this dummy method.

    I hope this makes a little more sense...and sorry for saying function.

    Just realized these might help as well:
    This is the dummy function:
    Code:
    -(NSDictionary*)dummy:(NSMutableDictionary *)memory atIP:(unsigned long)IP withRegs:(NSMutableArray *)reg andSymbolTable:(NSMutableDictionary *)symboltable
    {
    	NSDictionary			*returnDict;
    	NSLog(@"Dummy function. Nothing happens");
    	[returnDict setValue:memory forKey:@"memory"];
    	[returnDict setValue:reg forKey:@"reg"];
    	return returnDict;
    }
    
    This is where I am trying to call it. I haven't implemented the comparison between the current location in the symboltable (IP) and the known_symbols
    Code:
    -(NSDictionary*)boinccall:(NSMutableDictionary *)memory atIP:(unsigned long)IP withRegs:(NSMutableArray *)reg andSymbolTable:(NSMutableDictionary *)symboltable
    {
    	NSDictionary		*returnDict;
    	NSDictionary		*known_symbols=??????
    
    	return returnDict;
    	
    }
    
    So, just maybe in recap. boinccall() gets called and it should compare symboltable[IP] with the known_symbols, which should be no problem. The real question is: if symboltable[IP] is in the known_symbols dictionary how can it use that to run a specific function.

    I hope this all helps and doesn't hinder.
     
  13. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #13
    My very first reply should have what you need. (it sounds like)

    Every objective-C method is defined by a @selector(methodName)

    You can make a selector from a NSString instance so store the NSString that describes the method in your array.

    One of the polymorphisms of objective-c is that you can dispatch a selector at runtime via

    [object performSelector: selector];

    Here are some ways to make a selector.
    @selector(methodName:andArgument:)
    NSSelectorFromString( (NSString *)stringObject )

    EDIT:
    It sounds like you maybe don't know how to tell if a NSDictionary has a key. If you ask a dictionary for objectForKey: key and key isn't in the dictionary it will return nil. So you can either just message nil, or test to see if the return is nil.
     
  14. l0uismustdie thread starter macrumors regular

    l0uismustdie

    Joined:
    Nov 30, 2009
    Location:
    Edinburgh, UK
    #14
    Almost there I think...
    Here is what I have so far:
    Code:
    NSDictionary	*known_symbols=[NSDictionary dictionaryWithObjectsAndKeys:NSStringFromSelector(@selector(dummy:)),@"boinc_init",NSStringFromSelector(@selector(dummy:)),@"_start",nil];
    	
    if([known_symbols objectForKey:[symboltable objectForKey:[NSString stringWithFormat:@"%u",IP]]])
    {
            NSLog(@"Symbol Identified. Running substitute function.");
    	returnDict=[self performSelector:NSSelectorFromString([known_symbols objectForKey:[symboltable objectForKey:[NSString stringWithFormat:@"%u",IP]]])withObject:memory withObject:[NSNumber numberWithUnsignedLong:IP]];
    }
    
    This is giving me the error:
    Code:
    2010-06-30 11:44:57.132 vm[11588:207] *** -[virtual_machine_32 dummy:]: unrecognized selector sent to instance 0x3915b50
    
    Also, it looks like there is only a performSelector:withObject:withObject. I need to send 4 objects in this case any thoughts on that?

    jared...thanks a lot for your help.
    Also, finding whether or not the key is in the dictionary isn't a problem. My problem really is just using selectors like this.
     
  15. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #15
    This

    Code:
    @selector(dummy:)
    doesn't match this

    Code:
    -(NSDictionary*)dummy:(NSMutableDictionary *)memory atIP:(unsigned long)IP withRegs:(NSMutableArray *)reg andSymbolTable:(NSMutableDictionary *)symboltable
    Look at NSInvocation to call methods with 4 objects.
     
  16. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #16
    Define a single class that encapsulates your multiple parameters (tuples): memory, instruction pointer, registers, and symbols. Name it MyVirtualMachine. Then you can condense all your multiple-parameter methods into ones that take a single object containing all the necessary context, i.e. the virtual machine itself.

    This is a perfect example of when to create an object instead of passing multiple parallel parameters. Really, when will the instruction ptr, memory, or registers ever be used in isolation, i.e. separated from the rest of the vm context?
     
  17. l0uismustdie thread starter macrumors regular

    l0uismustdie

    Joined:
    Nov 30, 2009
    Location:
    Edinburgh, UK
    #17
    Bingo! Yeah I think I got what I'm looking for. Thanks again everyone. Here is my final result:
    Code:
    NSDictionary		*returnDict;
    NSDictionary		*known_symbols=[NSDictionary dictionaryWithObjectsAndKeys:NSStringFromSelector(@selector(dummy:)),@"boinc_init",NSStringFromSelector(@selector(dummy:)),@"_start",nil];
    Parameters		*toSend=[Parameters new];
    toSend.memory=memory;
    toSend.IP=[NSNumber numberWithUnsignedLong:IP];
    toSend.reg=reg;
    toSend.symbol_table=symboltable;
    
    if([known_symbols objectForKey:[symboltable objectForKey:[NSString stringWithFormat:@"%u",IP]]])
    {
    	NSLog(@"Symbol Identified. Running substitute function.");
    	returnDict=[self performSelector:NSSelectorFromString([known_symbols objectForKey:symboltable objectForKey:[NSString stringWithForat:@"%u",IP]]])withObject:toSend];
    	return returnDict;
    }
    
     
  18. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #18
    Agreed, you should never need to send more than 1 object unless it is in some way super convenient to send several.

    You can either pass it an array, or a custom object with members that you setup on your own.

    If you decide to send it an array, do yourself a favor and setup an enumerated type to say what index does what, such as...
    Code:
    typedef enum {
    	kMemoryIndex = 0,
    	kIPIndex,
    	kRegIndex,
    	kSymbolTableIndex
    } ParameterType;
    
     

Share This Page