Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partitioning struct into private and public sections?

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.

like image 744
Thomas Matthews Avatar asked Sep 29 '10 17:09

Thomas Matthews


People also ask

Can struct have public and private members?

A struct can indeed have public and private sections, member functions as well as member variables, and special functions such as constructors, destructors and operators.

Can you use private in struct?

Yes structures can have private members, you just need to use the access specifier for the same. struct Mystruct { private: m_data; }; Only difference between structure and class are: access specifier defaults to private for class and public for struct.

Are structs private or public?

Though classes defined using either keyword can have public , private and protected base classes and members, the default choice for classes defined using class is private , whilst for those defined using struct the default is public .


3 Answers

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.

like image 156
Tyler McHenry Avatar answered Oct 07 '22 14:10

Tyler McHenry


The concept sometimes used in C is

// lib.h
typedef struct {
  int publicInt;
  //...
  char * publicStr;
} Public;

Public * getPublic();
int function(Public * public);

// lib.c

typedef struct {
  Public public;
  int privateInt;
  // ...
  char * privateStr
} Private;

static Private * getPrivate();

Public * getPublic() { return (Public*) getPrivate(); }
int function(Public * public) {
  Private * private = (Private *) public;
  // ...
}

This uses the standard trick that a pointer to a struct can be interchanged with a pointer to the first element in a struct.

If you want all your fields to be private, it's even easier:

// lib2.h
typedef struct AllPrivate * Handle;
Handle getHandle();
int function2(Handle handle);

// lib2.c
struct AllPrivate { /* ... */ }

Files that #include lib2.h won't complain, since we only use struct AllPrivate *, and all pointers are the same size, so the compiler doesn't need to know the innards of struct AllPrivate.

To do a protected region, you'd just have to define

// include/public.h
struct Public { /* ... */ }
struct Public * getPublic();
int somePublicFunction(struct Public *);

// dev/include/protected.h
struct Protected { struct Public public; /* ... */ }
struct Protected * getProtected();
int someProtectedFunction(struct Protected *);

// dev/src/private.c
struct Private { struct Protected protected; /* ... * /}
struct Public * getPublic() { return (struct Public *) getPrivate(); }
struct Public * getProtected() { return (struct Protected *) getPrivate(); }
int somePublicFunction(struct Public * public) { 
  struct Private private = (struct Private *) public;
  // ...
}
int someProtectedFunction(struct Protected * protected) { 
  struct Private private = (struct Private *) protected;
  // ...
}

Then it's just a matter of making sure that dev/include isn't passed around.

like image 33
rampion Avatar answered Oct 07 '22 12:10

rampion


For data fields -- just don't use them. You can do some tricks like giving them crazy names to discourage their use, but that won't stop people. The only real way to do it is to make another private struct that is accessed by a void pointer by your library functions.

For private functions -- use file static functions. Put all of your library functions in one C file and declare the ones that you want to be private as static and don't put them in any header files.

like image 35
nategoose Avatar answered Oct 07 '22 14:10

nategoose