I have a class (EAGLView) which calls a member function of a C++ class without problems. Now, the problem is that I need to call in that C++ class a objective-C function [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; which I cannot do in C++ syntax.
I could wrap this Objective-C call to the same Objective-C class which in the first place called the C++ class, but then I need to somehow call that method from C++, and I cannot figure out how to do it.
I tried to give a pointer to EAGLView object to the C++ member function and include the "EAGLView.h" in my C++ class header but I got 3999 errors..
So.. how should I do this? An example would be nice.. I only found pure C examples of doing this.
You can mix C++ with Objective-C if you do it carefully. There are a few caveats but generally speaking they can be mixed. If you want to keep them separate, you can set up a standard C wrapper function that gives the Objective-C object a usable C-style interface from non-Objective-C code (pick better names for your files, I have picked these names for verbosity):
MyObject-C-Interface.h
#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__
// This is the C "trampoline" function that will be used
// to invoke a specific Objective-C method FROM C++
int MyObjectDoSomethingWith (void *myObjectInstance, void *parameter);
#endif
MyObject.h
#import "MyObject-C-Interface.h"
// An Objective-C class that needs to be accessed from C++
#interface MyObject : NSObject
{
int someVar;
}
// The Objective-C member function you want to call from C++
- (int) doSomethingWith:(void *) aParameter;
#end
MyObject.mm
#import "MyObject.h"
#implementation MyObject
// C "trampoline" function to invoke Objective-C method
int MyObjectDoSomethingWith (void *self, void *aParameter)
{
// Call the Objective-C method using Objective-C syntax
return [(id) self doSomethingWith:aParameter];
}
- (int) doSomethingWith:(void *) aParameter
{
// The Objective-C function you wanted to call from C++.
// do work here..
return 21 ; // half of 42
}
#end
MyCPPClass.cpp
#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"
int MyCPPClass::someMethod (void *objectiveCObject, void *aParameter)
{
// To invoke an Objective-C method from C++, use
// the C trampoline function
return MyObjectDoSomethingWith (objectiveCObject, aParameter);
}
The wrapper function does not need to be in the same .m file as the Objective-C class, but the file that it does exist in needs to be compiled as Objective-C code. The header that declares the wrapper function needs to be included in both CPP and Objective-C code.
(NOTE: if the Objective-C implementation file is given the extension ".m" it will not link under Xcode. The ".mm" extension tells Xcode to expect a combination of Objective-C and C++, i.e., Objective-C++.)
You can implement the above in an Object-Orientented manner by using the PIMPL idiom. The implementation is only slightly different. In short, you place the wrapper functions (declared in "MyObject-C-Interface.h") inside a class with a (private) void pointer to an instance of MyClass.
MyObject-C-Interface.h (PIMPL)
#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__
class MyClassImpl
{
public:
MyClassImpl ( void );
~MyClassImpl( void );
void init( void );
int doSomethingWith( void * aParameter );
void logMyMessage( char * aCStr );
private:
void * self;
};
#endif
Notice the wrapper methods no longer require the void pointer to an instance of MyClass; it is now a private member of MyClassImpl. The init method is used to instantiate a MyClass instance;
MyObject.h (PIMPL)
#import "MyObject-C-Interface.h"
#interface MyObject : NSObject
{
int someVar;
}
- (int) doSomethingWith:(void *) aParameter;
- (void) logMyMessage:(char *) aCStr;
#end
MyObject.mm (PIMPL)
#import "MyObject.h"
#implementation MyObject
MyClassImpl::MyClassImpl( void )
: self( NULL )
{ }
MyClassImpl::~MyClassImpl( void )
{
[(id)self dealloc];
}
void MyClassImpl::init( void )
{
self = [[MyObject alloc] init];
}
int MyClassImpl::doSomethingWith( void *aParameter )
{
return [(id)self doSomethingWith:aParameter];
}
void MyClassImpl::logMyMessage( char *aCStr )
{
[(id)self doLogMessage:aCStr];
}
- (int) doSomethingWith:(void *) aParameter
{
int result;
// ... some code to calculate the result
return result;
}
- (void) logMyMessage:(char *) aCStr
{
NSLog( aCStr );
}
#end
Notice that MyClass is instantiated with a call to MyClassImpl::init. You could instantiate MyClass in MyClassImpl's constructor, but that generally isn't a good idea. The MyClass instance is destructed from MyClassImpl's destructor. As with the C-style implementation, the wrapper methods simply defer to the respective methods of MyClass.
MyCPPClass.h (PIMPL)
#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__
class MyClassImpl;
class MyCPPClass
{
enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
MyCPPClass ( void );
~MyCPPClass( void );
void init( void );
void doSomethingWithMyClass( void );
private:
MyClassImpl * _impl;
int _myValue;
};
#endif
MyCPPClass.cpp (PIMPL)
#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"
MyCPPClass::MyCPPClass( void )
: _impl ( NULL )
{ }
void MyCPPClass::init( void )
{
_impl = new MyClassImpl();
}
MyCPPClass::~MyCPPClass( void )
{
if ( _impl ) { delete _impl; _impl = NULL; }
}
void MyCPPClass::doSomethingWithMyClass( void )
{
int result = _impl->doSomethingWith( _myValue );
if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
{
_impl->logMyMessage( "Hello, Arthur!" );
}
else
{
_impl->logMyMessage( "Don't worry." );
}
}
You now access calls to MyClass through a private implementation of MyClassImpl. This approach can be advantageous if you were developing a portable application; you could simply swap out the implementation of MyClass with one specific to the other platform ... but honestly, whether this is a better implementation is more a matter of taste and needs.
You can compile your code as Objective-C++ - the simplest way is to rename your .cpp as .mm. It will then compile properly if you include EAGLView.h (you were getting so many errors because the C++ compiler didn't understand any of the Objective-C specific keywords), and you can (for the most part) mix Objective-C and C++ however you like.
The easiest solution is to simply tell Xcode to compile everything as Objective C++.
Set your project or target settings for Compile Sources As to Objective C++ and recompile.
Then you can use C++ or Objective C everywhere, for example:
void CPPObject::Function( ObjectiveCObject* context, NSView* view )
{
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)view.layer]
}
This has the same affect as renaming all your source files from .cpp or .m to .mm.
There are two minor downsides to this: clang cannot analyse C++ source code; some relatively weird C code does not compile under C++.
Step 1
Create a objective c file(.m file) and it's corresponding header file.
// Header file (We call it "ObjCFunc.h")
#ifndef test2_ObjCFunc_h
#define test2_ObjCFunc_h
#interface myClass :NSObject
-(void)hello:(int)num1;
#end
#endif
// Corresponding Objective C file(We call it "ObjCFunc.m")
#import <Foundation/Foundation.h>
#include "ObjCFunc.h"
#implementation myClass
//Your objective c code here....
-(void)hello:(int)num1
{
NSLog(#"Hello!!!!!!");
}
#end
Step 2
Now we will implement a c++ function to call the objective c function that we just created!
So for that we will define a .mm file and its corresponding header file(".mm" file is to be used here because we will be able to use both Objective C and C++ coding in the file)
//Header file(We call it "ObjCCall.h")
#ifndef __test2__ObjCCall__
#define __test2__ObjCCall__
#include <stdio.h>
class ObjCCall
{
public:
static void objectiveC_Call(); //We define a static method to call the function directly using the class_name
};
#endif /* defined(__test2__ObjCCall__) */
//Corresponding Objective C++ file(We call it "ObjCCall.mm")
#include "ObjCCall.h"
#include "ObjCFunc.h"
void ObjCCall::objectiveC_Call()
{
//Objective C code calling.....
myClass *obj=[[myClass alloc]init]; //Allocating the new object for the objective C class we created
[obj hello:(100)]; //Calling the function we defined
}
Step 3
Calling the c++ function(which actually calls the objective c method)
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "ObjCCall.h"
class HelloWorld : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// a selector callback
void menuCloseCallback(cocos2d::Ref* pSender);
void ObCCall(); //definition
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__
//Final call
#include "HelloWorldScene.h"
#include "ObjCCall.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
origin.y + closeItem->getContentSize().height/2));
// create menu, it's an autorelease object
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
// position the label on the center of the screen
label->setPosition(Vec2(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - label- >getContentSize().height));
// add the label as a child to this layer
this->addChild(label, 1);
// add "HelloWorld" splash screen"
auto sprite = Sprite::create("HelloWorld.png");
// position the sprite on the center of the screen
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(sprite, 0);
this->ObCCall(); //first call
return true;
}
void HelloWorld::ObCCall() //Definition
{
ObjCCall::objectiveC_Call(); //Final Call
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
return;
#endif
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
Hope this works!
You need to make your C++ file be treated as Objective-C++. You can do this in xcode by renaming foo.cpp to foo.mm (.mm is the obj-c++ extension). Then as others have said standard obj-c messaging syntax will work.
Sometimes renaming .cpp to .mm is not good idea, especially when project is crossplatform. In this case for xcode project I open xcode project file throught TextEdit, found string which contents interest file, it should be like:
/* OnlineManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OnlineManager.cpp; sourceTree = "<group>"; };
and then change file type from sourcecode.cpp.cpp to sourcecode.cpp.objcpp
/* OnlineManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = **sourcecode.cpp.objcpp**; path = OnlineManager.cpp; sourceTree = "<group>"; };
It is equivalent to rename .cpp to .mm
Also, you can call into Objective-C runtime to call the method.
#DawidDrozd's answer above is excellent.
I would add one point. Recent versions of the Clang compiler complain about requiring a "bridging cast" if attempting to use his code.
This seems reasonable: using a trampoline creates a potential bug: since Objective-C classes are reference counted, if we pass their address around as a void *, we risk having a hanging pointer if the class is garbage collected up while the callback is still active.
Solution 1) Cocoa provides CFBridgingRetain and CFBridgingRelease macro functions which presumably add and subtract one from the reference count of the Objective-C object. We should therefore be careful with multiple callbacks, to release the same number of times as we retain.
// C++ Module
#include <functional>
void cppFnRequiringCallback(std::function<void(void)> callback) {
callback();
}
//Objective-C Module
#import "CppFnRequiringCallback.h"
#interface MyObj : NSObject
- (void) callCppFunction;
- (void) myCallbackFn;
#end
void cppTrampoline(const void *caller) {
id callerObjC = CFBridgingRelease(caller);
[callerObjC myCallbackFn];
}
#implementation MyObj
- (void) callCppFunction {
auto callback = [self]() {
const void *caller = CFBridgingRetain(self);
cppTrampoline(caller);
};
cppFnRequiringCallback(callback);
}
- (void) myCallbackFn {
NSLog(#"Received callback.");
}
#end
Solution 2) The alternative is to use the equivalent of a weak reference (ie. no change to the retain count), without any additional safety.
The Objective-C language provides the __bridge cast qualifier to do this (CFBridgingRetain and CFBridgingRelease seem to be thin Cocoa wrappers over the Objective-C language constructs __bridge_retained and release respectively, but Cocoa does not appear to have an equivalent for __bridge).
The required changes are:
void cppTrampoline(void *caller) {
id callerObjC = (__bridge id)caller;
[callerObjC myCallbackFn];
}
- (void) callCppFunction {
auto callback = [self]() {
void *caller = (__bridge void *)self;
cppTrampoline(caller);
};
cppFunctionRequiringCallback(callback);
}
You can mix C++ in with Objectiv-C (Objective C++). Write a C++ method in your Objective C++ class that simply calls [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; and call it from your C++.
I haven't tried it before my self, but give it a shot, and share the results with us.
Related
Is it possible to use [[NSProcessInfo processInfo] operatingSystemVersion] from C++ or C and how do I do that?
In XCode and Objective-C, files with extension .mm are treated as "mixed" in the sense that one single file can contain C++/C notion as well as Objective-C code, and that even C++/C function implementations can access Objective-C classes and send messages to them.
Such a .mm-files can then provide a trampoline function, i.e. a C-style function that is exposed and can then be used in pure C++/C-files (extension .cpp, where Objective-C code must not occur).
Let's consider the following setting:
A mixed file ProcessInfoProvider.mm, which calls [[NSProcessInfo processInfo] operatingSystemVersion] and provides the result in a C-style function std::string getProcessInfoVersion_C(),
A Pure cpp-file and a corresponding hpp-file UseProcessInfoInC.cpp/hpp defining a class MyCppProcessInfo, which provides a printVersion()-member function and which uses getProcessInfoVersion_C,
Some other file that instantiates MyCppProcessInfo and calls member function printVersion()
Let's start with ProcessInfoProvider.mm:
#import <Foundation/Foundation.h>
#import <Foundation/NSProcessInfo.h>
#include <string.h>
#include <iostream>
std::string getProcessInfoVersion_C (void) {
NSProcessInfo *processInfo = [[NSProcessInfo alloc] init];
// check avaiability of the property operatingSystemVersion (10.10+) at runtime
if ([processInfo respondsToSelector:#selector(operatingSystemVersion)])
{
NSOperatingSystemVersion versionObj = [processInfo operatingSystemVersion];
char version[500];
snprintf(version, 500, "Version %ld.%ld.%ld",
(long)versionObj.majorVersion,
(long)versionObj.minorVersion,
(long)versionObj.patchVersion);
return version;
}
else
{
// Implement fallback for OSX 10.9 and below
return "?";
}
}
Note that getProcessInfoVersion_C is a C-style function but contains objective-C code in it's implementation.
Then, pure C++ files UseProcessInfoInC.cpp/hpp implement class MyCppProcessInfo:
// UseProcessInfoInC.hpp:
#ifndef UseProcessInfoInC_hpp
#define UseProcessInfoInC_hpp
class MyCppProcessInfo {
public:
void printVersion(void);
};
#endif /* UseProcessInfoInC_hpp */
and
// UseProcessInfoInC.cpp:
#include "UseProcessInfoInC.hpp"
#include <iostream>
extern std::string getProcessInfoVersion_C (void);
void MyCppProcessInfo::printVersion(void)
{
std::string version = getProcessInfoVersion_C();
std::cout << version;
}
Note that these two files do not contain any Objective-C-stuff.
Finally, let's try out MyCppProcessInfo, e.g. in a function main():
// File main.mm
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#include "UseProcessInfoInC.hpp"
int main(int argc, char * argv[]) {
#autoreleasepool {
MyCppProcessInfo pi;
pi.printVersion();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
I am trying to wrap a c++ library with objective-c++ so i can use it in and iOS application. The library is not finished, only the specifications and a draft of the header file that I will be using.
The api has a function
Result initialize(const char* oemUser, const char* oemPassword, Callback* pCallback);
where pCallback will be called with different states and progress.
Callback is a pure virtual Class.
I have been following some guides to wrap the code, and I think I understand how to properly wrap it so I can call the C++ functions from Objective-C++ but not the other way around.
Some code:
foo.hpp (c++ api-headers)
#ifndef FOO_API_H
#define FOO_API_H
namespace foo {
namespace bar {
enum struct Result
{
Ok,
Error,
};
enum struct State
{
Uninitialized,
Done,
kError,
};
class Callback
{
public:
virtual ~Callback() {}
virtual Result enumerateEndpoints(int index,
char* info,
unsigned length) const = 0;
virtual Result processState(State state,
float progress,
bool reqNext) = 0;
};
/** Initialize - initializes the FOO library
\param oemUser OEM user for validating library
\param oemPassword OEM password for validating library
\param pCallback Pointer to an object implementing Callback
interface
\return Result
*/
Result initialize(const char* oemUser,
const char* oemPassword,
Callback* pCallback);
Result terminate();
State getCurrentState();
Result getLastError(char* buffer, unsigned length);
}
}
#endif
FooApiControlWrapper.h (My main api wrapper i obj-c++)
#import <Foundation/Foundation.h>
#include "FooApiCallbackWrapper.h"
#include "foo_api.hpp"
#interface FooApiControlWrapper : NSObject
- (foo::bar::Result)initialize:(NSString*)oemUser with:(NSString*)oemPassword using:(foo::bar::Callback*)callback;
- (foo::bar::Result)terminate;
- (foo::bar::State)getCurrentState;
- (foo::bar::Result)getLastError:(NSString*)buffer lengt:(NSInteger*)length;
#end
FooApiControlWrapper.mm
Here you can see that I am providing foo::bar::Callback to the obj-c++ init parameter, but somehow I think this should be an Obj-c++ Object.
#import "FooApiControlWrapper.h"
#include "foo_api.hpp"
#implementation FooApiControlWrapper
- (foo::bar::Result)initialize:(NSString*)oemUser with:(NSString*)oemPassword using:(foo::bar::Callback*)callback {
return foo::bar::initialize([oemUser UTF8String], [oemPassword UTF8String], callback);
}
- (foo::bar::Result)terminate {
return foo::bar::Result::Ok;
}
- (foo::bar::State)getCurrentState {
return foo::bar::State::Uninitialized;
}
- (foo::bar::Result)getLastError:(NSString*)buffer lengt:(NSInteger*)length {
return foo::bar::Result::Ok;
}
#end
FooApiCallbackWrapper.h (My wrapper for the callback class)
#import <Foundation/Foundation.h>
#include "foo_api.hpp"
#interface FooApiCallbackWrapper : NSObject
- (instancetype) init;
- (foo::bar::Result)enumerateEndpoints:(NSInteger*)index with:(NSString*)info and:(NSInteger*)length;
- (foo::bar::Result)processState:(foo::bar::State)state progress:(float)progress reqNext:(Boolean)reqNext;
#end
FooApiCallbackWrapper.mm
#import "FooApiCallbackWrapper.h"
#include "foo_api.hpp"
#implementation FooApiCallbackWrapper
- (instancetype) init{
self = [super init];
return self;
}
- (void)dealloc{
}
- (foo::bar::Result)enumerateEndpoints:(NSInteger*)index with:(NSString*)info and:(NSInteger*)length {
return foo::bar::Result::Ok;
}
- (foo::bar::Result)processState:(foo::bar::State)state progress:(float)progress reqNext:(Boolean)reqNext {
return foo::bar::Result::Ok;
}
#end
I just want to be able to write a callback in Obj-C++ that later will be called by my C++-Api.
Where and how to proceed?
Remember that C++ is allowed in Objective-C++, so the standard way is to just add a plain C or C++ function in your Objective-C++ part (outside of the library), and let that function - which is compiled as Objective-C++, operate with the Obj-C++ stuff, i.e. perform methods on Obj-C++-Objects. If you need to pass References to Objects, some void * will usually do. Alternatively, some singleton / look-up pattern can be used.
I am writing a device driver for a Blackmagic Design AV device in XCode, and I'm having trouble including BMD's SyncController class from their abbreviated sample code (below) into my purely Objective-C project.
Their DecklinkAPI.h file is rich in C++ code, so when I try include this header file as-is in a an Objective-C class, the compiler chokes deep in the API include: Unknown type name 'class'; did you mean 'Class'?
I have tried to to bundle up the C++ bits into a Obj-C class extension as noted here, but without much success. I've never done any C++ programming (and have never used Obj-C class extensions), so this is new territory for me.
I'm not sure if I need to create an additional wrapper class for my SyncController object, or whether I can just do a class extension on this one and shuffle the C++ bits into the .mm file.
I would like to be able to do a #include "SyncController.h" (or its wrapper) in an Objective-C class without having the compiler choke.
Any assistance in doing so would be much appreciated.
First up, here is my current SyncController.h file:
#import <Cocoa/Cocoa.h>
#import "DeckLinkAPI.h" // this is rich in C++ code
class PlaybackDelegate;
#interface SyncController : NSObject {
PlaybackDelegate* playerDelegate;
IDeckLink* deckLink;
IDeckLinkOutput* deckLinkOutput;
}
- (void)scheduleNextFrame:(BOOL)prerolling;
- (void)writeNextAudioSamples;
#end
class PlaybackDelegate : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback
{
SyncController* mController;
IDeckLinkOutput* mDeckLinkOutput;
public:
PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput);
// IUnknown needs only a dummy implementation
virtual HRESULT QueryInterface (REFIID iid, LPVOID *ppv) {return E_NOINTERFACE;}
virtual ULONG AddRef () {return 1;}
virtual ULONG Release () {return 1;}
virtual HRESULT ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result);
virtual HRESULT ScheduledPlaybackHasStopped ();
virtual HRESULT RenderAudioSamples (bool preroll);
};
void ScheduleNextVideoFrame (void);
Next up, here is my (simplified) SyncController.mm file:
#import <CoreFoundation/CFString.h>
#import "SyncController.h"
#implementation SyncController
- (instancetype)init
{
self = [super init];
return self;
}
- (void)dealloc
{
}
- (void)scheduleNextFrame:(BOOL)prerolling
{
}
- (void)writeNextAudioSamples
{
}
#end
PlaybackDelegate::PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput)
{
mController = owner;
mDeckLinkOutput = deckLinkOutput;
}
HRESULT PlaybackDelegate::ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result)
{
[mController scheduleNextFrame:NO];
return S_OK;
}
HRESULT PlaybackDelegate::ScheduledPlaybackHasStopped ()
{
return S_OK;
}
HRESULT PlaybackDelegate::RenderAudioSamples (bool preroll)
{
[mController writeNextAudioSamples];
if (preroll)
mDeckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
return S_OK;
}
I have tried to to bundle up the C++ bits into a Obj-C class extension as noted here, but without much success.
If you're targeting 64-bit, the class extension method should be fairly simple.
The following is equivalent to the code you've post, but moves all of the C++ declarations to a separate header:
SyncController.h:
#import <Cocoa/Cocoa.h>
#interface SyncController : NSObject
- (void)scheduleNextFrame:(BOOL)prerolling;
- (void)writeNextAudioSamples;
#end
SyncController_CPP.h
#import "SyncController.h"
#include "DeckLinkAPI.h"
class PlaybackDelegate;
#interface SyncController() {
PlaybackDelegate* playerDelegate;
IDeckLink* deckLink;
IDeckLinkOutput* deckLinkOutput;
}
#end
class PlaybackDelegate ...
{
...
}
SyncController.mm
#import "SyncController_CPP.h"
#implementation SyncController
...
#end
PlaybackDelegate::PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput)
{
mController = owner;
mDeckLinkOutput = deckLinkOutput;
}
// etc..
Any other ObjC classes that need access to SyncController will import "SyncController.h". Any other ObjC++ classes can import either "SyncController.h" or "SyncController_CPP.h"
Not a complete answer, however errors like:
Unknown type name 'class'; did you mean 'Class'?
Is a classic issue with Objective-C++ where an Objective-C implementation file is seeing a C++ header file.
I can only provide advice about how to avoid it as you didn't post the complete build output.
Don't put C++ headers is the pre-compiled header.
Try to only include C++ headers within Objective-C++ implementation files and not in their counterpart header file which might, in turn, be included into an Objective-C file.
Hide the use of C++ from any header files, for example using private instance variables:
#import <vector>
#implementation MyObjCppClass {
std::vector<int> _stuff;
}
- (id)init {
...
}
#end
If you are mixing Objective-C and Objective-C++ then you might find you need to provide Objective-C wrappers to C++ classes (which look from the outside as Objective-C but are actually implemented in Objective-C++).
Rename your .m files (objective-c) to .mm (objective-c++). this should allow you to then mix objc and c++ by including c++ headers and referencing c++ code from your objc.
---EDIT---
Any header file you include from objective-c must contain only objective-c. Remove any c++ from the header in your wrapper class to get the other objc classes to build. In modern objc, you can split your ivars between the .h and .m files; keep all your methods in the .h for other objc classes to use, and declare your c++ ivars in the .mm. Stick your c++ delegate class in its own .h that is only included from the .mm wrapper.
Use #if __cplusplus.
For example,
#import <Cocoa/Cocoa.h>
#if __cplusplus
#import "DeckLinkAPI.h" // this is rich in C++ code
#endif // __cplusplus
#interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
#end
#if __cplusplus
class PlaybackDelegate : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback
{
...
};
#endif // __cplusplus
The header file can be used with Objective-C and Objective-C++. But you can not use C++ class signature in SyncController Objective-C class declaration in the header. Use void * instead of PlaybackDelegate * with proper type cast.
Also using void * means that C++ stuff in the header is no longer needed.
#import <Cocoa/Cocoa.h>
#interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
#end
In Objective-C++ code,
// initialize
syncController.playerDelegate = new PlaybackDelegate();
// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate;
initialize
syncController.playerDelegate = new PlaybackDelegate();
// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate
I have a few questions about DLL's. I tried a lot but I can not get the complete picture. Most examples are in C# etc.
With the wizard in VS2005 I created a unmanaged MFC regular DLL (must be MFC because of remaining code). Then I tried to import it in a VS2005 managed .NET C++ application. See code below.
mfc_main.h:
//---------------------------------------------------------
// mfc_main.h : main header file for the mfc_main DLL
//---------------------------------------------------------
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
class __declspec(dllexport) Cmfc_mainApp : public CWinApp
{
public:
Cmfc_mainApp();
// Overrides
public:
virtual BOOL InitInstance();
int SayHello(int j);
int init;
DECLARE_MESSAGE_MAP()
};
mfc_main.cpp:
//----------------------------------------------------------------
// mfc_main.cpp : Defines the initialization routines for the DLL.
//----------------------------------------------------------------
#include "stdafx.h"
#include "mfc_main.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
BEGIN_MESSAGE_MAP(Cmfc_mainApp, CWinApp)
END_MESSAGE_MAP()
Cmfc_mainApp::Cmfc_mainApp()
{
}
Cmfc_mainApp theApp;
BOOL Cmfc_mainApp::InitInstance()
{
CWinApp::InitInstance();
return TRUE;
}
int Cmfc_mainApp::SayHello(int j)
{
init = 12; // Comment this out the application works !!!!
return j * 6;
};
in application
[DllImport("mfc_main.dll",
EntryPoint = "?SayHello#Cmfc_mainApp##QAEHH#Z",
ExactSpelling = true)]
static int SayHello(int a);
......
private: System::Void button_Click(System::Object^ sender, System::EventArgs^ e)
{
int retval = SayHello(2);
}
My questions are:
1 - Why is it working without the init = 12 in the function SayHello and with the application crashes (error: Attempted to read or write protected memory)?
2 - Is in this case the InitInstance() executed although I don't call it (and why is there no ExitInstance)?
3 - Why do I see some examples giving the EntryPoint when using DLLImport and some don't?
4 - Can I give a delegate as parameter to a function in a MFC C++ DLL instead of a normal function pointer, to create a callback?
Methods cannot be P/Invoked. If you want to export a class from unmanaged DLL to be used in managed world, you have to flatten it, eg.
Create a constructor function, which looks like:
__declspec(dllexport) void * __stdcall MyClass_Create()
{
return new MyClass();
}
Create a destructor function, which looks like:
__declspec(dllexport) void * __stdcall MyClass_Destroy(MyClass * instance)
{
delete instance;
}
Flatten method calls. Let's suppose, that you have the following method in your class:
int MyClass::MyMethod(int i, double j) { ... }
Then you have to create a following function:
__declspec(dllexport) int __stdcall MyClass_MyMethod(MyClass * instance, int i, double j)
{
return instance->MyMethod(i, j);
}
Prepare P/Invoked external methods in C# (You already know how to do it, so I'll omit these)
Create instance of your class:
IntPtr instance = MyClass_Create();
Then call its method:
int i = MyClass_MyMethod(instance, 4, 2.0);
Finally, destroy the class:
MyClass_Destroy(instance);
Don't forget to add some error checking - I omitted it to keep the example clear.
i found a great post Calling Objective-C method from C++ method?
however, i have to 2 questions, which makes me overcoming this issue very problematic :
note that i have made a change to MyObject-C-Interface.h
#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__ 1
struct CWrap {
void * myObjectInstance;
int MyObjectDoSomethingWith (void *parameter) {
//how to call the
//myObjectInstance->MyObjectDoSomethingWith ???
}
}
#endif
1) how can i call a method on a void * myObjectInstance…
i mean, i cannot cast to MyObject or otherwise let the compiler know, what method to call:(
2) how do i actually use it?
in my, lets say code.m that has both the
#import "MyObject.h"
//and
#include "MyObject-C-Interface.h"
i would do?
MyObject tmp = [[MyObject alloc] init];
CWrap _cWrap;
_cWrap.myObjectInstance = (void *)tmp;
//pass the CWrap to a deep C++ nest
myMainCPPCode->addObjectiveCToCWrap(_cWrap);
and than in my deep C++ code simply call
_cWrap.MyObjectDoSomethingWith((void *)somePrameter)
iam missing a link, and thats, how to link the C wraper function to a ObjectiveC function :(
thank you
i mean, i cannot cast to MyObject or otherwise let the compiler know, what method to call
Why not?
Your C++ wrapper class must be in an Objective-C++ file (usually denoted with the .mm) extension. Then, because you even have to declare your private instance variables in the class declaration, you need a private member to hold the Objective-C pointer in the class header. This must be something that a pure C++ compiler can understand so one thing you could do is this:
#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__ 1
#if defined __OBJC__
typedef id MyObjCClass;
#else
typedef void* MyObjCClass;
#endif
struct CWrap {
MyObjCClass myObjectInstance;
int MyObjectDoSomethingWith (void *parameter);
}
Then in the .mm file
#import "ObjCClassHeader.h"
int CWrap::MyObjectDoSomethingWith(void *parameter)
{
return [myObjectInstance doSomethingWith: parameter];
}
__OBJC__ is a predefined macro that is defined when the compiler is compiling Objective-C or Objective-C++