views:

118

answers:

4

I know there's no one answer to this question, but I'd like to get people's thoughts on how they would approach the situation.

I'm writing an Objective-C wrapper to a C library. My goals are:

1) The wrapper use Objective-C objects. For example, if the C API defines a parameter such as char *name, the Objective-C API should use name:(NSString *).

2) The client using the Objective-C wrapper should not have to have knowledge of the inner-workings of the C library.

Speed is not really any issue.

That's all easy with simple parameters. It's certainly no problem to take in an NSString and convert it to a C string to pass it to the C library.

My indecision comes in when complex structures are involved.

Let's say you have:

struct flow
{
    long direction;
    long speed;
    long disruption;
    long start;
    long stop;
} flow_t;

And then your C API call is:

void setFlows(flow_t inFlows[4]);

So, some of the choices are:
1) expose the flow_t structure to the client and have the Objective-C API take an array of those structures
2) build an NSArray of four NSDictionaries containing the properties and pass that as a parameter
3) create an NSArray of four "Flow" objects containing the structure's properties and pass that as a parameter

My analysis of the approaches:
Approach 1: Easiest. However, it doesn't meet the design goals
Approach 2: For some reason, this seems to me to be the most "Objective-C" way of doing it. However, each element of the NSDictionary would have to be wrapped in an NSNumber. Now it seems like we're doing an awful lot just to pass the equivalent of a struct.
Approach 3: Seems the cleanest to me from an object-oriented standpoint and the extra encapsulation could come in handy later. However, like #2, it now seems like we're doing an awful lot (creating an array, creating and initializing objects) just to pass a struct.

So, the question is, how would you approach this situation? Are there other choices I'm not considering? Are there additional advantages or disadvantages to the approaches I've presented that I'm not considering?

+2  A: 

I'd stick with approach 3. You're "just passing a struct" now, but the Flow object might expand more in the future. You say speed is not an issue and so I'd imagine memory consumption isn't either, or you would be sticking with C anyway.

greg
+2  A: 

I think that approach 3 would be the preferred way to do things. When you wrap a library you'll want to create wrappers around any object or structure that the user is expected to deal with.

If you wrap everything, then you are free to change the internal workings of your classes at a later date without affecting the interfaces that your users have become accustomed to. For example, in the future you may realize that you'd like to add some type of error checking or correction... maybe setting a stop that is earlier than the start causes some calculation errors, you could change the stop method in your Flow wrapper to set start equal to stop if stop is less than start (I admit, that's a really bad example).

jessecurry
I agree. And after all, initializing 4 objects and putting them in an array isn't really that much different than initializing 4 structs and putting them in an array, so I think I worry too much.
Wade Williams
A: 

Since Objective-C uses structures, why not leave it as a struct like NSRect?

Stephen Furlani
Because it's *usually* nicer to have the structs as actual objects, since you can't put structs in collections (like NSArrays) without boxing them in `NSValue` objects first.
Dave DeLong
A: 

My answer is not what you were asking, but it is still how I "would approach the situation".

The first question I would ask is, "what value does this wrapper add?" If the answer is, "to use Objective-C syntax" then the answer is "don't change a thing, use the library as-is because C is Objective-C syntax."

I don't know which library you are wrapping, so I'll use SQLite for an off the top of my head example. Using a layered approach, I would do something like this:

A) High level objects (Order, Customer, Vendor...)

B) Base class (Record)

C) SQLite

So the base class is written to call SQLite directly then the other classes work as regular Objective-C classes.

This is apposed to:

1) High level objects (Order, Customer, Vendor...)

2) Base class (Record)

3) SQLite Wrapper (Objective-C)

4) SQLite

The same application is created but there is extra work creating, maintaining and debugging level 3 with very little to show for it.

In the first version, layer B wrapped SQLite so nothing above it called SQLite directly AND it didn't try to provide all of SQLite functionality. It just provides the functionality that layer A needs and uses SQLite to achieve what is needed. It 'wraps' SQLite but in a more application specific manner. The same Record class could be reused in another application later and extended to meet the needs of both applications at that time.

Gary C