tags:

views:

216

answers:

6

I am planning to write a code library to access some hardware at a low-level (i.e. flipping register bits and such).

Previously, I wrote everything as C functions and used extern "C" to make the library compile for both C and C++ code. So, both C and C++ users merely had to include the header file and call the functions as they were.

Now, I am thinking of organising things as classes. For example, I can put all the functions to initialise, configure, transmit and receive a UART in a class. This works fine in C++ but how about C? I can't extern "C" an entire class.

One thing that I was thinking of: write everything in standard C functions escaped with extern "C". Then, provide a wrapper class for C++, that has a bunch of inline methods that call these 'C' functions.

int foo_bar (int *address, int data) {...} // extern C stuff
int foo::bar (int *address, int data) { return foo_bar(address, data); } // inline method

Is that okay? Any other ideas? Best practices?

+2  A: 

You can do that, but what does it gain you? Unless the class adds some functionality, I would stick with the free function approach.

On the other hand, it is possible for the class approach to make the C functions much easier to use, by doing things like managing buffers etc. for the classes clients - the class still uses the C API to do the actual work.

anon
Nothing gained in the binary. But it may be easier to do UART.send(data) instead of UART_send(*port, data); The pointer could be private for eg.
sybreon
The C-interface would still be available, and you actually need to pointer to port to use that. So it wouldn't really be private.
MadKeithV
+5  A: 

There is some precedent for what you're proposing - Microsoft's MFC classes are just C++ wrappers around the C-compatible Windows API.

Before you start though, you should have some goal in mind beyond just creating busywork for yourself. The C++ should be easier to work with than the C, or you're not gaining anything.

Mark Ransom
+1  A: 

I understand that you want to benefit from the abstraction capabilities of C++ but still have your code accessible to code written in C. One approach to achieve this is to write the bulk of your code in C++ and then create a set of thin extern "C" wrapper functions that will interface your code to the C world.

The approach you suggest also works, but, as another respondent noted, it doesn't buy you any additional power.

Note that both approaches introduce a slight performance penalty over the plain C function approach. In your proposal the C++ code pays the price, in mine the C code pays the price. This can be minimized by defining the functions as inline.

Diomidis Spinellis
Yes, I plan to make them inline wherever possible.
sybreon
+2  A: 

One solid reason for doing this is if your C interface uses the typical "handle" idiom to represent resources.

OpaqueThingHandle t = CreateOpaqueThing();

DoStuffWithOpaqueThing(t);

DestroyOpaqueThing(t);

In C, to achieve information hiding, the OpaqueThingHandle is often a typedef for void * so that clients have no visibility of how it is implemented.

C++ wrappers will be able to add something genuinely useful simply by applying RAAI - mapping construction and destruction on to the functions that aquire or free the resource identified by the handle type:

class OpaqueThing : boost::noncopyable
{
    OpaqueThingHandle handle;

public:
    OpaqueThing()
        : handle(CreateOpaqueThing()) {}

    ~OpaqueThing()
        { DestroyOpaqueThing(handle); }

    void DoStuff()
        { DoStuffWithOpaqueThing(handle); }
};
Daniel Earwicker
+1  A: 

Frankly, I don't see what having essentially duplicate C and C++ functions buys you.

I'd suggest keeping the C functions as basic as possible, defining an API with all the primitives exposed and nothing fancy. Then define the C++ classes at a much higher level, so a UART would have a constructor, destructor, meaningful methods, that sort of thing. It would have all necessary data storage. The C++ classes would not expose all possible primitives; in order to do something different, you'd write a new member function or class or whatever, calling the C API.

That's the approach in systems like OCCI (the Oracle C++ Call Interface) and MFC and others, and it works well.

David Thornley
Yup, that's what I intend to do. Maybe it wasn't clear in the question.
sybreon
+2  A: 

You'd need to export both the C - method-based and the C++ - class interface. You can go both ways - either a thin C++ wrapper around the C functions, or C functions around the C++ instance.

For the latter, a typical pattern is this:

void * c_open_thing(id) { return new CThing(id); }
void c_close_thing(void * handle) { delete (CThing) handle; }
int c_transmit(void * handle, transmitbuf) 
  { return ((CThing *)handle)->Transmit(transmitbuf); }

Such a simplistic wrapper is pointless, no matzter in which way you do it, though. The C++ wrapper could add value in the following ways:

  • Enforce construction / destruction requirements
    Using cosntructor/destructor is usually not sufficient here, unless you can provide assignment and copy construction. I'd typically use a reference counted handle.

  • Error handling
    This might be required cleanup, converting errors to (meaningful!) exceptions etc.

  • Thread safety Of course, if you control the original library, you can add it there.

  • ...

peterchen