PDA

View Full Version : C Libraries




Cromulent
Jun 18, 2008, 01:24 PM
I wrote a very simple Usenet reader which is basically just a Unix application and wanted to turn it into a library so I could port it to Objective-C and Cocoa so I could write a GUI for it.

Are there any rules for doing so? I mean a lot of the functions deal with strings and processing the return codes that the server sends. Is there a convention for returning dynamically allocated strings that need to be freed outside of the library for instance or is this considered bad practice? Also, how does one go about performance testing a C library? I guess Instruments would be the tool to use but it wants to attach to a specific process and I have no idea how to go about doing that with a library without basically writing a full featured application first. Any suggestions?



lee1210
Jun 18, 2008, 01:56 PM
I'm not a huge fan of libraries allocating things that I will need to clean up, but if you are I would have those functions return the pointer to the allocated memory, and have alloc in its name so it's at least a bit clearer that the function is allocating something.

The other option is to have a structure that is passed into your routines, and an element of the structure is pointing to an allocated character array, etc. You would then have to define a "protocol" that user's of the library have to abide by, by which you require that if they call "FillSpecialStruct" they must call "DestorySpecialStruct". You could then have elements of the structure that determine if something has been cleared, allocated, etc. and safely free the things that you allocated.

If you truly cannot have some local variables that are large enough to hold any possible result that you can return a pointer to that the user has to explicitly copy out of (and they can malloc if needed), it gets pretty rough. It might be best to never ensure the user of coherency between calls into your library, so even if you dynamically allocate something you can just always reuse it/realloc if needed, etc. That way the user can allocate their own memory for it, so they know what has to be free'd.

-Lee

yeroen
Jun 18, 2008, 02:03 PM
Many frameworks wrap raw C strings into an opaque type and provide Create and Free functions for creating and freeing objects of those types. For example Motif does this with the XmString type, the XmStringCreateLocalized() and XmStringFree() functions.

Closer to home, this is also done in Core Foundation, for example you create a CFString from a C-string with CFStringCreateWithCString and free the CFString's memory when you're done with CFRelease. For more info, read up on it here:
Core Foundation Memory Management (http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/uid/20001148-103029)

In both Motif and Core Foundation, the idea is to provide functions whose name implies ownership. That is functions with 'Create' in their name imply that you the caller are responsible for freeing any dynamically allocated memory these functions return. Provision for doing so is given by functions with 'Free' or 'Release' in their name.

As for benchmarking libraries, you will have to write an application to test it. The good news is that it only has to be a simple main() driver that calls each of the libraries functions in turn, perhaps looping each call over n iterations. You can use gprof or Shark for compiling execution profile statistics.

Cromulent
Jun 18, 2008, 02:58 PM
Good advice. I think I'm going to need to sit down with a pen and paper and decide the best way to do things. I do have one design problem. Is it best to leave pretty much everything up to the programmer or to abstract as much as possible, i.e keep it to simple connect() and quit() functions with some useful message funtions or allow the programmer some degree of flexibility over what they are doing so they are in charge of every step.

Still, I guess I'll be the only person that will ever use the library so I guess it is not that important. But I do want to do it properly just in case I ever want to write a serious library in the future.

Edit : Just found a very interesting article on the Apple developer site about this if anyone is interested.

http://developer.apple.com/documentation/DeveloperTools/Conceptual/DynamicLibraries/Introduction.html

lazydog
Jun 18, 2008, 04:08 PM
How about running your code through the C++ compiler and make use of std::string. You don't have to convert your whole lib to make use of C++ classes etc, just use std::string for your string handling and function return values. You could then return strings by value and won't have to worry about ownership issues etc.

b e n

Cromulent
Jun 18, 2008, 04:35 PM
How about running your code through the C++ compiler and make use of std::string. You don't have to convert your whole lib to make use of C++ classes etc, just use std::string for your string handling and function return values. You could then return strings by value and won't have to worry about ownership issues etc.

b e n

Sounds like a bit of a hack to me. Won't that cause problems for C programs using the library? Especially if returning the string, does std:string use normal C null terminated strings? I.e is it just a wrapper class for C strings?

lee1210
Jun 18, 2008, 05:40 PM
If you used C++ string types, you'd be stuck with C++/Objective-C++. It's a decent idea, but it only works if you can link in C++ objects (even if you made C wrappers).

If you are writing C that accesses it, but you can link against C++ objects, it would be possible to write wrappers for the classes that call c_str() on the std::strings and return the const char *. It is const, so it cannot be modified. The C program could then make a copy, etc. This is done similarly to how I described above, in the circumstances that you return something that will only maintain coherency until the next call. See: http://www.cplusplus.com/reference/string/string/c_str.html, specifically:
"the values in this array ... are only granted to remain unchanged until the next call to a non-constant member function of the string object".

Because of this, it seems easier to just deal with it yourself in one of the other ways mentioned in C, rather than pull C++ into it without having major gains. This way if C++ wants to use the library, no problem. It can call your library, and use the resulting char * to instantiate a std::string, and move on with its life. If you want to use it in C, no problem, you have to do your own memory management but you are using C, so suck it up. If you want to use objective C, use it to instantiate an NS(Mutable)String and move on with your life.

I would lean towards options that avoid the user having to free something that they did not specifically allocate. Either having a single place you alloc/realloc that only remains valid between calls to your library or instituting a policy by which the user calls a function in the library to create a "thing" then they must call another function to get rid of that "thing" seem best.

-Lee

lazydog
Jun 19, 2008, 04:35 AM
Sounds like a bit of a hack to me. Won't that cause problems for C programs using the library? Especially if returning the string, does std:string use normal C null terminated strings? I.e is it just a wrapper class for C strings?

Yeah I guess you're right, though I was really trying to draw your attention to C++. With std::string your problems are solved. Your functions would return a string by value. If the receiver needs a copy then it would simply copy the function value. Something like this:-


//
// Library function.
//
const std::string library_fn()
{
char* test = "this is an example constructor from a c string" ;

return std::string( test ) ;
}



//
// Client code.
//
str::string my_string ;
..
..
my_string = library_fn()


There is no problem with ownership.

b e n

Cromulent
Jun 19, 2008, 03:14 PM
Yeah I guess you're right, though I was really trying to draw your attention to C++. With std::string your problems are solved. Your functions would return a string by value. If the receiver needs a copy then it would simply copy the function value. Something like this:-


//
// Library function.
//
const std::string library_fn()
{
char* test = "this is an example constructor from a c string" ;

return std::string( test ) ;
}



//
// Client code.
//
str::string my_string ;
..
..
my_string = library_fn()


There is no problem with ownership.

b e n

The problem with C++ is that I know absolutely nothing about it. I'm sure it allows you to do things in a more elegant manner (in some respects, although it does have a tendency to produce needlessly obtuse code). I wouldn't feel confident in using it as I would just end up it hacking it to look like a C program which is probably the worst of both worlds.

Edit : Is a library meant to export normal function symbols like this?

Typhoon:Release simon$ nm -gm libnntpMac.dylib

libnntpMac.dylib(single module):
(undefined) external ___stderrp (from libSystem)
(undefined [lazy bound]) external _bzero (from libSystem)
(undefined [lazy bound]) external _close$UNIX2003 (from libSystem)
(undefined [lazy bound]) external _connect$UNIX2003 (from libSystem)
(undefined [lazy bound]) external _fprintf (from libSystem)
(undefined [lazy bound]) external _free (from libSystem)
(undefined [lazy bound]) external _fwrite$UNIX2003 (from libSystem)
00000e36 (__TEXT,__text) external _getSocketToNNTPServer
(undefined [lazy bound]) external _gethostbyname (from libSystem)
00001008 (__DATA,__data) external _gnntpSock
00001004 (__DATA,__data) external _host
(undefined [lazy bound]) external _malloc (from libSystem)
00000f33 (__TEXT,__text) external _procReturnCode
00000d8e (__TEXT,__text) external _quitConnectionToNNTPServer
(undefined [lazy bound]) external _recv$UNIX2003 (from libSystem)
(undefined [lazy bound]) external _send$UNIX2003 (from libSystem)
00000f67 (__TEXT,__text) external _sepReturnCode
00001014 (__DATA,__common) external _serverAddress
(undefined [lazy bound]) external _socket (from libSystem)
(undefined [lazy bound]) external _strncpy (from libSystem)
(undefined [lazy bound]) external _strtol (from libSystem)