Search code examples
c++objective-cflutterobjective-c++

How to call functions in c++ and objective-c to each other


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

Solution

  • 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)

    Objective-C++ approach

    Calling C++ code from Objective-C++

    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);
    }
    

    Calling Objective-C++ code form C++

    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];
    }
    

    C approach

    Calling C++ code from Objective-C

    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);
    }
    

    Calling Objective-C code from C++

    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);
        }
    };