3

In my C programming, I use opaque-pointers to struct as a way to enforce abstraction and encapsulation of my code, in that manner :

interface_header.h:

 typedef struct s_mytype t_mytype;

defs_header.h:

struct s_mytype
{
/* Actual definition of the struct */
};

My problem I want to use a simple type as t_mytype (char for example), but not inform the interface about that. I can't just use typedef char t_mytype, since that would expose the the internals of the type. I could just use void pointers, but at the cost of type-checking, and I'd rather avoid that.

Doing two typedef wont work either, since that throw an typedef redefinition with different types from the compiler.

I am also considering doing a struct with only one member, which will be my simple type, but would that be an overkill ?

Thanks for your answers.

VannTen
  • 441
  • 2
  • 12
  • 2
    If you need an opaque type, structures are necessary. Decide whether opaqueness or simplicity is more important. Remember that you’ll be passing pointers around. – Jonathan Leffler Nov 13 '17 at 17:25
  • 2
    Use the single member. It's a bit of boilerplate in your *source*, but the compiled code should't be different. –  Nov 13 '17 at 17:26
  • Can you do something similar to how `sockaddr` and `sockaddr_in` are implemented? – MFisherKDX Nov 13 '17 at 17:28
  • @MFisherKDX Ewww! (and how does that solve the problem?) –  Nov 13 '17 at 17:30
  • @FelixPalmen -- expose `sockaddr` in your API and use `sockaddr_in` in your implementation ??. – MFisherKDX Nov 13 '17 at 17:34
  • @MFisherKDX Could you elaborate a bit more on that ? – VannTen Nov 13 '17 at 18:10
  • I'm going to withdraw my suggestion. I tried implementing `s_mytype` as a 0 sized structure and `s_mytype_in` as a struct with a first member of type `s_mytype` and a second member of type `char` and then realized this is worse than your and @FelixPalmen's proposed solutions to use a struct with 1 member. And on top of that, I discovered C does not necessarily allow 0 sized structures, although some compilers do. https://stackoverflow.com/questions/755305/empty-structure-in-c – MFisherKDX Nov 13 '17 at 18:37

1 Answers1

3

You have to make a decision. With code like this:

typedef char myHandle;

frobnicate(myHandle *obj);

you already document the intent that client code should only use pointers to myHandle and never assume anything about the underlying type, so you should be able to change the typedef later -- unless there's "sloppy" client code making assumptions it shouldn't.


If you want to completely hide what's behind myHandle, a struct is your only option in C:

typedef struct myHandle myHandle;

frobnicate(myHandle *obj);

and only in a private header or implementation file, you will put

struct myHandle
{
    char val;
};

The first option is simpler because inside the implementation of frobnicate(), you can access your value simply using *obj while the second option requires to write obj->val. What you gain with the second version is that you force client code to be written correctly.

In terms of the resulting executable, both versions are equivalent.

  • 1
    I'll opt for the second version. That will be more consistent with the rest of my code, and I always prefer to enforce my intent rather than just stating it. And anyway, adding the syntactic overhead of `obj->val` is not much, since there will be not much code there, expect for the dereferencing part. Thanks for that answer, that helped me to make up my mind ! – VannTen Nov 14 '17 at 08:05
  • @VannTen the dereference is always needed with opaque handles/pointers, and with `obj->val`, the compiler just finds an offset of `0` to be added to the pointer, which is of course eliminated, so you'll have no additional overhead in the binary. I personally prefer to force correct usage of my interfaces (as far as this is possible) as well, but it's a matter of taste ;) Good question btw, I ran into the same design decision earlier. –  Nov 14 '17 at 08:08