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 struct
s 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.
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.
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.
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 .
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With