Search code examples
c++cinner-classesconditional-compilationone-definition-rule

typedefing a pointer recognized by C to an inner C++ class


I have a class that I want to share between C and C++, where C is only able to get it as a pointer. However because it is an inner class it cannot be forward-declared. Instead this is what our current code does in a common header file:

#ifdef __cplusplus
class Container {
public:
    class Object {
        public:
        int x;
    };
};

typedef Container::Object * PObject;
#else

typedef void* PObject;

#endif

This looks like it violates the one definition rule (ODR), because C and C++ see different definition using the #ifdef. But because this is a pointer, I'm not sure if this creates a real problem or not. C only uses the pointer in order to pass it to C++ functions, it doesn't directly do anything with it. For instance this code in the common header file:

#ifdef __cplusplus
extern "C" {
#endif
int object_GetX(PObject pObject);

#ifdef __cplusplus
}
#endif /* __cplusplus */

This as how we implemented it in the C++ file:

int object_GetX(PObject pObject) {
    return pObject->x;
}

My questions are:

  1. Would violating the ODR like this cause any real problems?
  2. If so, is there another convenient way to share the class to C?

Solution

  • First of all, type-aliasing pointers is usually a recipe for trouble.
    Don't do it.

    Second, the "inner" class is an overused concept, so my first reflex would be to consider whether it's really necessary.

    If it is necessary, you can define an opaque empty type and derive from it for some type safety:

    In a shared header:

    struct OpaqueObject;
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    int object_GetX(OpaqueObject* pObject);
    
    #ifdef __cplusplus
    }
    #endif /* __cplusplus */
    

    In a C++ header:

    
    struct OpaqueObject {};
    
    class Container {
    public:
        class Object : public OpaqueObject {
            public:
            int x;
        };
    };
    
    
    

    Implementation:

    int object_GetX(OpaqueObject* pObject) {
        return static_cast<Container::Object*>(pObject)->x;
    }