Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expose C struct size without exposing its type?

Tags:

c

I have the following in C (not C++!):

module.c
    struct Private {...};
    void foo(void* private, int param) {...}

module.h
    #define PRIVATE_SIZE ???;
    void foo(void* private, int param);

main.c
    char m1[PRIVATE_SIZE];
    char m2[PRIVATE_SIZE];

    int main()
    {
        foo(m1, 10);
        foo(m2, 20);
    }

How can I expose sizeof(Private) at compile time so that application can statically allocate its storage without exposing Private type?

Note, this is a very limited embedded system and dynamic allocation is not available.

like image 967
jackhab Avatar asked Mar 04 '23 11:03

jackhab


1 Answers

You shouldn't expose the size of the struct to the caller, because that breaks the whole purpose of having private encapsulation in the first place. Allocation of your private data is no business of the caller. Also, avoid using void* because they complete lack type safety.

This is how you write private encapsulation in C:

  • In module.h, forward declare an incomplete type typedef struct module module;.
  • In module.c, place the struct definition of this struct. it will only be visible to module.c and not to the caller. This is known as opaque types.
  • The caller can only allocate pointers to this struct, never allocate objects.
  • Caller code might look like:

    #include "module.h"
    ...
    module* m;
    result = module_init(&m)
    
  • And the module_init function acts as a "constructor", declared in module.h and defined in module.c:

    bool module_init (module** obj)
    {
      module* m = malloc(sizeof *m);
      ...
      m->something = ...; // init private variables if applicable
    
      *obj = m;
      return true;
    }
    
  • If the caller does need to know the size of the objects, it would only be for the purpose of hard copy etc. If there's a need for that, provide a copy function which encapsulates the allocation and copy ("copy constructor"), for example:

    result module_copy (module** dst, const module* src);
    

Edit:

Please note that the manner of allocation is a separate issue. You don't have to use dynamic allocation for the above design. In embedded systems for example, it is common to use static memory pools instead. See Static allocation of opaque data types

like image 112
Lundin Avatar answered Mar 06 '23 23:03

Lundin