18

For those of you who have successfully been able to call C++ code from Objective-C, will you please enlighten me?

This link says you need to wrap your C++ code using a technique he describes in his article. It looks good but I still have problems.

This link says that as long as the Objective-C class calling the C++ class has been converted to a .mm (Objective-C++) class then the two should work nicely together.

Each of these approaches are causing me grief the minute I try to add a method call. Can somebody please give me code for a simple Hello World iOS app that uses Objective-C for the "Hello" part and a C++ class for the "World" part with an Objective-C++ class in the middle? Or do I still have the entire concept wrong?

Community
  • 1
  • 1
Patricia
  • 5,019
  • 14
  • 72
  • 152
  • 1
    The second approach is what I use successfully. Describe your grief in more detail. – trojanfoe Oct 07 '13 at 16:11
  • 1
    For the most part, you just throw all the "gotta work together" code into one .mm file and call back and forth as needed. The only real "gotcha" is storage management. – Hot Licks Oct 07 '13 at 17:24
  • 1
    Describe your grief in more detail. – Hot Licks Oct 07 '13 at 18:33
  • @lucky: Did you get any solution to this ? , i also want a simple helloword app to know it actually works – Piyush matta Apr 08 '15 at 12:26
  • @Piyushmatta - yes, I did. Give me a few and I'll give you the help you're looking for. :-) – Patricia Apr 08 '15 at 14:57
  • @Piyushmatta - I can at least give you some information to get started until I can give a complete but simple solution. Essentially you need an ObjC class with .mm extension that calls a C++ wrapper class with .mm extension that calls your actual .cpp class. It's a little tricky setting it up, so give it a try and I'll follow up with the solution as soon as I can. – Patricia Apr 08 '15 at 18:04
  • @Piyushmatta - See my answer for code that will show you how to do it. – Patricia Apr 08 '15 at 22:06

4 Answers4

25

Essentially you need an ObjC class with .mm extension that calls an ObjC class with .mm extension. The second one will be used as a C++ wrapper class. The wrapper class will call your actual .cpp class. It's a little tricky, so I'm going to give you some verbose code. Here is an overview of the project:

enter image description here

In your ObjC code (ViewController) you would call the CplusplusMMClass

- (IBAction)buttonPushed:(UIButton *)sender {
    self.mmclass = [[CplusplusMMClass alloc]init];  // bad practice; but showing code
    NSString *str = [self.mmclass fetchStringFromCplusplus];
    [self populateLabel:str];
}

Here is the CplusplusMMClass .h and .mm

#import <Foundation/Foundation.h>
#import "WrapperClass.h"

@interface CplusplusMMClass : NSObject
@end

@interface CplusplusMMClass()
@property (nonatomic, strong) WrapperClass *wrapper;
- (NSString*)fetchStringFromCplusplus;
@end

#import "CplusplusMMClass.h"
#import "WrapperClass.h"

@implementation CplusplusMMClass

- (NSString*)fetchStringFromCplusplus {
    self.wrapper = [[WrapperClass alloc] init];
    NSString * result = [self.wrapper getHelloString];
    return result;
}

@end

Here is WrapperClass .h and .mm

#ifndef HEADERFILE_H
#define HEADERFILE_H

#import <Foundation/Foundation.h>

#if __cplusplus

#include "PureCplusplusClass.h"

@interface WrapperClass : NSObject
@end

@interface WrapperClass ()
- (NSString *)getHelloString;
@end


#endif
#endif

#import "WrapperClass.h"

#include "WrapperClass.h"
#include "PureCplusplusClass.h"

using namespace test;

@interface WrapperClass ()
@property (nonatomic) HelloTest helloTest;
@end

@implementation WrapperClass

- (NSString *)getHelloString {
    self.helloTest = *(new HelloTest);
    std::string str = self.helloTest.getHelloString();
    NSString* result = [[NSString alloc] initWithUTF8String:str.c_str()];
    return result;
}

@end

Here is the PureCplusplusClass .h and .cpp

#ifndef __HelloWorld__PureCplusplusClass__
#define __HelloWorld__PureCplusplusClass__

#include <stdio.h>
#include <string>

using namespace std;

namespace test {
    class HelloTest
    {
    public:
        std::string getHelloString();
    };
}

#endif /* defined(__HelloWorld__PureCplusplusClass__) */

#include <stdio.h>
#include <string>

std::string test::HelloTest::getHelloString() {
    std::string outString = "Hello World";
    return outString;
}

This code is not perfect! I'm having trouble with the namespace test being recognized. I'll update when I can.

But this should get you there!!!!

Patricia
  • 5,019
  • 14
  • 72
  • 152
  • dear! Can you upload your project anywhere and give me link? I was try to repeat Ctrl+C -> Ctrl+V and I have some error: Undefined symbols for architecture x86_64: "test::HelloTest::getHelloString()", referenced from: -[WrapperClass getHelloString] in WrapperClass.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) I suspect that I need to plug some framework. What do you think about this? – Andrew Kachalin Aug 10 '16 at 14:25
  • 1
    Hi, Andrew: The error you are getting suggests that the problem is with your architecture settings or how you are linking. I have created a new project since the code above is a bit antiquated. Apparently we no longer need to use "#if __cplusplus" and the C++ #ifndef and #define are different. The new project is located here: [https://drive.google.com/open?id=0B8tC1ewFw3KlZ2RZM28yQmhPRTg](https://drive.google.com/open?id=0B8tC1ewFw3KlZ2RZM28yQmhPRTg). If you are still having trouble, see the answers to this question: http://stackoverflow.com/q/7533321/1735836 – Patricia Aug 15 '16 at 21:19
  • Andrew: Let me know if you're still have problems and cannot find the answer. I'm curious. – Patricia Aug 15 '16 at 21:27
  • @Patricia hi I have the same problem and I downloaded your project form google drive but I can't run it cuz it doesn't have storyboard."/Users/xinruchen/Downloads/HelloWorld/HelloWorld/Base.lproj/LaunchScreen.storyboard: Interface Builder could not open the document LaunchScreen.storyboard" because it does not exist." thx – Tony Chen Apr 22 '17 at 17:16
  • @TonyChen - I haven't looked at this project in years and I have no idea how much has changed but I'll try to have a look at it sometime this week. – Patricia Apr 24 '17 at 19:23
  • @TonyChen - Sorry I didn't get to this. I'm slammed at work. Do tell! PLEASE! Someone else may need the information. I can upvote your answer and others can to but only if you provide one. :-) – Patricia May 24 '17 at 18:27
  • @Patricia oh it's been a long time since I am on this site last time so I cannot remember... but I think I solved my problem by another way not this, so it's not going to be helpful anyway – Tony Chen Jul 03 '17 at 22:28
  • @TonyChen how you solved the missing storyboard problem? – Oldes Mar 13 '18 at 11:32
2

Another (contrived) one:

Use a C++ class as an ivar:

File Foo.h

#import <Foundation/Foundation.h> 

@interface Foo : NSObject
@property (nonatomic, readonly) NSString* what;
@end

File: Foo.mm

#import "Foo.h"
#include <string>

@implementation Foo {
    std::string _string;  // C++ class must have a default c-tor
}

- (id)init
{
    self = [super init];
    if (self) {
        _string = "Hello, World!"
    }
    return self;
}

- (NSString*) what {
    NSString* result = [[NSString alloc] initWithBytes:_string.data()
                                                length:_string.size() 
                                              encoding:NSUTF8StringEncoding];
    return result;
}

@end

Note:

An executable may need to explicitly link against the C++ library, e.g. by adding an additional flag to "Other Linker Flags": -lc++

Alternatively, the main.m file can be renamed to main.mm.

The latter is more robust in selecting the "correct" library, since the tool chain will do that itself. But perhaps, for anyone else examining the project, a renamed "main.m" may not be that obvious and may cause confusion.

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
-1

I ran into this situation with xcode 12.4, needing to use an existing C++ class "CppModule" in an objective-c project. Here is what I did.

  1. First problem was that #include in CppModule.hpp gave a compiler error: 'string' file not found. Fixed that by wrapping the .hpp file contents in
#if defined __cplusplus
  declarations
#endif
  1. Then string var declarations gave another compiler error: Unknown type name 'string'; did you mean 'std::string'? Fixed that by:
#include <string> 
using namespace std;
  1. The C++ CppModule.cpp, the CppModule.hpp include file, the calling objective-c module.m, and it's module.h files MUST all be set as Type "Objective-C++ Source", using xcode's Inspector (top right in the editor window). Renaming module.m to module.mm also sets the type to "Objective-C++ Source" (xcode 12.4). Perhaps good to use .mm, as a reminder it calls C++.

In the module.h file include the C++ header:

#include < CppModule.hpp>
  1. Odd (to me) is that in the module.h file "CppModule" is not recognised as a known type, so you cannot declare vars or properties of that type in your module.h, but in the module.m(m) file it is a known type. In the module.mm file, after the @implementation add:
static CppModule *module_cpp;
  1. Now you can, e.g. in the module.mm's init, call the C++ module's initialiser, like:
module_cpp = new CppModule(parameters);

And the C++ module remains accessible via that static module_cpp *pointer.

  1. Call the module.cpp's methods (example) in your objective-c method:
CppModule::Result r = module_cpp->CppMethod();

Appears to work absolutely fine!

If anyone can explain the oddities in 4. I'd be grateful.

The module I needed just does some complex (numeric) calculations, and I did not feel like converting that code to Objective-C.

RickJansen
  • 1,615
  • 18
  • 24
-1

IOS Netbanking sample for Objective c

- (IBAction)Button:(id)sender {

    NSMutableArray *userdata=[[NSMutableArray alloc]initWithObjects:@"Name",@"CreditcardNumber",@"Pinnumber",@"Active", nil]; // arrkey = userdata.

    NSMutableArray *Yogesh=[[NSMutableArray alloc]initWithObjects:@"Yogesh",@"908380637367",@"5656",@"yes", nil];
    NSMutableArray *sabari=[[NSMutableArray alloc]initWithObjects:@"Sabari",@"7635298721",@"6543",@"no",nil];
    NSMutableArray *rajesh=[[NSMutableArray alloc]initWithObjects:@"rajesh",@"8373197272",@"8765",@"yes",nil];
    NSMutableArray *Ramya =[[NSMutableArray alloc]initWithObjects:@"Ramya ",@"123456",@"9898",@"No",nil];
    NSMutableDictionary * dic= [[NSMutableDictionary alloc]initWithObjects:Yogesh forKeys:userdata];
    NSMutableDictionary * dic1=[[NSMutableDictionary alloc]initWithObjects:sabari forKeys:userdata];
    NSMutableDictionary * dic2=[[NSMutableDictionary alloc]initWithObjects:rajesh forKeys:userdata];
    NSMutableDictionary * dic3=[[NSMutableDictionary alloc]initWithObjects:Ramya  forKeys:userdata];
    /* NSLog(@"%@", dic);
     NSLog(@"%@", dic1);
     NSLog(@"%@", dic2);*/

    NSMutableArray * arraylist=[[NSMutableArray alloc]initWithObjects:dic,dic1,dic2,dic3, nil];
    NSLog(@"Array = %@", arraylist);
    NSLog(@"user entered value %d", _ccNumber.text.intValue);
    NSLog(@"pin number %d ",_pin.text.intValue);

    for(int i = 0; i< [arraylist count]; i++){
    
        NSMutableDictionary *dictionary= arraylist[i];
            
        // NSLog(@"userdata is [%d] %@",i,[dictionary objectForKey:@"Pinnumber"]);
    
        NSInteger a;
        NSInteger vx;
        NSString *b;
        NSString *str;
        a = (self.ccNumber.text.integerValue);
        vx = (self.pin.text.integerValue);
        b = ([dictionary objectForKey:@"CreditcardNumber"]);
        str = ([dictionary objectForKey:@"Pinnumber"]);
        NSInteger c = [b integerValue];
        NSInteger d = [str integerValue];
        if(a == c && vx == d){
            NSLog(@" user data is [%d] Name: %@",i,[dictionary objectForKey:@"Name"]);
            NSLog(@" user data is [%d] Card Number: %@",i,[dictionary objectForKey:@"CreditcardNumber"]);
            NSLog(@" user data is [%d] Pin Number :%@",i,[dictionary objectForKey:@"Pinnumber"]);
            NSLog(@" user data is [%d] Active: %@",i,[dictionary objectForKey:@"Active"]);
        }else{
        
            NSString *DataStatus= @"Invalid Card Number";
        
            self.dataStatusLbl.text = DataStatus;
        
            NSLog(@"%@",DataStatus);
        }
    }
}
@end
James Risner
  • 5,451
  • 11
  • 25
  • 47