views:

91

answers:

5

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.

+1  A: 

I feel sorry for you, because mashing different OO concepts into C is often like a square peg in a round hole. That being said, GObject has support for public and private members, but it's one of my least favourite architectures on earth. If you're not concerned with the minor performance hit, you may be able to do a simpler solution - have a secondary struct that's filled with private members, and have an anonymous pointer to that struct from the primary (public) struct.

Reinderien
I would like to stay away from library packages as this involves licensing issues and the development needs to be finished quickly.
Thomas Matthews
(a) GObject is an open-source library, though admittedly even that may present license issues; (b) the secondary private struct does not require a library.
Reinderien
+5  A: 

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.

rampion
+2  A: 

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.

nategoose
+8  A: 

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.

Tyler McHenry
+1 Thanks for reminding me about the Pimple idiom. I never considered applying it to C language.
Thomas Matthews
In C99, you might want to consider replacing the pointer to `struct FooPrivate` with a flexible array member `struct FooPrivate p[]`. This will prevent the user of the interface from accidentally making a shallow-copy of `struct Foo`, since it will make `struct Foo` an incomplete type.
caf
+1  A: 

Often by convention, a private member has an extra underscore in its name, or something like _pri appended. Or possibly a comment. This technique doesn't do compiler-enforced checking to make sure no one inappropriately accesses those fields, but serves as a warning to anyone reading a struct declaration that the contents are implementation details and they shouldn't peek or poke at them.

Another common technique is to expose your structure as an incomplete type. For example, in your header file, you might have:

struct my_struct;

void some_function(struct my_struct *);

And in the implementation, or some internal header which is not accessible to consumers of the library, you have:

struct my_struct
{
    /* Members of that struct */
};

You can also do similar tricks with void pointers, that get cast to the right place in the "private" portion of the code. This approach loses some flexibility (you can't have a stack-allocated instance of an undefined type, for example), but that may be acceptable.

If you want to have a mixture of private and public members, you can do the same thing as above, but store the private struct pointer as a member of the public one, and leave it incomplete in public consumers of the library.

Although, this introduces some indirection, which may hurt performance. There are some (generally non-portable, but will work on reasonable compilers) type-punning tricks you can use too:

struct public_struct
{
   int public_member;
   int public_member2;
   /* etc.. */
};

struct private_struct
{
   struct public_struct base_members;

   int private_member1;
   int private_member2;
};

void some_function(struct public_struct *obj)
{
   /* Hack alert! */
   struct private_struct *private = (struct private_struct*)obj;
}

This also assumes that you can't store these objects on the stack or in static storage, or get the size at compile time.

asveikau