is there any way to compile objective c programs on ubuntu without the use of GNUStep? I want to use the default C standard libraries and all but with Objective-C's OOB syntax. The problem I am having now is once I have all the methods up, I need a way to call them. In Mac I would just alloc and init it but on Linux when I try to compile this, clang just gives me an error.
#include <stdio.h> // C standard IO library (for printf)
#include <stdlib.h> // C standard library
// Interface
#interface test
-(void)sayHello :(char *)message;
#end
// Implementation
#implementation test
-(void)sayHello :(char *)message {
printf("%s", message);
}
int main(int argc, char *argv[]) {
test *test = [[test alloc] init];
[test sayHello:"Hello world"];
}
You can compile objective-c with gcc, but remember to use the -lobjc switch so the compiler knows what language you're using.
You'll also need to include the following header:
#import <objc/Object.h>
...and extend Object from your interface. See the hello world example for objective-c here:
http://en.m.wikipedia.org/wiki/List_of_Hello_world_program_examples#O
Good question - it got me to dig into the matters myself, since I want to rewrite several old Python projects in a normal language (C/ObjC), so I aim both to stay away from Crap++ and avoid GNUstep overhead. Here goes my try-and-test solution:
Foo.h:
#import <objc/Object.h>
#interface Foo: Object
{
#private
int
bar;
}
+ (id) alloc;
+ (id) new;
- (id) init;
- (id) set_bar: (int)value;
- (int) get_bar;
#end
Foo.m:
#import <stdio.h>
#import <objc/runtime.h>
#import "Foo.h"
#implementation Foo
+ (id) alloc
{
puts(__func__);
return class_createInstance(self, 0);
}
+ (id) new
{
return [[self alloc] init];
}
- (id) init
{
puts(__func__);
bar = 31;
return self;
}
- (id) set_bar: (int)value
{
puts(__func__);
bar = value;
return self;
}
- (int) get_bar
{
puts(__func__);
return bar;
}
#end
main.m:
#import <stdio.h>
#import "Foo.h"
int
main
()
{
id
foo = [Foo new];
printf("old bar: %i\n", [foo get_bar]);
[foo set_bar: 10];
printf("new bar: %i\n", [foo get_bar]);
return 0;
}
Makefile:
run: main.o Foo.o
gcc $^ -o $# -lobjc
main.o: main.m Foo.h
gcc -c $< -o $#
Foo.o: Foo.m Foo.h
gcc -c $< -o $#
The output I've got:
+[Foo alloc]
-[Foo init]
-[Foo get_bar]
old bar: 31
-[Foo set:bar:]
-[Foo get_bar]
new bar: 10
Looks like it works!
Related
I came across this unusual bit of Objective-C today which, to my suprise, compiles without fail and outputs "hi":
#include <Foundation/Foundation.h>
#interface MyObject : NSObject
- (NSString *)method:param;
#end
#implementation MyObject
- (NSString *)method:param
{
return param;
}
#end
int main() {
MyObject * m = [MyObject new];
NSLog(#"%#", [m method:#"hi"]);
return 0;
}
Compile command:
# clang version: clang-900.0.37
clang test.m -fobjc-arc -o test
method has a single argument, param, but I did not explicitly give a type. I can't seem to find any documentation on this? What's going on?
I've been unable to find any official docs on this, but in my testing this is indistinguishable from the param being a generic id type.
I'm making a simple test to see if I can run Objective-C on Linux without GNUStep, so I followed an example here on SO to get it running, here's my code:
//WSObject.h
#include <objc/runtime.h>
#include <stdio.h>
#interface WSObject
+ (id) alloc;
- (void) dealloc;
- (void) hello;
#end
//WSObject.m
#include "WSObject.h"
#implementation WSObject
+ (id) alloc {
return class_createInstance(self, 0);
}
+ (void) dealloc {
object_dispose(self);
}
+ (void) hello {
puts("Hello, world");
}
#end
//test.m
#include "WSObject.h"
int main(int argc, const char *argv[]) {
WSObject *obj = [WSObject alloc];
[obj hello];
[obj dealloc];
return 0;
}
When I then try to compile it with gcc test.m -o test -lobjc
I get the following error:
/tmp/ccDzvsol.o:(.data+0x80): undefined reference to `__objc_class_name_WSObject'
collect2: error: ld returned 1 exit status
Any help?
I am trying to make a new Objective-C root class on the GNU runtime. Here is what I have so far:
foo.h:
#include <objc/objc.h>
#interface Foo {
Class isa;
int bar;
}
+ (id) alloc;
+ (id) newWithBar: (int) bar;
- (id) initWithBar: (int) bar;
- (void) p;
- (void) dispose;
#end
foo.m:
#import <foo.h>
#include <stdio.h>
#implementation Foo
+ (id) alloc {
return class_createInstance(self, 0);
}
+ (id) newWithBar: (int) bar {
return [[self alloc] initWithBar: bar];
}
- (id) initWithBar: (int) bar_ {
bar = bar_;
}
- (void) p {
printf ("bar=%d\n", self->bar);
}
- (void) dispose {
object_dispose(self);
}
#end
and a little test program, main.m:
#import <foo.h>
int main(int argc, char *argv[]) {
Foo *foo = [Foo newWithBar: 3];
[foo p];
[foo dispose];
return 0;
}
When I compile foo.m, I get the following warning:
foo.m: In function ‘+[Foo alloc]’:
foo.m:7:3: warning: return makes pointer from integer without a cast [enabled by default]
Why? When I dive into the header files, I can see that class_createInstance returns id. What am I doing wrong here?
You need to include the header for the objective C runtime. The default behaviour of the compiler is to assume undeclared functions return an int.
#include <objc-auto.h>
Sorry - answer above is for OS X/iOS. For GNU you need to include runtime.h as well as objc.h
#include <objc/runtime.h>
How to make an Objective-C project work on Ubuntu?
My files are:
Fraction.h
#import <Foundation/NSObject.h>
#interface Fraction: NSObject {
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
#end
Fraction.m
#import "Fraction.h"
#import <stdio.h>
#implementation Fraction
-(void) print {
printf( "%i/%i", numerator, denominator );
}
-(void) setNumerator: (int) n {
numerator = n;
}
-(void) setDenominator: (int) d {
denominator = d;
}
-(int) denominator {
return denominator;
}
-(int) numerator {
return numerator;
}
#end
main.m
#import <stdio.h>
#import "Fraction.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac = [[Fraction alloc] init];
// set the values
[frac setNumerator: 1];
[frac setDenominator: 3];
// print it
printf( "The fraction is: " );
[frac print];
printf( "\n" );
// free memory
[frac release];
return 0;
}
I've tried two approaches to compile it:
Pure gcc:
$ sudo apt-get install gobjc gnustep gnustep-devel
$ gcc `gnustep-config --objc-flags` -o main main.m -lobjc -lgnustep-base
/tmp/ccIQKhfH.o:(.data.rel+0x0): undefined reference to `__objc_class_name_Fraction'
I created a GNUmakefile Makefile:
include ${GNUSTEP_MAKEFILES}/common.make
TOOL_NAME = main
main_OBJC_FILES = main.m
include ${GNUSTEP_MAKEFILES}/tool.make
... and ran:
$ source /usr/share/GNUstep/Makefiles/GNUstep.sh
$ make
Making all for tool main...
Linking tool main ...
./obj/main.o:(.data.rel+0x0): undefined reference to `__objc_class_name_Fraction'
So in both cases compiler gets stuck at
undefined reference to `__objc_class_name_Fraction'
Do you have and idea how to resolve this issue?
It's right. In both cases you did not include Fraction.m in your list of files to be compiled, so it can't find the implementation of the class Fraction
From the comment, this command works
gcc `gnustep-config --objc-flags` -o main *.m -lobjc -lgnustep-base
I am not an expert at writing the make files like that, I find simply typing the following works on ubuntu quite well:
gcc -I /usr/include/GNUstep/ -I /usr/include/mysql -L /usr/lib/GNUstep/\
-lgnustep-base -lmysqlclient\
-g -ggdb\
-fconstant-string-class=NSConstantString -o test *.m
I am using it on this project:
http://github.com/uptecs/SmsgateDelivery/
If the above GCC command does not work you have not installed enough packages, use apt-cache to search for more gcc and objective c packages to install (I just installed more packages that looked relevant at random until it worked)
the make file:
include ${GNUSTEP_MAKEFILES}/common.make
APP_NAME=Fraction
Fraction_HEADERS = Fraction.h
Fraction_OBJC_FILES = main.m Fraction.m
include ${GNUSTEP_MAKEFILES}/application.make
The approach I just got working was (in Ubuntu, which is closely related to Debian):
Use Synaptic to install all likely-looking GnuStep packages;
Source ( . ) the GnuStep startup script, /usr/share/GNUstep/Makefiles/GNUstep.sh (this can go into .profile or .bashrc or something so you don't have to do it manually every time)
Create a GNUmakefile according to the instructions in A First Tool
This allowed me to successfully build command line programs.
Add Fraction.m in make file,
include ${GNUSTEP_MAKEFILES}/common.make
TOOL_NAME = main
main_OBJC_FILES = main.m Fraction.m
include ${GNUSTEP_MAKEFILES}/tool.make
and then make ^^
Just add Fraction.m in main.m instead to add Fraction.h in main.m. And this will work. The following is the main.m file i used :
#import <stdio.h>
#import "Fraction.m"
int main( int argc, const char *argv[] ) {
//create a new instance
Fraction *frac = [[Fraction alloc] init];
//set the values
[frac setNumerator: 1];
[frac setDenominator: 3];
//print it
printf("The fraction is : ");
[frac print];
printf("\n");
//free memory
[frac release];
return 0;
}
I have no idea if this is possible, but if it is, what would the syntax look like?
If not possible, why not?
You should be able to bind to a message implementation (IMP), which are just plain C functions with two hidden parameters, self and _cmd of types id and SEL respectively.
EDIT: Just tested the following complete example, and it appears to work.
#include <stdio.h>
#include <boost/bind.hpp>
#include <Foundation/NSObject.h>
#interface MyClass : NSObject
{
}
-(int) doSomething:(int)arg;
#end
#implementation MyClass
-(int) doSomething:(int)arg
{
printf("doSomething: self=0x%08x _cmd=0x%08x\n", self, _cmd);
return arg + 1;
}
#end
int main(void)
{
MyClass *myObj = [[MyClass alloc] init], *otherObj = [[MyClass alloc] init];
typedef int (*MyFunc)(id, SEL, int);
SEL doSomething_sel = #selector(doSomething:);
MyFunc doSomething_impl = (MyFunc)[myObj methodForSelector:doSomething_sel];
// bind self & _cmd arguments:
// calls [myObj doSomething:x]
int result = boost::bind(doSomething_impl, myObj, doSomething_sel, _1)(14);
printf("result1: %d\n", result);
// bind _cmd & arg:
// calls [otherObj doSomething:3]
result = boost::bind(doSomething_impl, _1, doSomething_sel, 42)(otherObj);
printf("result2: %d\n", result);
return 0;
}
With GNUstep, compile as:
gcc objcbind.mm -o objcbind -I/usr/include/GNUstep -lobjc -lstdc++ -lgnustep-base
On Mac OS X, compile as:
gcc objcbind.mm -o objcbind -framework Foundation -lstdc++
Output:
doSomething: self=0x01a85f70 _cmd=0x00602220
result1: 15
doSomething: self=0x01a83d70 _cmd=0x00602220
result2: 43