Search code examples
c++cstructtypedef

Pass generated C++ struct templates to a C header


See also "How can I pass an object of a C++ class to/from a C function?" (C++ FAQ), however I am unsure how to apply it to my case.

I'm trying to expose C-compatible types (structs) from my C++ source file.

I made them theoretically compatible by "casting" the struct to different ways. How do I make those "casted" types accessible to by C header c_header.h?

cpp_header.hpp

#ifdef __cplusplus

template <typename T>
struct item {
    uint32_t foo;
    T data;
};

int bar(struct item<T> *item);

extern "C" {

typedef struct item<uint8_t> u8_item_t;
typedef struct item<uint16_t> u16_item_t;

}

#endif // __cplusplus

c_header.h

#include "cpp_header.hpp"

int bar_u8(u8_item_t *item);
int bar_u16(u16_item_t *item);

cpp_source.cpp

#include "cpp_header.hpp"
#include "c_header.h"

template <typename T>
int bar(struct item<T> *item) {
    // ...
    return 0;
}

extern "C" {

int bar_u8(u8_item_t *item) { return bar<uint8_t>(item); }
int bar_u16(u16_item_t *item) { return bar<uint16_t>(item); }

}

When compiling, the first errors I see are

c_header.h : error: unknown type name 'u8_item_t'
   | int bar_u8(u8_item_t *item);
                ^~~~~~~~~
c_header.h : error: unknown type name 'u16_item_t'
   | int bar_u16(u16_item_t *item);
                 ^~~~~~~~~

Solution

  • The problem is that your typedefs for the C types are inside the C++ guard. Unfortunately, if you move them outside, you will get an error any time your are compiling C, because C does not have template types.

    Probably your best choice is just to define C incomplete types. You will also need a separate C function for each variant of your template type you use because C can't tell the difference -they are all pointers and it also doesn't have parameter overloading.

    #ifdef __cplusplus
    
    template <typename T>
    struct item {
        uint32_t foo;
        T data;
    };
    
    int bar(struct item<T> *item);
    
    extern "C" {
    #endif
    
    typedef struct u8_item_t u8_item_t;
    typedef struct u16_item_t u16_item_t;
    
    int bar_u8(u8_item_t *item);
    int bar_u16(u16_item_t *item);
    
    #ifdef __cplusplus
    }
    
    #endif // __cplusplus
    

    bar_u8 and bar_u16 are shim functions and their only purpose is to do the cast and call the template function.