While developing an application with Flutter framework, I have to use C++
and Objective-C
.
Sometimes you need to call a function of C++
from Objective-C
, and sometimes you need to call a function of Objective-C
from C++
.
I looked through several articles but couldn't find the answer I was looking for.
Add test code. This is a simple code.
objc.m
@implementation Objc
-(int) objcSum:(int)num1 secondInt(int)num2{
return num1 + num2;
}
Cpp
int MyCpp::sum(int n1, int n2){
int result = objcSum(n1, n2);
return result;
}
Cannot call objcSum()
.
AppDelegate.m
MyCpp* mycpp = [[MyCpp alloc]init];
int res = mycpp->sum(10, 20);
NSLog("@res : %d", res);
The myCpp object is not created, and the header file is imported
but cannot be referenced at all.
Edit
Calling c++
from objc
was successful.
Based on your answer, I wrote the code as below.
But calling objc
from c++
throws an error.
///ObjcSum.h
#ifndef objcSum_h
#define objcSum_h
#import <Foundation/Foundation.h>
@interface TDWObject : NSObject
-(NSInteger)addNum:(NSInteger)lhs second:(NSInteger)rhs;
@end
int TDWObjcSum(int num1, int num2);
#endif /* objcSum_h */
///ObjcSum.mm
#import "objcSum.h"
@implementation TDWObject
-(NSInteger)addNum:(NSInteger)lhs second:(NSInteger)rhs
{
return lhs + rhs;
}
@end
int TDWObjcSum(int num1, int num2)
{
return [[TDWObject new] addNum:num1 second:num2]; //Implicit conversion loses integer precision: 'NSInteger' (aka 'long') to 'int'
}
///Mycpp.h
class MySum {
public:
int ObjcCall(int num1, int num2);
};
///MyCpp.cpp
#include "mycpp.hpp"
#include "objcSum.h"
int MySum::ObjcCall(int num1, int num2)
{
int res = TDWObjcSum(num1, num2);
return res;
}
///AppDelegate.mm
MySum* mySum = new MySum();
int res = mySum->ObjcCall(10, 40);
delete mySum;
error code
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:535:61: Unknown type name 'NSString'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:539:30: Unknown type name 'NSString'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:539:53: Format argument not an NSString
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:540:31: Unknown type name 'NSString'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:540:63: Format argument not an NSString
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSZone.h:19:63: Unknown type name 'NSString'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSZone.h:20:19: Unknown type name 'NSString'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:523:1: Expected unqualified-id
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSZone.h:9:1: Expected unqualified-id
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObject.h:10:1: Expected unqualified-id
too many errors emitted, stopping now
While Objective-C
is a strict superset of C
, there is another language called Objective-C++
which is a strict superset of C++
. You can use Objective-C++
to write in C++
and Objective-C
in the same source files.
Another option is to avoid exposing any C++
and Objective-C
specific statements and keep it compatible with subset available to both languages (i.e. predominantly C
language)
This is quite simple, if you compile your project with Xcode and Apple Clang, just rename the source file extension from m
to mm
(e.g. from objc.m
to objc.mm
). It will make the compiler accept C++ code in the given source file. E.g. if you have a C++
header defined like this:
// MyCpp.hpp file
#include <string>
struct MyCpp {
std::string var;
int sum(int a, int b);
};
You can import it in any mm
file like any other header and seamlessly write C++ code inline:
// objc.mm
#import "MyCpp.hpp"
@implementation Objc
- (int)objcSum:(int)num1 secondInt(int)num2{
return MyCpp{}.sum(num1, num2);
}
Here the things are a little complicated. C++ as a programming language doesn't define any interoperability interface with Objective-C, so the best you can do is to switch from a compilation unit of C++ language to a compilation unit of Objective-C++ language where both syntaxes are acceptable (usually it merely means switching from cpp
extension to mm
extension). Again, assuming you have an Objective-C interface defined like this:
// MyObjc.h
#import <Foundation/Foundation.h>
@interface TDWObject: NSObject
- (NSInteger)addNum:(NSInteger)lhs toNum:(NSInteger)rhs;
@end
In some C++ class this can be called without any issues, provided the class exists within Objective-C++ compilation unit:
// MyCpp.mm
#import "MyCpp.hpp"
#import "MyObjc.h"
int MyCpp::sum(int a, int b) {
return [[TDWObject new] addNum:a toNum: b];
}
Both Objective-C and C++ support certain C syntax, so an alternative approach to have both languages callable from each other is to restrict headers to C-compatible statements only. E.g. you can have a header declared like this for a C++ implementation:
// CppSum.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int TDWCppSum(int a, int b);
#ifdef __cplusplus
}
#endif
(Side note: extern "C"
guard is required in order to avoid C++ naming mangling when linking this header to C++ source code. Otherwise C code would not be able to locate the symbols in the compiled C++ object file)
The implementation file, however, is not restricted anyhow, so you can use any C++ code your compiler supports:
// CppSum.cpp
#include "CppSum.h"
template<typename T>
class MySum {
public:
T sum(T lhs, T rhs) {
return lhs + rhs;
}
};
int TDWCppSum(int a, int b) {
return MySum<int>{}.sum(a, b);
}
In Objective-C it can be used like this:
// objc.m
#import "CppSum.h"
@implementation Objc
- (int)objcSum:(int)num1 secondInt(int)num2{
return TDWCppSum(num1, num2);
}
The approach is pretty much the same: use only C statements in a header, and don't expose any Objective-C implementation there (like @interface
or @protocol
statements):
// ObjcSum.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int TDWObjcSum(int a, int b);
#ifdef __cplusplus
}
#endif
And again, you can use whatever Objective-C features you want in the implementation:
// ObjcSum.m
#import "ObjcSum.h"
#import <Foundation/Foundation.h>
@interface TDWObject: NSObject
- (NSInteger)addNum:(NSInteger)lhs toNum:(NSInteger)rhs;
@end
@implementation TDWObject
- (NSInteger)addNum:(NSInteger)lhs toNum:(NSInteger)rhs {
return lhs + rhs;
}
@end
int TDWObjcSum(int a, int b) {
return [[TDWObject new] addNum:a toNum:b];
}
For C++ code it would be just another free function:
// mycpp.cpp
#include "ObjcSum.h"
struct S {
void foo() {
auto result = TDWObjcSum(10, 15);
}
};