Peaceable Kingdom: Objective-C and C++

An Objective-C program in a .m file is simply C with @keywords and [square brackets]. An Objective-C++ program in a .mm file is simply C++ with @keywords and [square brackets]. See Using C++ With Objective-C.

The variable hello in the drawRect: method of class View is a pointer to an Objective-C object. The variable world is a C++ object. We call its member function c_str, which returns a pointer to the first character in the string.

Source code in Peaceable.zip

  1. main.m
  2. Class PeaceableAppDelegate
  3. Class View

Create the project

Simply rename View.m to View.mm. Right-click on View.m in the Groups & Files pane of Xcode, and select Rename. In the comment at the start of View.mm, change the filename from View.m to View.mm. View.mm must #include the header file for the C++ class string.

Things to try

  1. Immediately after including the C++ header file <string>, say
    using namespace std;
    
    Then change the std::string to string.

  2. Change the storage allocation of the C++ object from automatic to dynamic. Remember to change the dot to an arrow.
    	std::string *world = new std::string("World");	//C++ object
    
    	NSString *sum = [NSString stringWithFormat: @"%@, %s!",
    		hello, world->c_str()];
    
    	delete world;
    

  3. We have just called a member function (c_str) of a C++ object in the middle of Objective-C code. Now we will call a method (drawAtPoint:withFont:) of an Objective-C object by writing C++ code. See the Objective-C Runtime Programming Guide and the Objective-C Runtime Reference. Include the header file <objc/runtime.h> in View.mm. In the drawRect: method of class View, change
    	[sum drawAtPoint: CGPointZero withFont: f];
    
    to the following. The pointer p points to a function that implements an Objective-C method that takes two arguments (a CGPoint structure and a pointer to a UIFont object) and returns a CGSize structure.
    	Class c = object_getClass(sum);
    	if (c == Nil) {	//upercase N for value of type Class
    		NSLog(@"object_getClass returned Nil.");
    		return;
    	}
    	NSLog(@"class_getName(c) == %s", class_getName(c));
    
    	//does the same thing as SEL s = @selector(drawAtPoint:withFont:);
    	SEL s = sel_registerName("drawAtPoint:withFont:");
    	NSLog(@"sel_getName(sel) == %s", sel_getName(s));
    
    	Method m = class_getInstanceMethod(c, s);
    	if (m == NULL) {
    		NSLog(@"class_getInstanceMethod returned NULL.");
    		return;
    	}
    	NSLog(@"sel_getName(method_getName(m)) == %s", sel_getName(method_getName(m)));
    
    	CGSize (*p)(id, SEL, CGPoint, UIFont *) =
    		reinterpret_cast<CGSize (*)(id, SEL, CGPoint, UIFont *)>(method_getImplementation(m));
    	if (p = NULL) {
    		NSLog(@"method_getImplementation returned NULL.");
    		return;
    	}
    	NSLog(@"p == %p", p);
    
    	//Call drawAtPoint:withFont:.
    	CGSize size = (*p)(sum, s, CGPointZero, f);
    
    	NSLog(@"The method drawAtPoint:withFont: returned the size %g × %g.",
    		size.width, size.height);
    
    class_getName(c) == NSCFString
    sel_getName(sel) == drawAtPoint:withFont:
    sel_getName(method_getName(m)) == drawAtPoint:withFont:
    p == 0x2d4e3c
    The method drawAtPoint:withFont: returned the size 184 × 38.
    
    Two simplifications. Create a typedef (i.e., a one-word name) for the data type
    CGSize (*)(id, SEL, CGPoint, UIFont *)
    which is the data type of a pointer to a function that takes four arguments and returns a CGSize structure. (Without the first pair of parentheses it would be the type of a function that returns a pointer, rather than a pionter to a function.)
    	typedef CGSize (*pointer_t)(id, SEL, CGPoint, UIFont *);
    	pointer_t p = reinterpret_cast<pointer_t>(method_getImplementation(m));
    
    And when we finally call the function to which p points, we don’t have to dereference p explicitly.
    	//Call drawAtPoint:withFont:.
    	CGSize size = p(sum, s, CGPointZero, f);
    

  4. Package the above code in a C++ function named callMethod. Change
    	[sum drawAtPoint: CGPointZero withFont: f];
    
    to
    	callMethod(sum, "drawAtPoint:withFont", CGPointZero, f);
    
    Of course, there’s still the problem that callMethod will need a variable number of arguments of variable data types. Can we implement this with the C++ ellipsis (...) in IMP, C++ function name overloading, or C++ templates?

  5. Discover how A C++ program could call a class method such as the alloc method of class NSObject. Can we write the app entirely in C++?