8

Is it legal to forward-declare a struct as a C-struct

// api.h
#ifdef __cplusplus
extern "C" {
#endif

    typedef struct handle_tag handle_t;

    handle_t *construct();
    void destruct(handle_t *h);

    void func(handle_t *h);

#ifdef __cplusplus
}
#endif

and subsequently define it as a C++-struct, i.e. as a non-POD type?

// api.cpp
struct handle_tag {
    void func();
    std::string member;
};

void func(handle_t *h) {
    h->func();
}

The general intention is to get via a C interface an externally accessible opaque type handle_t which is internally implemented as an C++ data type.

7
  • 4
    Incidentally, PODness is unrelated to C vs. C++ linkage. C++ names are mangled regardless of whether they are PODs. Commented Jul 10, 2013 at 10:57
  • @KonradRudolph That struct is not POD Commented Jul 10, 2013 at 11:10
  • 1
    @BЈовић Well yeah, nobody claimed it to be, no? Commented Jul 10, 2013 at 11:19
  • 1
    Naming quibble: a "handle" is something that gives you an indirect way to refer to something, similar to a pointer. Giving your heavy struct the name "handle_t" and then asking the user to use pointers to it doesn't make sense. Instead, give the struct some other name and typedef "handle_t" to be a pointer to it. Then use that type directly instead of pointers. Commented Jul 10, 2013 at 11:56
  • 1
    You do. My point is that the type handle_t* looks extremely unnatural to me, and I would probably use plain handle_t constantly and unconsciously and then be annoyed when it doesn't compile. Compare the HANDLE and similar types in the Windows API. Commented Jul 10, 2013 at 13:44

2 Answers 2

9

Yes, that will work fine as long as the C code never needs to see "inside" the handle_tag structure, and the appropriate C++ construction/destruction is performed by the C++ code (which I preseume the construct and destruct are for).

All that the C code needs is a pointer to some datastructure - it won't know what the contents is, so the content can be anything you like it to to be, including constructor/destructor reliant data.

Edit: I should point out that this, or methods similar to it (e.g. using a void * to record the address of an object for the C portion to hold), is a fairly common way to interface C-code to C++ functionality.

Edit2: It is critical that the C++ code called doesn't "leak" exceptions into the C code. That is undefined behaviour, and very much liable to cause crashes, or worse, "weird things" that don't crash... So unless the code is guaranteed to not cause exceptions (and for example std::string is liable to throw bad_alloc in case of low memory), it is required to use a try/catch block inside code like construct anf func in the C++ side.

Sign up to request clarification or add additional context in comments.

11 Comments

You sure that works? Will the object file for api.cpp export a non-mangled name for handle_tag?
@KonradRudolph: usually only variables and functions manifest themselves as exported names, just a type itself doesnt have any mangled name.
as long as you use the exported typedef only as a pointer, you will have no problem doing this. The C doesnt need to know the size of the struct to know the size of a pointer to that struct.
the names for struct, class and union are not "exported". They are used by the compiler during compilation - and used for name-mangling, but as long as the C callable functions are wrapped with extern "C", that shouldn't matter.
@PlasmaHH Sure they are. Since when does the C ABI support templates and namespaces? But I agree, there’s no reason to mangle a non-template type in the global namespace. But is this universally guaranteed?
|
2

Won't work quite as is, but the concept's ok. When the functions are defined, you also need to make sure the names aren't mangled so that the C code can find them. That means #include "api.h" should be added atop your api.cpp file.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.