1

I have an ObjC class called Log. In this class I have a public static method call error.

I want to call this method from my C++ code (if possible like this: Log::error(string);).

I was going around the internet and stack overflow but I found only this:

http://philjordan.eu/article/mixing-objective-c-c++-and-objective-c++

and this:

Calling Objective-C method from C++ method? (I tried to use the first method but I don't know how to create the void *objectiveCObject variable.)

Community
  • 1
  • 1
Marti Markov
  • 746
  • 6
  • 25
  • I also have a C++ class called `Log` which is used from both C++ and Objective-C++ and this wasn't an issue for me as I implemented the class using C++, and not Objective-C++, which is kind of the *lowest common denominator* of the two languages. Would that approach work for you? – trojanfoe Nov 07 '13 at 08:58
  • I wanted to do it with objc code as I'm trying to gain more exp in it. – Marti Markov Nov 07 '13 at 09:18
  • Sure, but it's clumsy, especially for a logging class which is used frequently and in many places. The only real mechanism available to allow C++ to Objective-C is a *callback mechanism* using a C/C++ function. This gets more complicated if you want to use instance methods as you have to pass around the class pointer as `void *`. If you want to gain Objective-C experience then choose something else to do it with. – trojanfoe Nov 07 '13 at 09:21
  • My question was how to create that pointer in the c++ code. I know it is an overkill but the Log class is just one of many that will use this functionality and interconnection. – Marti Markov Nov 07 '13 at 09:44
  • OK, I will post an answer on my take on that. – trojanfoe Nov 07 '13 at 09:45

1 Answers1

2

Within the header file for the Log class you need to provide a callback mechanism with C-linkage that can be used by C++ and you need to guard against C++ seeing the Objective-C stuff. This header can then be shared by both C++ and Objective-C. An alternative to this is to provide a separate header file and implementation file solely for the use of C++, however that adds more cruft to the implementation.

As per your comment, I have added C++ accessible methods to create and destroy the Objective-C Log object, however I'm certain for this to work you'll have to use MRR rather than ARC so that you can manage the lifetime. So you you will need to compile Log.m with -fobjc-no-arc.

Log.h:

#ifdef __OBJC__
#import <Foundation/Foundation.h>
#else // !__OBJC__
#include <stdarg.h>
// And maybe other C++ headers...
#endif // __OBJC__

typedef void *ObjcLog;

#ifdef __cplusplus
extern "C" {
#endif

extern ObjcLog *logCreate(const char *filename);
extern void logDestroy(ObjcLog *logObj);
extern void logInfo(ObjcLog *logObj, const char *msg, ...);
extern void logError(ObjcLog *logObj, const char *msg, ...);

#ifdef __cplusplus
}  // extern "C"
#endif

#ifdef __OBJC__
@interface Log : NSObject {
    // stuff
}

// Other stuff

@end
#endif // __OBJC__

Log.m:

#import "Log.h"

ObjcLog *logCreate(const char *filename) {
    // Assumes [Log initWithFilename:]
    Log *log = [[Log alloc] initWithFilename:[NSString stringWithUTF8String:filename]];
    return (ObjcLog *)log;
}

void logDestroy(ObjcLog *logObj) {
    Log *log = (Log *)logObj;
    [log release];
}

void logInfo(ObjcLog *logObj, const char *msg, ...) {
    char buffer[8192];
    va_list va;
    va_start(va, msg);
    vsprintf(buffer, msg, va);
    va_end(va);

    Log *log = (Log *)logObj;
    // Assumes [Log info:]
    [log info:[NSString stringWithUTF8String:buffer]];
}

void logError(ObjcLog *logObj, const char *msg, ...) {
    char buffer[8192];
    va_list va;
    va_start(va, msg);
    vsprintf(buffer, msg, va);
    va_end(va);

    Log *log = (Log *)context;
    // Assumes [Log error:]
    [log error:[NSString stringWithUTF8String:buffer]];
}

@implementation Log

...

@end

Your C++ code should then be able to use the Objective-C Log object like this:

ObjcLog *logObj = logCreate("/path/to/file.log");
...
logInfo(logObj, "The answer is %d.  What is the question?", 42);
...
logDestroy(logObj);

I would probably favour the creation of a C++ wrapper class for the Objective-C class, which will make it easier to manage the lifetime of the object and simplify access to it. However I will avoid adding it here as it will likely just complicate something that is already over-complicated.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • Is there any way I can init it in the .cpp file? If you could share the code for it that will be great. – Marti Markov Nov 07 '13 at 10:26
  • @MartiMarkov OK, I have updated my answer. That actually makes the implementation much neater and more obvious. Take note about not using ARC when implementing `Log`. – trojanfoe Nov 07 '13 at 10:48
  • What do you mean by that? Log is mainly static (singleton if not accessing the static methods). You mean for the ARC to not remove it and the c code to use it after it was removed? – Marti Markov Nov 07 '13 at 13:13
  • @MartiMarkov Yeah - you are controlling the lifetime of the object from C++ so you want to disable ARC for that class. – trojanfoe Nov 07 '13 at 16:29
  • Ummm.. How do you disable ARC for a specific class? :D I haven't played that much with ObjC's memory management. (BTW this is why I wanted to use this implementation I learned a lot from your answers. :) ) – Marti Markov Nov 07 '13 at 16:38
  • 1
    @MartiMarkov See this SO question: http://stackoverflow.com/questions/6646052/how-can-i-disable-arc-for-a-single-file-in-a-project – trojanfoe Nov 07 '13 at 17:56