2

Currently I have an iOS project which is using Objective-C. For performance reasons I have implemented Objective-C++ code which looks like the following:

base11.mm

#include <string>
#include "base11.h"

static const std::string characters = "0123456789_";

const char* to_base11(long n) {
    // some c++ code here
}

long from_base11(const char* input) {
    // some c++ code here
}

base11.h

#ifndef base11_h
#define base11_h

#include <stdio.h>

const char* to_base11(long n);
long from_base11(const char* input);

#endif /* base11_h */

Of course, I used the .mm extension for the class, and I am only using plain C function parameters and return types.

Then I have an Objective-C class which wraps these C++ functions in the following methods:

Util.m

#include "base11.h"

+(NSString*) convertBase10ToBase11:(long) base10 {
    const char* base11 = to_base11(base10);
    return [NSString stringWithUTF8String:base11];
}

+(NSNumber*) convertBase11ToBase10:(NSString*) base11 {
    const char* input = [base11 UTF8String];
    return @(from_base11(input));
}

And the Util.h:

@interface Util : NSObject
    +(NSString*) convertBase10ToBase11:(long) base10;
    +(NSNumber*) convertBase11ToBase10:(NSString*) base11;
@end

However this gives the following compilation error:

Undefined symbols for architecture x86_64:
  "_from_base11", referenced from:
      +[Util convertBase11ToBase10:] in Util.o
  "_to_base11", referenced from:
      +[Util convertBase10ToBase11:] in Util.o
ld: symbol(s) not found for architecture x86_64

However, this is very weird. I have chosen to NOT use the .mm extension for the Util.m class since I don't use any C++ code there. Also note that the base11.h header file is not using any C++ code as well. So, why do I get this error? According to this post my approach should be correct:

How to call a method on .mm file from a objective c class

4
  • Add CF_EXTERN before to_base11 and from_base11 prototypes. Commented Nov 12, 2018 at 8:46
  • @Cy-4AH after adding these in the header file, the same error persists. Commented Nov 12, 2018 at 8:58
  • Note that the C standard library contains (usually fast) base 11 string reading functions in strtol/strtoul: strtoul(string, NULL, 11). (This assumes that "A" = decimal 10) Commented Nov 12, 2018 at 11:56
  • @pmdj yes, thank you. However in our implementation we have replaced the A with _ Commented Nov 12, 2018 at 11:58

1 Answer 1

4

This is problem in function's symbol name. C++ allows functions with same name and different prototype. So when functions compiled with C++/Objective-C++ it got symbol name that describes it prototype and makes it unique.

So you need to force compiler use C's symbol name. You can achieve that by adding extern "C" before function prototype. But it's not very convenient if you use this header in both C and C++ source files, because C doesn't know what it is extern "C".

Luckily CoreFoundation already have solved this issue. You can peep how they declared their CFString.h, CFArray.h.

So you just need use CoreFoundation's macro: CF_EXTERN_C_BEGIN and CF_EXTERN_C_END.

#ifndef base11_h
#define base11_h

#include <stdio.h>
#include <CoreFoundation/CFBase.h>

CF_IMPLICIT_BRIDGING_ENABLED
CF_EXTERN_C_BEGIN

const char* to_base11(long n);
long from_base11(const char* input);

CF_EXTERN_C_END
CF_IMPLICIT_BRIDGING_DISABLED

#endif /* base11_h */
Sign up to request clarification or add additional context in comments.

4 Comments

Unfortenately, the same error persists. Thanks anyway.
This should fix the problem, are you sure the .mm file is being linked into your project? Find the Object file (.o) corresponding to your .mm file and check what symbols it exports using the nm command: nm path/to/base11.o; the path will be something like /Users/USERNAME/Library/Developer/Xcode/DerivedData/PROJECTNAME-RANDOMSTRING/Build/Intermediates/PROJECTNAME.build/Debug/TARGETNAME.build/Objects-normal/x86_64/base11.o Check that _from_base11 and _to_base11 are listed as T.
@GiovanniTerlingen If they're there, you're not linking against the file. If they're missing, you've got the extern "C" part wrong. To avoid pulling in CoreFoundation (or to rule out issues with your use of those macros), you can just wrap the declarations in #ifdef __cplusplus extern "C" { #endif#ifdef __cplusplus } #endif
Your answer, in combination with adding the file to the build variant (it was not added -.-) worked! Thanks a lot and have a nice day.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.