Search code examples
c++cclasstypedefvoid-pointers

Redefining a handle ptr of void* to handle ptr to struct* (C/C++ mixcode) for access


I have C/C++ mix code and want to pass around a struct that contains a reference to a class. Because of this, I can't declare this struct in the header file of the C++ component (because class is defined in source file of C++ component) but only in the source file. The main script in C however has to reference that struct somehow, so I typedef it to void*. However because of that, I can't dereference the handle type back to a struct. Redefining the handle pointer in the source file is not possible. How can I work around this?

header_with_obj.hpp

class A {
    int a;
};

header.hpp

typedef void* config_handle_t;

source.cpp

#include "header.hpp"
#include "header_with_obj.hpp"

typedef struct {
    A* ptr;
    int some_other;
} config_t;

// typedef config_t* config_handle_t <-- error: conflicting declaration 'typedef struct config_t* config_handle_t '

int foo(void* arg)
{
    config_handle_t handle = (config_handle_t) arg;
    handle->A.a = 4; //  <-- error: 'config_handle_t' {aka 'void*'} is not a pointer-to-object type
}

main.c

#include "header.hpp"

int main()
{
    // we get that void* from somewhere and pass it in
    foo(arg);
}

Solution

  • The usual way to do this is to use an undefined struct. In its most basic form:

    void foo(struct the_config_struct *arg);
    // OK even though 'struct the_config_struct' wasn't defined!
    // surprisingly this is also allowed in C++
    

    You can also make a typedef:

    typedef struct the_config_struct *config_handle_t;
    void foo(config_handle_t arg);
    

    and if you want, you can even call the typedef the same thing as the struct. Just to avoid confusing people, I wouldn't do this unless it's a typedef for the struct (not a pointer).

    typedef struct the_config_struct the_config_struct;
    void foo(the_config_struct *arg);
    

    You don't to actually have defined the struct until you want to access its members:

    // if we uncomment this definition then it's OK
    // struct my_struct {
    //     char *message;
    // };
    
    void foo(struct my_struct *arg) {
        puts(arg->message); // error: struct my_struct is undefined
    }
    

    Finally (since this confused you before) you should know that typedef names and struct names are completely separate in C.

    struct foo {}; // defines "struct foo" but "foo" is completely unrelated
    typedef int bar; // defines "bar" but "struct bar" is completely unrelated
    
    foo *get_foo(); // error: "foo" is unknown
    struct foo *get_foo(); // OK
    
    typedef struct bar foo;
    foo *get_bar(); // OK: returns pointer to struct bar (not struct foo!)
    struct foo *get_foo(); // this one returns pointer to struct foo
    
    struct baz {};
    typedef struct baz baz;
    // now "baz" is an alternative name for "struct baz" - they are interchangeable
    
    typedef struct baz {} baz; // short version
    

    and structs don't have to have names:

    // foo is a variable, and it's a struct variable, but the struct has no name.
    // so we have no way to use the struct for anything else.
    struct {
        int i;
    } foo;
    
    // The struct is still there even though it doesn't have a name!
    // In C++ you can write decltype(bar) to say "the same type as variable bar".
    // Even though we don't know the person's name we can still yell out "Hey you in the red shirt!"
    decltype(foo) foo2; // a variable foo2. The type is decltype(foo) i.e. the struct from before
    
    // GCC lets you do it in C using "typeof".
    // This is not standard. It's a special feature in GCC.
    typeof(foo) foo2;
    
    // This struct also has no name either. But the typedef means we have
    // an "unofficial" way to name it, just like decltype(foo) before.
    // This is valid in C as well as C++.
    typedef struct {
        char message[50];
    } bar;