PDA

View Full Version : Call C-function and return its result in window (hello world app/Mix Obj-C & C)




cea
Oct 26, 2009, 01:50 PM
Hello,

i have created a simple "hello world" application with one button. click on the button and "hello world!" appears in the center of the window. it works fine.

Now, i include 2 simple plain c file in this project:
info.h
info.c

I would like to call the info() function from the info.c and return/output its result in the window like the message "hello world!" when i push the button.

My intention is to use plain c as core and obj-c as GUI.
How can i do this without to change/rewrite c-code inside info.c to use obj-c GUI?

here is the hello world project with obj-c and c-files:
http://www.mediafire.com/download.php?znhwmwnyzzz

Would you please download it and make the necessary changes for me so that i can use it as a example to better understand how it works.
Thank you!



lee1210
Oct 26, 2009, 02:41 PM
I don't think people need to do the work for you, but I'll tell you the way I'd approach this.

right now, info has a char passed in, does absolutely nothing with it, then returns that value as an int. I'm not sure what the goal was, but what you probably need to do will depend on how much you can change info.c.

I would personally change it to return an autoreleased NSString *. That way you don't have to worry about the size of a char * passed in, etc. Then you'd change your printf calls in info.c to stringWithFormat: messages to NSString, and append those things to the NSString you're returning, etc. info.c would need to be changed to a .m file to use NSString, though.

If you don't want to do this, then you'll need to: pass in a char * that is statically allocated with enough room, or dynamically allocated before calling info, or in info. I really don't like allocating things in a function, so i think it would be best to pass in a char * pointing to some memory of a predetermined size. You could also pass in it's length for error checking. You could then snprintf, or strncat into that thing in info. Then in the calling function, you could turn the char * into an NSString, and set that as the value of textField with setStringValue as you are already doing in sayHello:.

If you're doing any dynamic allocation of memory in C, you'll have to be careful to make sure you clean it up, etc. Once you "go there" you take responsibility for the memory. You don't get to lean on Cocoa's retain/release memory management, or Objective-C 2.0s garbage collection, you are on your own. By the same token, if you don't allocate dynamically, then you have the burden of making sure you don't overflow your fixed size buffer. Truly, the easiest way to avoid all of this is to just use NSMutableString's, etc. that will resize to fit your needs.

-Lee

cea
Oct 26, 2009, 05:43 PM
Thank you for your reply.
I am still newbee, hence the "hello world" app for execise.
I try but do not understand the theory what you mean. I think i need a practical example.

lee1210
Oct 26, 2009, 06:42 PM
Here's a rewritten info.c (changed to info.m so messages can be passed):
#include "info.h"
#include <stdio.h>
#include <stdlib.h>

NSString *stringWithInfo()
{
struct person {
char *name;
int age;
char *occupation;
};
struct person people[3] = {"Peter",45,"Architect",
"Jenni",21,"Singer",
"Sarah",29,"Teacher"};
int i = 0;
NSMutableString *myResult = [[NSMutableString alloc] init];
for (i=0; i < 3; i++) {
NSString *tmpString = [NSString stringWithFormat:@"Name: %s\nAge: %d\nOccupation: %s\n",
people[i].name,people[i].age,people[i].occupation];
[myResult appendString:tmpString];
}

return [myResult autorelease];
}

Here is the changed version of Foo.m:


//
// Foo.m
// HelloWorld

#import "Foo.h"
#import "info.h" //inport plain c-header

@implementation Foo

- (IBAction)sayHello:(id)sender
{

/* call info(result); function from info.c and return/output its content
/* in the textField in the window like this:

hello world!
Name: Peter
Age: 45
Occupation: Architect
Name: Jenni
Age: 21
Occupation: Singer
Name: Sarah
Age: 29
Occupation: Teacher

*/

[textField setStringValue:stringWithInfo()];
}

@end


I also changed the .xib file, making the setting the line breaks attribute to "word wrap", and making the field about the size of the frame to fit the additional lines of text.

-Lee

Edit:
If you REALLY want to do this with C, you could play it like this:

#include "infoPlain.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int numDigits(int);

char *infoAlloc() {
char *result = (char *)NULL;
struct person {
char *name;
int age;
char *occupation;
};
struct person people[3] = {"Peter",45,"Architect",
"Jenni",21,"Singer",
"Sarah",29,"Teacher"};
int neededSize = 0;
int x = 0;
for (x = 0; x < sizeof(people) / sizeof(people[0]); x++) {
neededSize += strlen(people[x].name) + strlen(people[x].occupation) + numDigits(people[x].age);
neededSize += 26;
}
result = (char *)malloc((size_t)(neededSize+1));
char *tmpLine = (char *)malloc((size_t)(neededSize+1));
result[0]='\0';
for(x=0;x < sizeof(people) / sizeof(people[0]); x++) {
sprintf(tmpLine,"Name: %s\nAge: %d\nOccupation: %s\n",people[x].name,people[x].age,people[x].occupation);
strncat(result,tmpLine,neededSize+1);
}
free((void *)tmpLine);
return result;
}

int numDigits(int x) {
char tmp[32];
snprintf(tmp,32,"%d",x);
return strlen(tmp);
}


Then calling would look like:

char *tmpCStr = infoAlloc();
NSString *tmpStr = [NSString stringWithCString:tmpCStr];
[textField setStringValue:tmpStr];
free((void *)tmpCStr);


If you have an Objective-C compiler available, though, I'd use it.

cea
Oct 26, 2009, 08:17 PM
ahhhhhh! i do understand the principle of calling function (the way with rewritting code in the info.m)

I will try to execise the second part (the way without any changes in C-code). this is what i am looking for because my finished project is written in pure C (commandline) and it would take me age to rewrite it just to be able to use obj-c GUI with a few outputs.

//Edit:
The second part works for me, too. And the most important thing to me is to understand the principle.

I upload the both working examples here in the case that someone needs it:

- Cocoa Hello world with C file inside (changed to *.m):
http://www.mediafire.com/download.php?jkuaiyzzmzi

- Cocoa Hello world with plain C file inside (original/unchanged):
http://www.mediafire.com/download.php?mgdhzq3b5yw

All credits go to lee1210.

Thank you very much again, Lee!

lee1210
Oct 26, 2009, 08:53 PM
If your goal is to have a commandline interface and a GUI, you need to avoid implementing methods that implement logic and do I/O. You need to change from something like info as you had implemented, which sorted through some data, laid it out, and displayed it, and instead make something like infoAlloc that can be called to get some data generically, then you can call this from a routine that will print the result to the console, and one that will use this to set a GUI element.

Honestly, infoAlloc could be changed to an even more generic version that would just get the data, totally unformatted. Then you can format it appropriately based on the interface in use.

-Lee

cea
Oct 26, 2009, 09:44 PM
Your advice is wise and gold worth to me. Yes, you are right. It does I/O. I do study your first implementing method and i think it is not too hard to implement it since i have understand the way it work yet. I will try this way.
I have learned much from this thread.
Thank you very much! :)