QT run objective-c code - objective-c

I'm trying to run native object-c code on my Mac application.
My code looks like:
MainWindow.h:
#ifdef Q_OS_MAC
#include <Carbon/Carbon.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <mach/mach_port.h>
#include <mach/mach_interface.h>
#include <mach/mach_init.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
#endif
MainWindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
#ifdef Q_OS_MAC
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: #selector(receiveSleepNote:)
name: NSWorkspaceWillSleepNotification object: NULL];
#endif
}
#ifdef Q_OS_MAC
- (void) receiveSleepNote: (NSNotification*) note
{
NSLog(#"receiveSleepNote: %#", [note name]);
}
#endif
But am getting errors that seems that QT does not understand the code structure:
application.cpp: error: expected external declaration
- (void) receiveSleepNote: (NSNotification*) note ^

In order to compile objective-c with C++, you need to have the objective-c code in a .m or .mm file.
The accompanying header can then contain functions that can be called from C++ and the body of those functions can contain objective-c code.
So let's say, for example, we wanted to call a function to pop up an OSX notification. Start with the header: -
#ifndef __MyNotification_h_
#define __MyNotification_h_
#include <QString>
class MyNotification
{
public:
static void Display(const QString& title, const QString& text);
};
#endif
As you can see, this is a regular function in a header that can be called from C++. Here's the implementation:-
#include "mynotification.h"
#import <Foundation/NSUserNotification.h>
#import <Foundation/NSString.h>
void MyNotification::Display(const QString& title, const QString& text)
{
NSString* titleStr = [[NSString alloc] initWithUTF8String:title.toUtf8().data()];
NSString* textStr = [[NSString alloc] initWithUTF8String:text.toUtf8().data()];
NSUserNotification* userNotification = [[[NSUserNotification alloc] init] autorelease];
userNotification.title = titleStr;
userNotification.informativeText = textStr;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
}
The implementation contains objective-c and due to its .mm file extension, the compiler will handle this correctly.
Note that in the example you provide in the question, you need to think about what the code is doing; especially when using 'self', as I expect that would need to refer to an Objective-C class, not a C++ class.

Related

How to disable XCode compiler defines "-DXXX" in the Objective-C source code files

Tried #undef with NS_BLOCK_ASSERTIONS, but it looks like the Release default option from settings is welded brutally into the build process. The trick is to use only .m source code file directives (maybe pragmas) to disable it, because I'm in a situation where I have no control on XCode project settings and they are set as default for me in Release builds.
#undef NS_BLOCK_ASSERTIONS
#import <Foundation/Foundation.h>
#include <stdio.h>
#interface LoggingAssertionHandler : NSAssertionHandler
#end
#implementation LoggingAssertionHandler
- (void)handleFailureInMethod:(SEL)selector
object:(id)object
file:(NSString *)fileName
lineNumber:(NSInteger)line
description:(NSString *)format, ...
{
NSString *failureMessageDescription = [NSString stringWithFormat:#"NSAssert Failure: Method %# for object %# in %##%li. Reason: \"%#\"", NSStringFromSelector(selector), object, fileName, (long)line, format];
printf("%s\n", [failureMessageDescription UTF8String]);
}
- (void)handleFailureInFunction:(NSString *)functionName
file:(NSString *)fileName
lineNumber:(NSInteger)line
description:(NSString *)format, ...
{
NSString *failureMessageDescription = [NSString stringWithFormat:#"NSCAssert Failure: Function (%#) in %##%li. Reason: \"%#\"", functionName, fileName, (long)line, format];
printf("%s\n", [failureMessageDescription UTF8String]);
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSAssertionHandler *assertionHandler = [[LoggingAssertionHandler alloc] init];
[[[NSThread currentThread] threadDictionary] setValue:assertionHandler forKey:NSAssertionHandlerKey];
NSCAssert(true == false, #"Impossible.");
NSLog(#"Hello, World!");
}
return 0;
}
The problem is that the macros are part of the system files added at project creation, which are precompiled (pre-preprocessed). Your #undef is compiled later, therefore it cannot change the already preprocessed makros.
To change this, put the #undef in the .pch (precompiled headers) file ahead of the include of the system headers. It might look like this:
#ifdef __OBJC__
#undef NS_BLOCK_ASSERTIONS
#import <Cocoa/Cocoa.h>
#endif

OSX command line app linker error

I have created an OSX command app in Xcode 5
Here is the main.m
#import <Foundation/Foundation.h>
#import "ConnectionListener.h"
#import "SOMatrix.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSLog(#"Hello, World!");
print_m();
}
return 0;
}
and here is my header file:
#ifndef __GDC1__SOMatrix__
#define __GDC1__SOMatrix__
#ifdef __cplus
#include <iostream>
#endif
int print_m();
#endif /* defined(__GDC1__SOMatrix__) */
And here is a partial listing of the SOMatrix.mm file
#include "SOMatrix.h"
#include <iostream>
using namespace std;
int print_m() {
// logic removed to keep it short; no compile time error
return 0;
}
When I build the project I got a linker error:
Undefined symbols for architecture x86_64:
"_print_m", referenced from:
_main in main.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 don't understand why the function is showhow changed to have a leading underscore in the name ('_print_m').
Why this error occurs? Do I need to add the .mm file explicitly to the project?
You need to change these lines:
#ifdef __cplus
#include <iostream>
#endif
to this in your .h file:
#ifdef __cplusplus
#include <iostream>
extern "C"
{
#endif
with a companion:
#ifdef __cplusplus
}
#endif
at the end of the .h file.
Because you are trying to access a C++ function from Objective-C, and C++ tends to do a bit of name mangling (adding the underscore, for example). Adding the "extern "C"" bit allows your Objective-C code to find your C function declarations. The answers to this related question might elaborate on things a bit better than I can.

How to run a simple objective-c in linux

EDITED
I have been trying to start coding in Objective-c. Its just a simple program to try getter and setter methods. Also print Hello World. THe following is my code:
#import <objc/Object.h>
#interface Car:Object{
int wheel: 5;
}
- (int)wheel;
- (void)setWheel: (int)newWheel;
#end
#include <stdio.h>
#implementation Car
- (int)wheel{
return wheel;
}
- (void)setWheel: (int)newWheel{
wheel = newWheel;
}
#end
#include <stdlib.h>
int main(void){
printf("Hello World");
}
I now get garbage
/tmp/cc3UC6jY.o: In function `__objc_gnu_init':
hello.m:(.text+0x6d): undefined reference to `__objc_exec_class'
/tmp/cc3UC6jY.o:(.data+0x1c0): undefined reference to `__objc_class_name_Object'
collect2: error: ld returned 1 exit status
I used the the command gcc -o hello hello.m -lobjc
I have spent hours googling this answer.
The following variation of your code compiled and ran for me:
#import <objc/Object.h>
#interface Car : Object {
int wheel;
}
- (int)wheel;
- (void)setWheel: (int)newWheel;
#end
#implementation Car
- init {
wheel = 5;
return self;
}
- (int)wheel {
return wheel;
}
- (void)setWheel: (int) newWheel {
wheel = newWheel;
}
#end
#include <stdio.h>
int main(void){
printf("Hello World\n");
id myCar = [[Car alloc] init];
printf("Wheel value is %d\n", [myCar wheel]);
return 0;
}

Expected identifier or '(' before #interface

First post and I am really hoping this is not a repetitive or solved question. I tried searching here and Google and while I have found similar Expected identifier or '(' errors none of the solutions work for me.
Basically I'm trying to learn Design patterns and as I used to know a bit of java I am trying to use it as an opportunity to learn objective-c so I have a java program that works and an xCode project that I get the error Expected identifier or '(' in my header file just before the #interface
this is my java solution (very simple I know):
public class Duck {
public void quack(){
System.out.print("Quack!");
}
public void swim(){
System.out.print("swimming duck!");
}
public void display(){
quack();
swim();
}
}
public class mainClass {
public static void main(String[] args){
Duck duck = new Duck();
duck.display();
}
}
and this is my objective-c version.
//duck.h
#include <CoreFoundation/CoreFoundation.h>
#interface Duck : NSObject{ //Expected identifier or '('
}
#end
// Duck.m
#include "Duck.h"
#implementation Duck
-(void)quack{
printf("Quack!");
}
-(void)swim{
printf("swimming duck!");
}
-(void)display{
[self quack];
[self swim];
}
#end
// main.c
#include <CoreFoundation/CoreFoundation.h>
#include "Duck.m"
int main(int argc, const char * argv[])
{
Duck *duck = [[Duck alloc] init];
[duck display];
return 0;
}
If any one can help I would greatly appreciate it, and again sorry if this is a duplicate post
The compiler doesn't know what NSObject is. If you look at the reference, you'll see that it's part of the Foundation framework, not CoreFoundation, so:
#import <Foundation/Foundation.h>
instead of:
#import <CoreFoundation/CoreFoundation.h>
//duck.h
//#include <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h> // or Cocoa/Cocoa.h
#interface Duck : NSObject//{ //Expected identifier or '('
//} not necessary if there are no instance fields
- (void)quack;
- (void)swim;
- (void)display;
#end
// Duck.m
//#include "Duck.h"
#import "Duck.h"
#implementation Duck
-(void)quack{
printf("Quack!");
}
-(void)swim{
printf("swimming duck!");
}
-(void)display{
[self quack];
[self swim];
}
#end
// main.c SHOULD BE ~main.m~ if using ObjC!!!
//#include <CoreFoundation/CoreFoundation.h>
//#include "Duck.m"
#import "Duck.h"
Additionally, get in to the habit of using NSString literals; #"example" for if/and when you decide to advance into Cocoa. Good luck with your studies.
It could be that you don't really need curly brackets on your empty interface:
#interface Duck : NSObject
#end
Try using import instead of include. Also, make sure that the CoreFoundation framework is actually part of your project.

Extend iTunesApplication class with Categories

I am just learning how to use ScriptingBridges. I made a method that slowly fades the volume on iTunes, and would like to make it a category so I can do the following:
iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:50 speed:1];
I made another category for NSSpeechSynthesizer that works, but I can't get this one to. I keep getting the following build error:
"_OBJC_CLASS_$_iTunesApplication", referenced from:
l_OBJC_$_CATEGORY_iTunesApplication_$_iTunesApplicationAdditions in iTunesApplication.o
objc-class-ref-to-iTunesApplication in iTunesApplication.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Is there something special I can do to make it work since I can't include the symbols?
Thanks,
Ryan Pendleton
UPDATE:
I only found one solution, which is below. It involves MethodSwizzling, so I'm open to better answers, but for now it's all I have.
The solution I found was to use the Objective-C runtime API. I'm sure there's a better way to organize this, but here's how I did it:
Here are my .h and .m files for creating the category. Notice how lowerVolume is not an actual method, but a C function with the arguments id self, and SEL _CMD. You'll also notice a setupCategories function. We'll call that later.
// iTunes+Volume.h
#import <objc/runtime.h>
#import "iTunes.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed);
void setupCategories();
#interface iTunesApplication (Volume)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// iTunes+Volume.m
#import "iTunes+Volume.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed)
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
void setupCategories()
{
id object = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
Class class = [object class];
[object release];
class_addMethod(class, #selector(lowerVolume:speed:), (IMP)lowerVolume, "#:if");
}
Now that I've made the functions, I need to actually add them to the scripting bridge class using the Objective-C runtime API. I'll do this in main.m to make sure that the methods are ready to be used when the run loop starts.
// main.m
#import <Cocoa/Cocoa.h>
#import "iTunes+Volume.h"
int main(int argc, char *argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
setupCategories();
return NSApplicationMain(argc, (const char **) argv);
[pool drain];
}
Now, I can use my method wherever I want as long as I include the header files:
- (void)mute
{
iTunesApplication* iTunes = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:0 speed:1];
[iTunes release];
}
If any of this doesn't make sense, just tell me and I'll try to explain it better.
I think you need to include -framework ScriptingBridge to your gcc arguments. That got it to compile for me!
As noted above, you can't easily do a category on iTunesApplication because it doesn't exist at compile time, and also because the runtime class name is ITunesApplication (capital "I").
The best solution I've found is to do your category on the class that DOES exist, SBApplication. Here's the code I tested that works and does what the original example was trying to do:
// SBApplication+Extensions.h
#import ScriptingBridge;
#interface SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// SBApplication+Extensions.m
#import "iTunes.h"
#import "SBApplication+Extensions.h"
#implementation SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
#end
// Caller, say in AppDelegate
#import "SBApplication+Extensions.h"
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
iTunesApplication *iTunesApp =
[SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunesApp lowerVolume:4 speed:3.3f];
}