Question

I've added a obj-c class (.mm and 2 headers) to my current Xcode 4.6 project. One header file has the prototype to call to the obj-c class and the other defines the .mm class. Here's what it looks like.

myinterface.h

#ifndef MYINTERFACE_H
#define MYINTERFACE_H

BOOL LaunchApp(CAtlString exePath);

#endif

myLaunchClass.h

#import "myinterface.h"

@interface myLaunchClass : NSObject
-(BOOL) LaunchApp:(CAtlString)exePath;
@end

myLaunchClass.mm

@import "myLaunchClass.h"

@implementation myLaunchClass
-(BOOL) LaunchApp:(CAtlString)exePath
{
    ....
    return someCondition;
}
@end

From there it compiles good. I added the .mm file to the Target in the Build Phases and the header location to the Header Search Paths in the Build Settings.

When I include the header file in my .cpp file (#include "myinterface.h") I have no errors. However, when I call my function (::LaunchApp(exePath);), I a linker error.

Error

Undefined symbols for architecture i386:
  "LaunchApp(CAtlString)", referenced from:
      myCppFile::myCppFunction() const in myCppFile.o
ld: symbol(s) not found for architecture i386

Any ideas? I think it must be an obvious mistake for a Mac developer but I'm a bit new still with Mac programming. Any help is appreciated.

Was it helpful?

Solution

Objective-C++ doesn't interoperate this way. -(BOOL)LaunchApp:(CAtlString)exePath is declaring an instance method on an Objective-C class. That method can only ever be called from Objective-C (i.e. .m, .mm) files, and if the method signature includes C++ types (like it does here), then it can only be called from Objective-C++ (.mm) files. Furthermore, -(BOOL)LaunchApp:(CAtlString)exePath is an instance method, and you appear to be calling it as if it were a C++ static/class method, which won't work either.

If you want to wrap your Objective-C class to make it available to straight C++ consumers, you would have to do something like this:

MyLaunchClass.h:

#if __cplusplus
#import <string>
#endif

@interface MyLaunchClass : NSObject

#if __cplusplus
- (BOOL)launchApp: (std::string)str;
#endif

@end

#if __cplusplus

struct WrappedMyLaunchClass
{
    MyLaunchClass* mImpl;

    WrappedMyLaunchClass() : mImpl([[MyLaunchClass alloc] init]) { };

    ~WrappedMyLaunchClass() { mImpl = nil; }; // Assuming ARC here. for non-ARC, [mImpl release]

    bool LaunchApp(std::string str)
    {
        return !![mImpl launchApp:str];
    }
};

#endif

MyLaunchClass.mm:

#import "MyLaunchClass.h"
#import <string>

@implementation MyLaunchClass

- (BOOL)launchApp: (std::string)str
{
    NSLog(@"%s", str.c_str());
    return YES;
}

@end

SomeOtherFile.cpp:

void someOtherFunction()
{
    WrappedMyLaunchClass x;
    x.LaunchApp("foobar");
}

In sum, you are overestimating the interoperability of C++ and Objective-C++. You can think of Objective-C++ as "Objective-C with the addition of the ability to have variables of C++ types and the ability to call C++ code" but not as "an alternate syntax for declaring C++ types."

Caveat Emptor!: This is drastically oversimplifying what would be involved in meaningfully wrapping an Objective-C object in a C++ object. C++'s by-value/by-reference semantics are drastically different from Objective-C's and a naive implementation like this is going to hit all sorts of problems if the C++ object is ever passed by value (i.e. via a copy constructor) etc. I provide the example only to show why what you attempted wouldn't work, not as some kind of general purpose code for wrapping Objective-C objects in C++ objects.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top