Datahiding in C

J

Jason Curl

Hello,

I often try to implement datahiding as best as I can in C but the method
I use is typecasts. I've read in numerous posts of this newsgroup that
any typecasts (while in some cases is valid), is usually shunned upon.

I've not listed the missing headers and I've not done any error checking
for brevity in the example below. Of course one would normally check for
NULL pointers as results of malloc() and as inputs.

Is this generally acceptable, or are there better ways of implementing
datahiding (without the potential of making silly errors due to typecasts).

I try and avoid "private" headers, because they're still headers that
must be distributed and that someone can see and modify internal parameters.

mydt.h
------
typedef void mydt_handle;

mydt_handle *initdt(int value);
void termdt(mydt_handle *handle);

mydt.c
------
typedef struct mydt_handle_s {
int value;
} mydt_handle_t;

mydt_handle *initdt(int value)
{
mydt_handle_t *h;
h = malloc(sizeof(mydt_handle_t));
h->value = value;
return (mydt_handle *)h;
}

void termdt(mydt_handle *handle)
{
mydt_handle_t *h;
h = (mydt_handle_t *)handle;
printf("Value is %d\n", h->value);
free(h);
}
 
?

=?ISO-8859-1?Q?Bj=F8rn_Augestad?=

Jason said:
Hello,

I often try to implement datahiding as best as I can in C but the method
I use is typecasts. I've read in numerous posts of this newsgroup that
any typecasts (while in some cases is valid), is usually shunned upon.

I've not listed the missing headers and I've not done any error checking
for brevity in the example below. Of course one would normally check for
NULL pointers as results of malloc() and as inputs.

Is this generally acceptable, or are there better ways of implementing
datahiding (without the potential of making silly errors due to typecasts).

You almost got it right. ;-)

First of all, remember that you can declare the type in the header file
and later define it in the c file. There's no need for the mydt_handle
type.

Second, no need to cast between void* and pointers to other types.

Here's one way of doing what you want:

foo.h
------
#ifndef FOO_H
#define FOO_H

typedef struct foo_t* foo;
foo initfoo(int value);

#endif

foo.c
------

#include <stdlib.h>
#include "foo.h"

struct foo_t {
int value;
};

foo initfoo(int value)
{
foo p;

if( (p = malloc(sizeof *p)) != NULL)
p->value = value;

return p;
}
</untested code>

Some people prefer to be more explicit and just typedef struct foo_t foo
instead of typedef'ing a pointer to foo_t. That makes it clearer that
pointers are involved.

An excellent book on this subject is "C Interfaces and Implementations"
by David R. Hanson, ISBN 0-201-49841-3.

HTH
Bjørn
[snip]
 
C

CBFalconer

Jason said:
Hello,

I often try to implement datahiding as best as I can in C but the method
I use is typecasts. I've read in numerous posts of this newsgroup that
any typecasts (while in some cases is valid), is usually shunned upon.

I've not listed the missing headers and I've not done any error checking
for brevity in the example below. Of course one would normally check for
NULL pointers as results of malloc() and as inputs.

Is this generally acceptable, or are there better ways of implementing
datahiding (without the potential of making silly errors due to typecasts).

I try and avoid "private" headers, because they're still headers that
must be distributed and that someone can see and modify internal parameters.

mydt.h
------
typedef void mydt_handle;

mydt_handle *initdt(int value);
void termdt(mydt_handle *handle);

mydt.c
------
typedef struct mydt_handle_s {
int value;
} mydt_handle_t;

mydt_handle *initdt(int value)
{
mydt_handle_t *h;
h = malloc(sizeof(mydt_handle_t));
h->value = value;
return (mydt_handle *)h;
}

void termdt(mydt_handle *handle)
{
mydt_handle_t *h;
h = (mydt_handle_t *)handle;
printf("Value is %d\n", h->value);
free(h);
}

IMO the alleged type hiding is useless, better to simply use void*
and let the user see the result. In some cases you may want an
opaque incomplete type.

The code as shown is incomplete and cannot be properly criticized.
Obviously missing are #includes for stdio.h and stdlib.h, also
mydt.h. Try to post compilable code.

I think you miss the point of data-hiding. It is to ensure that
compilation modules can be freely modified and corrected without
affecting other compilation modules.
 
J

Jason Curl

CBFalconer said:
IMO the alleged type hiding is useless, better to simply use void*
and let the user see the result. In some cases you may want an
opaque incomplete type.

I tried to avoid this, as if there are multiple datatypes, it makes code
review much more difficult to determine what variable is of what datatype.

Imagine

void foo(void) {
void *i;
void *j;
void *k;

....
}

instead of

void foo(void) {
complex *i;
complex *j;
polar *k;

....
}

OTH, another poster enlightened me to use incomplete datatypes. While
I've used them before in forward declaration for linked lists, etc. I
never considered them for this purpose.

e.g. just declare the structure in the header file, and define the
structure in the source file.
The code as shown is incomplete and cannot be properly criticized.
Obviously missing are #includes for stdio.h and stdlib.h, also
mydt.h. Try to post compilable code.

Sorry - I'll do this next time - I just wanted to illustrate a coding
technique that somebody else might have a better idea on.

I think you miss the point of data-hiding. It is to ensure that
compilation modules can be freely modified and corrected without
affecting other compilation modules.

Isn't this what was effectively done by forcing the programmer not to
rely on the internal workings of 'dt'?

Thanks.
Jason.
 
C

CBFalconer

Jason said:
.... snip ...

I tried to avoid this, as if there are multiple datatypes, it makes
code review much more difficult to determine what variable is of
what datatype.

Imagine

void foo(void) {
void *i;
void *j;
void *k;
....
}

instead of

void foo(void) {
complex *i;
complex *j;
polar *k;
....
}

Why would you ever want to do that? Those declarations are already
strictly limited in scope to the foo function. Hiding is between
modules, not between functions, although limiting connectivity is
still a good thing.
 
E

E. Robert Tisdale

Jason said:
I often try to implement datahiding as best as I can in C
but the method [that] I use is typecasts.
?

I've read in numerous posts of this newsgroup that
any typecasts (while in some cases is valid) is usually [discouraged].
I've not listed the missing header [file]s
and I've not done any error checking for brevity in the example below.
Of course one would normally check for NULL pointers
as results of malloc() and as inputs.

Is this generally acceptable?
or are there better ways of implementing datahiding
(without the potential of making silly errors due to typecasts).

I try [to] avoid "private" header [file]s,
because they're still header [file]s that must be distributed
and that someone can see and modify internal parameters.

You are confused.
Data hiding is *not* about keeping secrets.
It is about preventing application programmers
from *accidently* referencing the actual data representation directly.
If you distribute source code,
application programmers can read type definitions
from source files as easily as they can from private header files.
If you are distributing only pre-compiled library archives,
you do *not* need to distribute private header files.
That's why they are called *private* header files.

It appears that what you are trying to do
is to implement an *opaque* data type
and you have done it badly.
> cat mydt.h
#ifndef GUARD_HANDLE_T
#define GUARD_HANDLE_T 1

// public declaration of [struct] handle_t
typedef struct handle_t handle_t;

handle_t* handle_t_new(int value);
void handle_t_delete(const handle_t* p);

#endif//GUARD_HANDLE_T
> cat mydt.c
#include <stdlib.h>
#include <stdio.h>
#include "mydt.h"

// private definition of struct handle_t
struct handle_t {
int value;
};

inline static // private function
handle_t* handle_t_init(handle_t* p, int value) {
p->value = value;
return p;
}

handle_t* handle_t_new(int value) {
handle_t* p = (handle_t*)malloc(sizeof(handle_t));
return handle_t_init(p, value);
}

void handle_t_delete(const handle_t* p) {
printf("Value is %d\n", p->value);
free((void*)p);
}
> gcc -Wall -std=c99 -pedantic -c mydt.c

1. The guard macro in mydt.h ensures that it is *idempotent*.
2. I have declared opaque type struct handle_t
in public header file mydt.h
3. I used typedef to define a synonym, handle_t, for struct handle_t.
4. I have declared all of the public functions
that operate on objects of type handle_t in mydt.h
5. I prefix all of the functions that operate on objects
of type handle_t with handle_t_.
6. I include the interface definition in the mydt.h header file
in the mydt.c implementation file so that the compiler
can check for consistency.
7. handle_t_delete(const handle_t*) accepts a ponter to a const object
so that I can use it to delete constants as well as variables.
 
M

Mark Piffer

CBFalconer said:
IMO the alleged type hiding is useless, better to simply use void*
and let the user see the result. In some cases you may want an
opaque incomplete type.

What would one do if the targeted platform lacks dynamic allocation?
That is, you can't malloc an opaque type from the user code but neither
would you want an unfit number of global objects in the module code?
Suppose an aggregate type like a file descriptor (on an embedded
system), which is simply too costly in terms of RAM usage to simply
have a bunch of them statically allocated on the module side. I can
think of a implementation-dependant function which returns a suitably
aligned pointer when given a char* (together with the size) like:
FILE* makeFileFromAnonBuf(char *buf,int buf_size);
// returns NULL if a FILE wouldn't fit into buf[buf_size]
// after alignment
but this has the drawback of possible failure at runtime which is quite
ugly for an embedded system, especially if the other alternative isn't
only simpler but also can't fail in this respect. It looks like a big
"HANDS OFF" sign around the type definition in the code (the module
will be distributed in source form) is the better alternative.

Mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,159
Messages
2,570,879
Members
47,414
Latest member
GayleWedel

Latest Threads

Top