6

This is an completely rewritten version of an earlier question; I think the first version omitted important details; this one provides all the context.

I have a header of some C++ API. The API declares a few classes like this:

class Foo
{
public:
    inline void Bar(void);
    /* more inlines */
private:
    inline Foo();
    /* maybe more inline constructors */
}

I.e. no members, all functions are inline and public, except constructors. Constructors are private, so, as far as I understand C++, I cannot really call them. To create these objects I'm supposed to use auto_ptrs to them:

class FooAutoPtr : public std::auto_ptr<Foo> 
{
public:
    inline FooAutoPtr();
    /* one for each Foo constructors */
}

The API also has a second set of functions like this:

void Foo_Bar(void *self);
Foo* Foo_Constructor();

Let's call them core functions, because these are the symbols actually exported from the host app. Not the C++ classes.

Core functions have C linkage (i.e. they declared as extern "C"), but they are declared as taking and returning C++ types (e.g. they can take a reference: Foo &foo). Finally the header contains the implementation of the inline functions of the C++ classes. All these functions do the same: they call the core functions. For example, the FooAutoPtr constructor is like this:

inline FooAutoPtr::FooAutoPtr()
{
   reset(Foo_Constructor());
}

From what I understand, the code receives some object that is supposed to be a pointer to Foo from the host app and changes the auto_ptr gizmo to point to this object. But to the developer it looks as if it was a true pointer to Foo. And calling Foo::Bar() goes like this:

inline Foo::Bar()
{
    Foo_Bar(this);
}

This goes for all C++ classes and methods. Clever, huh?

Now, could somebody please explain what does all this mean? :) It's not a true C++ API, is it? To me it looks more like a thin C++ wrapper on top of a C API. If so, can I redeclare the core functions to lose the C++ bits? I understand how to write a C wrapper around C++ (actually, I already wrote it), but, if possible, I'd rather lose the wrapper and use the functions directly. But how do I lose the C++ stuff?

For example, there could be a function with references:

Bar& Foo_GetBar(void* self, const Baz& baz, int& i);

Right now I call it from my C++ wrapper like this:

typedef struct bar bar_t; /* and others */
/*...*/
bar_t*
foo_get_bar(foo_t* foo, baz_t* baz, int* i)
{
    return (bar_t*) &Foo_GetBar(foo, *(Baz*)baz, *i);
}

and it works (I have no idea, how). But I'd rather have it redeclared like this:

/* type? */ Foo_GetBar(foo_t*, /* type? /*, /* type? */);

UPDATE: I found an interesting thing that confirms Neil's answer. It is code in Common Lisp that uses the same API. (Naturally, it has to use the C part.) And, from what I can (barely) read in the source, the author simply used pointers in place of of references. Here's a snippet from code that converts C++ declarations into Lisp:

;; use * instead of & - we're not interested in C++ details
line (regex-replace-all "&" line "*")

So that's it :) Thanks, everyone!

Community
  • 1
  • 1
Mikhail Edoshin
  • 2,639
  • 16
  • 25
  • 8
    Wow, a question that is _legitimately_ tagged both `c` and `c++`. A rare appearance... – ildjarn Jan 25 '12 at 20:58
  • 2
    If the core functions return C++ types (references) then it _is_ a real live C++ API (_not_ a C API), but it's just not an object-oriented one. – Seth Carnegie Jan 25 '12 at 20:59
  • @SethCarnegie So that's it :) You see, the header even has a comment near the core functions saying that these are "for C-only environment". Well, another outdated comment :) – Mikhail Edoshin Jan 25 '12 at 21:18
  • But your function `foo_get_bar` is still compiled with C++ compiler, right? – Krizz Jan 25 '12 at 21:27
  • @Krizz Yes, of course. But it exports it as "C", and all types are also C, so I can call it from plain C. – Mikhail Edoshin Jan 25 '12 at 21:35

2 Answers2

4

In theory, the details of how a compiler treats a reference in an C-linkage declaration is an unspecified implementation detail. However many compilers treat it as if it was a pointer. So if the C++ library header imports Bar& Foo_GetBar(void* self, const Baz& baz, int& i); then you could try importing it into your C header as bar_t* Foo_GetBar(foo_t* self, const baz_t* baz, int* i); and it might just work.

Neil
  • 54,642
  • 8
  • 60
  • 72
1
#define & *    // convert references into pointers.

Stop. Right. There. That was a joke, rather than an attempt to see how many downvotes could be accumulated on a single answer.

But the approach of rewriting the header file to replace references with pointers should certainly work. Because these are C functions, there's no name mangling to worry about, and I've yet to see a compiler that doesn't implement references as pointers. (Is there any other possible way?)

Roddy
  • 66,617
  • 42
  • 165
  • 277