7

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?

ibrewster
  • 3,482
  • 5
  • 42
  • 54
  • Have you renamed that specific cpp file to mm? – cli_hlt Oct 25 '11 at 16:26
  • The one associated with the header in question, yes. – ibrewster Oct 25 '11 at 16:33
  • The second way is the way to go, and probably the only one. You just shouldn't surrender after the first failed try. Provide more details, what includes what, there may be just more .cpp files that include `reportwindow.h`. – hamstergene Oct 25 '11 at 16:50
  • Well, I didn't exactly surrender after the FIRST failed try- I just didn't include all the gory details of my struggle :-) – ibrewster Oct 25 '11 at 17:20

4 Answers4

11

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

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
cli_hlt
  • 7,072
  • 2
  • 26
  • 22
  • 1
    Could you please give idea of .cpp implementation too :P How will you initialize MyDelegateObject from cpp. – Aalok Jan 09 '15 at 09:17
1

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.

jer
  • 20,094
  • 5
  • 45
  • 69
0

There is some useful information on how to set up your headers so they can be used both from ObjC and C++ in Can I separate C++ main function and classes from Objective-C and/or C routines at compile and link?

Community
  • 1
  • 1
uliwitness
  • 8,532
  • 36
  • 58
0

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

Why not forward-declare it as @class myClassName?

Luke
  • 7,110
  • 6
  • 45
  • 74
  • I tried that, it gave errors about "expected unqualified-id before '@' symbol". Turns out Qt was generating a moc_.cpp file that was throwing things off. Renaming that to .mm fixed the issue. – ibrewster Oct 25 '11 at 17:16