Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle objective-c delegation from C++?

Short version: I have a Qt/C++ to which I am having to add a limited amount of Cocoa/Objective-C code. I have changed the .cpp file to a .mm file and added the objective-c code/objects to said file, and it compiles and works. I now need a delegate for one of the objects I created- a NSPopUpButton (or, rather, the menu thereof) to be exact - and I'm stuck. How can I add a delegate for this object?

Details: Files in question:

reportwindow.h, reportwindow.cpp RENAMED TO reportwindow.mm -These are the files containing my original C++ implementation plus some objective-c code (open a NSSavePanel containing a NSPopUpButton). reportwindow.h is additionally included in a .cpp file, if that makes a difference.

menuHandler.h, menuHandler.mm -these files contain a (currently empty) objective-c class that I was intending to use as a delegate

My first thought was that I could simply make the C++ class the delegate, but this obviously doesn't work as straight C++ doesn't understand delegation. I then thought I'd make a separate objective-c class as a NSMenuDelegate and add an instance of it as a member object to my C++ class. As I have been able to add other objective-c objects as members, I figured this should work. However, as soon as I included the header for my new objective-c class in the C++ class header file, I got several hundred errors about "expected unqualified-id before '@' token" -from the apple header files (NSValue.h, NSObject.h, etc) So apparently that didn't work, at least not as-is. I get the same result when including ANY cocoa header in my class header file.

I then thought I'd try a forward-declaration of the objective-c class (that is how I got the other objective-c objects working). however, this didn't work either- if I declare it as "class myClassName" I get an error about re-defining the class as a different type of symbol (presumably c++ class vs objective-c protocol). If I try to forward declare it as @protocol myClassName, I get an error about "expected unqualified-id before '@' token". So how can I make this work?

like image 932
ibrewster Avatar asked Oct 25 '11 16:10

ibrewster


People also ask

What is the difference between delegate and datasource?

A data source is like a delegate except that, instead of being delegated control of the user interface, it is delegated control of data. A data source is an outlet held by NSView and UIView objects such as table views and outline views that require a source from which to populate their rows of visible data.

What is delegation in C?

C# delegates are similar to pointers to functions, in C or C++. A delegate is a reference type variable that holds the reference to a method. The reference can be changed at runtime. Delegates are especially used for implementing events and the call-back methods. All delegates are implicitly derived from the System.

What is the difference between delegate and protocol in Swift?

Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source. Apple notes that: A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.


2 Answers

Ok to answer your question:

reportwindow.h is additionally included in a .cpp file, if that makes a difference.

It does make a difference. Any compilation unit (cpp file in this case) that is touching Objective-C code has to be renamed to .mm or .m. Including the header that in turn is including Objective-C stuff in a C++ file will lead to the problem that the C++ compiler sees Objective-C code which it cannot handle.

Renaming the cpp file to mm will select the Objective-C option during compilation (which isn't when the file is named cpp or c) and hence allow to compile stuff with the Objective-C tokens (mainly "@" in your case).

An alternative would be not to include the Objective-C delegate class to your C++ class but rather include a pointer to your C++ class within the Objective-C delegate (i.e. implement it the other way around). This way you could arrange things such that the Objective-C code isn't touching the C++ code.

Edit: Actually, I'd prefer the second suggestion. Here is an example:

DelegateClass.h:

class MyCPPClassHandlingStuff;

@interface MyDelegateObject : NSObject <SomeDelegateProtocol> {
  MyCPPClassHandlingStuff *m_handlerInstance;
}

- (id) initWithCPPInstance:(MyCPPClassHandlingStuff*)cppInstance;

- (void) theDelegateProtocolMethod;

@end

DelegateClass.mm

#include "MyCPPClassHandlingStuff.h"

@implementation MyDelegateObject

- (id) initWithCPPInstance:(MyCPPClassHandlingStuff*)cppInstance
{
  self = [super init];
  if (self) {
    m_handlerInstance = cppInstance;
  }
  return self;
}

- (void) theDelegateProtocolMethod
{
  if (m_handlerInstance)
    m_handlerInstance->handleDelegateMethod();
}

@end

And well the MyCPPClassHandlingStuff.h:

#ifndef __MyCPPClassHandlingStuff_H__
#define __MyCPPClassHandlingStuff_H__

class MyCPPClassHandlingStuff
{
public:
  MyCPPClassHandlingStuff();
  void handleDelegateMethod();
};

#endif /* __MyCPPClassHandlingStuff_H__ */

MyCPPClassHandlingStuff can be initialized from Objective-C but you cannot initialise any Objective-C class from C++ code there. If you need to use Objective-C in your C++ code, you would have to compile it as Objective-C (i.e. use an .mm file). I leave the .cpp details as an exercise for the reader ;)

like image 153
cli_hlt Avatar answered Oct 20 '22 00:10

cli_hlt


Make an Objective-C class to satisfy the delegate protocol, and have it delegate to your C++ class. The problem is that the AppKit expects to talk to Objective-C objects, so you need a shim to delegate to your C++ objects. (Yes you can do some runtime abuse, defining an isa pointer in your C++ class to make it somewhat compatible with ObjC objects (to the point where you can send messages to C++ classes), but don't do that. It's nasty.)

So make a shim which is initialized with your C++ class, has that as an ivar. It should implement the delegate methods you're interested in, and pass those along to your C++ class in a manner that it can understand. Tada, done.

like image 20
jer Avatar answered Oct 19 '22 22:10

jer