Partitioning struct into private and public sections?

Thomas Matthews picture Thomas Matthews · Sep 29, 2010 · Viewed 14.9k times · Source

In C++ and Java, data structures can have private, public and protected regions. I'd like to port this concept to a C language program I am writing.

Are there any idioms for implementing private or protected function pointers and data fields in a C struct? I know that C structs are public, I'm looking for an idiom to help hide some implementation details and force users to use the public interface.

Note: The language has been chosen by the shop, so I am stuck implementing Object Oriented concepts into C.

Thanks.

Answer

Tyler McHenry picture Tyler McHenry · Sep 29, 2010

As you know, you cannot do this. However, there are idioms that will allow a similar effect.

C will allow you do do something similar to what is known as the "pimpl" idiom in object-oriented design. Your struct can have an opaque pointer to another forward-declared struct that acts as the struct's private data. Functions that operate on the struct, taking the place of member functions, can have the full definition for the private member, and can make use of it, while other parts of the code cannot. For example:

In a header, foo.h:

  struct FooPrivate;

  struct Foo {
     /* public: */
       int x; 
       double y;
     /* private: */
       struct FooPrivate* p;
  };

  extern struct Foo* Foo_Create(); /* "constructor" */

  extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */

In the implementation, foo.c:

  struct FooPrivate {
     int z;
  };

  struct Foo* Foo_Create()
  {
     struct Foo* foo = malloc(sizeof(Foo));

     foo->p = malloc(sizeof(FooPrivate));

     foo->x = 0;
     foo->y = 0;
     foo->p->z = 0;

     return foo;
  }

  void Foo_DoWhatever(struct Foo* foo) 
  {
      foo->p->z = 4; /* Can access "private" parts of foo */
  }

In a program:

  #include "foo.h"

  int main()
  {
      struct Foo* foo = Foo_Create();

      foo->x = 100; /* Can access "public" parts of foo */
      foo->p->z = 20; /* Error! FooPrivate is not fully declared here! */

      Foo_DoWhatever(foo); /* Can call "member" function */

      return 0;
  }

Note the need to use a "constructor" function in order to allocate memory for the private data. Obviously you would need to pair this with a special "destructor" function in order to deallocate the private data properly.

Or, alternatively, if you would like your struct to have no public fields whatsoever, you could make the entire struct opaque, and just have the header be something like

  struct Foo;

  extern struct Foo* Foo_Create(); /* "constructor" */

  extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */

With the actual definition of struct Foo in foo.c, and getter and setter functions available for any properties you would like to provide direct access to.