Home > OS >  How to hide the struct implementation and avoid variable has incomplete type at the same time in c?
How to hide the struct implementation and avoid variable has incomplete type at the same time in c?

Time:12-02

Define InputBuffer in InputBuffer.c

typedef struct InputBuffer_t {
  char* buffer;
  size_t buffer_length;
  ssize_t input_length;
} InputBuffer;

Hide the InputBuffer's implementation in InputBuffer.h

#ifndef INPUTBUFFER_H
#define INPUTBUFFER_H

typedef struct InputBuffer_t InputBuffer;

#endif

Then use InputBuffer in testBuffer.c

#include "InputBuffer.h"

void testBuffer() {
   InputBuffer b = sizeof(InputBuffer);
}

However, compiling testBuffer will result in "variable has incomplete type 'struct InputBuffer'" because the complete InputBuffer implementation is not in InputBuffer.h.

Therefore, I wonder if there is a way to hide the implementation of a struct type and avoid the incomplete type error at the same time.

CodePudding user response:

The down side of private encapsulation through forward declaration is that the caller gets an incomplete type, that's just how it is. The caller has to use pointer types.

If you for some reason need to expose the size of the struct outside the encapsulation, you'll have to design a getter function for that purpose. Example:

InputBuffer* b = InputBufferCreate();
size_t size = InputBufferGetSize(b);

More info here: How to do private encapsulation in C?

CodePudding user response:

sizeof(InputBuffer)

You can't take the size of a hidden struct (frequently called an "opaque struct")! testBuffer.c has no idea what size the struct is because it's hidden! It doesn't have access to the implementation.

Also I have no idea what you're trying to do here:

#include "InputBuffer.h"

void testBuffer() {
   InputBuffer b = sizeof(InputBuffer);  // <=== What is this?
}

You can't arbitrarily assign a number to a struct.


Additional notes:

Also, your typedef is awkward.

In InputBuffer.c, do:

typedef struct InputBuffer_s {
  char* buffer;
  size_t buffer_length;
  ssize_t input_length;
} InputBuffer_t;

In InputBuffer.h, do:

#ifndef INPUTBUFFER_H
#define INPUTBUFFER_H

// InputBuffer_h is a "handle", or pointer to an opaque struct; 
// AKA: InputBuffer_h is an "opaque pointer", meaning it is a pointer
// to a struct whose implementation is hidden. This is true data-hiding
// in C.
typedef struct InputBuffer_s *InputBuffer_h;

#endif

And in testBuffer.c:

#include "InputBuffer.h"

void testBuffer(InputBuffer_h inputBuffer) {

}

But, you can't do anything with that inputBuffer param really, as you can't dereference it nor access any of its members in "testBuffer.c" because its implementation is hidden and defined in a different source file (InputBuffer.c) which you have not included!


Therefore, I wonder if there is a way to hide the implementation of a struct type and avoid the incomplete type error at the same time.

So, you should declare your function prototypes which need access to the implementation in InputBuffer.h, and then write the function definitions in InputBuffer.c, so they have access to the opaque struct's implementation details, since the struct is defined in InputBuffer.c.

vvvvvvvvv
Here is a more-thorough answer I wrote on how I like to use and write "Object-based" C architecture using opaque pointers/structs: Opaque C structs: various ways to declare them
^^^^^^^^^

Note that an alternative approach to using an opaque pointer/struct architecture (which opaque pointers/opaque struct architectures frequently require using dynamic memory allocation with malloc(), as I show in my other answer linked-to above), is to simply include "hidden" implementations in headers suffixed with _private.h, such as myheader_private.h. The implication is that these "private" headers should only be included in source files which need to see the "hidden" struct's full definition, but should never be included by the user of the API directly. This is NOT true data hiding, whereas opaque pointers / opaque structs are true data hiding, but it gives an alternative approach to have similar behavior, and with the advantage of allowing static memory allocation instead of requiring dynamic memory allocation as opaque pointers (aka: opaque structs) require in my other answer linked-to above.

  • Related