By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
457,900 Members | 1,342 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 457,900 IT Pros & Developers. It's quick & easy.

About Information Hiding Design Rules

P: n/a
Hello group,

I'm trying to become familiar with the information hiding design rules, and I
have a lot (3) of questions for all you experts. AFAIK, a generic module has 2
files:

================
module.h
================
#ifndef __MODULE_HDR_INCLUDED__
#define __MODULE_HDR_INCLUDED__

typedef struct myadt_t {
void * pvt;
} myadt_t;

/* Define the public interface to myadt_t objects */

int myadt_create(myadt_t *);
int myadt_destroy(myadt_t *);
int myadt_get_status(myadt_t *, int *);

#endif

================
module.c
================
#include <stdlib.h>
#include <stdio.h>
#include "module.h"

typedef struct private_myadt_t {
int x;
int y;
} private_myadt_t;

int
myadt_create(myadt_t * ptr)
{
private_myadt_t * pvt;
ptr->pvt = malloc (sizeof(private_myadt_t));
if (ptr->pvt == NULL) {
printf("Memory couldn't be allocated\n");
return 0;
}
pvt = (private_myadt_t *) ptr->pvt;
pvt->x = 10;
pvt->y = 10;
return 1;
}

int
myadt_destroy(myadt_t * ptr)
{
free(ptr->pvt);
return 1;
}

int
myadt_get_status(myadt_t * ptr, int * status)
{
private_myadt_t * pvt;
if (!ptr->pvt) {
printf("Working with uninitialized data struct\n");
return 0;
}
pvt = (private_myadt_t *) ptr->pvt;
*status = pvt->x + pvt->y;
return 1;
}

and it can be used as follows in the main file:

==============
main.c
==============
#include <stdio.h>
#include "module.h"

int
main()
{
myadt_t ADT;
int status;
myadt_create(&ADT);
myadt_get_status(&ADT, &status);
myadt_destroy(&ADT);
printf("status: %d\n", status);
return 1;
}

My question are the following:

1) Is this a good way to proceed or is there way that is universally
recognized as *THE* way to use the information hiding technique in ANSI-C ?

2) With the code above, I can't prevent the use of uninitialized data
structures. That is, if I changed the main() to

#include <stdio.h>
#include "module.h"
int
main()
{
myadt_t ADT;
int status;
myadt_get_status(&ADT, &status); /* <-- PROBLEM HERE! */
printf("status: %d\n", status);
return 1;
}

I would expect that the message "Working with uninitialized data struct\n"
be displayed on the screen, but this does not occur because ptr->pvt is not
initialized to NULL. So, which test condition could I use in function
myadt_get_status() to prevent the use of uninitialized data struct?

3) In microcontroller programming, it often happens that the functions
malloc() and free() are not available. Is there a way (also a non-standard one)
to use information hiding design rules without using these functions?

Best Regards,

matt
Jul 1 '08 #1
Share this Question
Share on Google+
27 Replies


P: n/a
matt wrote:
Hello group,

I'm trying to become familiar with the information hiding design
rules, and I have a lot (3) of questions for all you experts. AFAIK, a
generic module has 2 files:

================
module.h
================
#ifndef __MODULE_HDR_INCLUDED__
#define __MODULE_HDR_INCLUDED__
Poor choice of macro name: Identifiers that start with two
underscores (or with an underscore and a capital letter) are
"reserved for any use," meaning that you shouldn't use them.
typedef struct myadt_t {
void * pvt;
} myadt_t;

/* Define the public interface to myadt_t objects */

int myadt_create(myadt_t *);
int myadt_destroy(myadt_t *);
int myadt_get_status(myadt_t *, int *);

#endif
[...]
>
My question are the following:

1) Is this a good way to proceed or is there way that is universally
recognized as *THE* way to use the information hiding technique in ANSI-C ?
"There are nine and sixty ways of constructing tribal lays,
And every single one of them is right!"

That said, the use of the "wrapper" struct is fairly unusual
and doesn't seem to add much. An approach more commonly seen is

/* myadt.h */
typedef struct myadt_t /* no content */ myadt_t;
myadt_t * myadt_create(void);
int myadt_destroy(myadt_t *);
int myadt_get_status(myadt_t *, int *);

In this formulation, myadt_t is an "incomplete type:" the
compiler knows its name and knows how to manipulate pointers to
it, but doesn't know how big it is nor what it looks like on the
inside. The details of what the struct actually contains remain
hidden in your implementation module:

/* myadt.c */
#include "myadt.h"
struct myadt_t {
int x;
int y;
};

.... so your implementation can use them, but the clients can't.
2) With the code above, I can't prevent the use of uninitialized
data structures. That is, if I changed the main() to

#include <stdio.h>
#include "module.h"
int
main()
{
myadt_t ADT;
int status;
myadt_get_status(&ADT, &status); /* <-- PROBLEM HERE! */
printf("status: %d\n", status);
return 1;
}

I would expect that the message "Working with uninitialized data
struct\n" be displayed on the screen, but this does not occur because
ptr->pvt is not initialized to NULL. So, which test condition could I
use in function myadt_get_status() to prevent the use of uninitialized
data struct?
There is no way to examine an object and decide whether it
has or hasn't been initialized. If it hasn't been given a value,
then the attempt to pluck a value from it produces undefined
behavior. Even if you dodge the U.B. by examining the object's
individual bytes, the "garbage" in the object might resemble what
you'd see in a properly initialized object.

However, this problem mostly goes away if you adopt the more
usual arrangement. Since myadt_create() is the source of all
myadt_t instances, making sure that myadt_create() initializes
the new instances properly solves the problem. Your client has
no way to create a myadt_t instance on its own, so your client
has no opportunity to forget to initialize one.
3) In microcontroller programming, it often happens that the
functions malloc() and free() are not available. Is there a way (also a
non-standard one) to use information hiding design rules without using
these functions?
There are several things you could do. At one extreme, you
could write your own malloc() et al., perhaps backed by the memory
of one big statically-allocated array. At the other, you could
have a statically-allocated array of myadt_t instances, and could
keep track of which are currently in use and which are available.

--
Er*********@sun.com
Jul 1 '08 #2

P: n/a
On 07/01/2008 07:16 PM, Eric Sosman wrote:
matt wrote:
> 2) With the code above, I can't prevent the use of uninitialized
data structures. That is, if I changed the main() to

#include <stdio.h>
#include "module.h"
int
main()
{
myadt_t ADT;
int status;
myadt_get_status(&ADT, &status); /* <-- PROBLEM HERE! */
printf("status: %d\n", status);
return 1;
}

I would expect that the message "Working with uninitialized data
struct\n" be displayed on the screen, but this does not occur because
ptr->pvt is not initialized to NULL. So, which test condition could I
use in function myadt_get_status() to prevent the use of uninitialized
data struct?

There is no way to examine an object and decide whether it
has or hasn't been initialized.
However there are programs like valgrind that can recognize whether you are
using uninitialised memory. Infact, if I run the example program with valgrind I
obtain

matt@isaac:~/work/c/information-hiding$ valgrind ./exe

[...]

==4581==
==4581== Use of uninitialised value of size 4
==4581== at 0x804849E: myadt_get_status (in
/home/matt/work/c/information-hiding/exe)
==4581== by 0x80484DE: main (in /home/matt/work/c/information-hiding/exe)
==4581==
==4581== Use of uninitialised value of size 4
==4581== at 0x80484A3: myadt_get_status (in
/home/matt/work/c/information-hiding/exe)
==4581== by 0x80484DE: main (in /home/matt/work/c/information-hiding/exe)
status: 953015467

[...]
However, this problem mostly goes away if you adopt the more
usual arrangement. Since myadt_create() is the source of all
myadt_t instances, making sure that myadt_create() initializes
the new instances properly solves the problem. Your client has
no way to create a myadt_t instance on its own, so your client
has no opportunity to forget to initialize one.
What do you mean with "properly"? Adopting this solution should suffice

myadt_t *
myadt_create(void)
{
myadt_t * ptr;
ptr = malloc (sizeof(myadt_t));
if (ptr == NULL) {
printf("Memory couldn't be allocated\n");
return 0;
}
ptr->x = 10;
ptr->y = 10;
return ptr;
}

.... isn't it?

Thanks for your time.

matt
Jul 1 '08 #3

P: n/a
matt wrote:
On 07/01/2008 07:16 PM, Eric Sosman wrote:
>>
There is no way to examine an object and decide whether it
has or hasn't been initialized.

However there are programs like valgrind that can recognize whether you
are using uninitialised memory.
True, but valgrind is not part of C and relies on features
that are not part of C. Have you tried it on Windows lately?

Also, I'm under the impression that these programs do not
detect uninitialized memory by examining the contents, but by
keeping track of initializations and stores in a separate data
structure maintained by instrumentation inserted in the code.
(I'm not sure about the mechanisms used by valgrind specifically,
but that's how at least one other product does the job.)
> However, this problem mostly goes away if you adopt the more
usual arrangement. Since myadt_create() is the source of all
myadt_t instances, making sure that myadt_create() initializes
the new instances properly solves the problem. Your client has
no way to create a myadt_t instance on its own, so your client
has no opportunity to forget to initialize one.

What do you mean with "properly"? Adopting this solution should suffice

myadt_t *
myadt_create(void)
{
myadt_t * ptr;
ptr = malloc (sizeof(myadt_t));
Consider `ptr = malloc(sizeof *ptr);' as a safer alternative,
"safer" because there's less chance of an accidental mismatch.
Many's the time I've seen errors like

msghead_t *p = malloc(sizeof(msghead_t));
msgbody_t *q = malloc(sizeof(msghead_t));

.... which the suggested idiom almost always avoids.
if (ptr == NULL) {
printf("Memory couldn't be allocated\n");
Consider writing error messages to stderr instead of to
stdout. Still better, consider writing no message at all, but
just returning the NULL and letting the client decide what to
do about the matter; the client has broader knowledge of the
context than you do.
return 0;
}
ptr->x = 10;
ptr->y = 10;
return ptr;
}

... isn't it?
Yes, this is what I meant: myadt_create() either returns
NULL or returns a pointer to a myadt_t instance that has been
initialized and is ready for use. There is no possibility that
the program can create a myadt_t instance in any other way,
hence no possibility of an uninitialized instance.

--
Er*********@sun.com
Jul 1 '08 #4

P: n/a
Eric Sosman wrote:
matt wrote:
>Hello group,

I'm trying to become familiar with the information hiding design
rules, and I have a lot (3) of questions for all you experts. AFAIK, a
generic module has 2 files:

================
module.h
================
#ifndef __MODULE_HDR_INCLUDED__
#define __MODULE_HDR_INCLUDED__

Poor choice of macro name: Identifiers that start with two
underscores (or with an underscore and a capital letter) are
"reserved for any use," meaning that you shouldn't use them.
>typedef struct myadt_t {
void * pvt;
} myadt_t;

/* Define the public interface to myadt_t objects */

int myadt_create(myadt_t *);
int myadt_destroy(myadt_t *);
int myadt_get_status(myadt_t *, int *);

#endif
[...]
>>
My question are the following:

1) Is this a good way to proceed or is there way that is
universally recognized as *THE* way to use the information hiding
technique in ANSI-C ?
> 3) In microcontroller programming, it often happens that the
functions malloc() and free() are not available. Is there a way (also
a non-standard one) to use information hiding design rules without
using these functions?
One simple technique that has been around for a long time is to statically
allocate the maximum number of items within your module. When you call
your create function, it allocates the next available entry in the array
and returns the index.

The disadvantage of that technique is that you must determine and allocate
the maximum number of items.

Another way is to expose the structure and let the caller statically
allocate it, then pass a pointer to the routines.

--
Thad
Jul 2 '08 #5

P: n/a
On Jul 1, 9:38 pm, matt <n...@none.comwrote:
3) In microcontroller programming, it often happens that the functions
malloc() and free() are not available. Is there a way (also a non-standard one)
to use information hiding design rules without using these functions?
You can have a linked list implemented on arrays. Something like:
#define MAX_SIZE ..
typedef struct _node {
Your_ADT data;
int next;
}node;
node store[MAX_SIZE];
static int free = 0; /* points to the current free node */

void initList(void) {
/* initialize the list */
for (i = 0; i < MAX_SIZE - 1; i++) {
store[i].next = i + 1;
}
store[MAX_SIZE - 1] = -1; /* stating that this is the end of the
list

Initially in your link list, every element is linked with the next
element. You will have to keep track of allocated and freed elements.
This is a crude approach but works. On the other hand, you can write
your own malloc() sort of thing. The implementation file will
internally maintain a statically allocated array of your ADT.
Individual elements of the array can be returned on successive calls.
On the other hand, if you wish to write generic malloc(), then
probably you can have a large static char array.
Jul 2 '08 #6

P: n/a
On 07/02/2008 08:34 AM, rahul wrote:
On Jul 1, 9:38 pm, matt <n...@none.comwrote:
> 3) In microcontroller programming, it often happens that the functions
malloc() and free() are not available. Is there a way (also a non-standard one)
to use information hiding design rules without using these functions?
You can have a linked list implemented on arrays. Something like:
#define MAX_SIZE ..
typedef struct _node {
Your_ADT data;
int next;
}node;
node store[MAX_SIZE];
static int free = 0; /* points to the current free node */

void initList(void) {
/* initialize the list */
for (i = 0; i < MAX_SIZE - 1; i++) {
store[i].next = i + 1;
}
store[MAX_SIZE - 1] = -1; /* stating that this is the end of the
list

Initially in your link list, every element is linked with the next
element. You will have to keep track of allocated and freed elements.
This is a crude approach but works. On the other hand, you can write
your own malloc() sort of thing. The implementation file will
internally maintain a statically allocated array of your ADT.
Individual elements of the array can be returned on successive calls.
On the other hand, if you wish to write generic malloc(), then
probably you can have a large static char array.
Yes, this could be a solution. However, I'm wondering if it could involve
performance issues in real-time applications. If this is the case, designing the
ADT as an opaque type by "asking" that users don't write code that depends on
how the ADT is implemented could still be considered a good solution?

In other words, how often are Information Hiding Design Rules used in
microcontrollers programming for real-time applications?
Jul 2 '08 #7

P: n/a
matt wrote:
In other words, how often are Information Hiding Design Rules used in
microcontrollers programming for real-time applications?
Not often, in my experience. Part of the reason is that most applications
are written by fewer people, with a shared set of rules and priority of
goals that tend to preclude using extra code or execution time resources to
enforce abstract data typing or little benefit.

My typical practice is to define the full data structure in the module
header file, then document, with comments, that all operations with the foo
data should be done by calling the foo module functions. If I expect the
module to only need one data structure (i.e., THE serial output buffer), I
will typically code it as static memory in the foo module, instead of
allocating an instance.

--
Thad
Jul 2 '08 #8

P: n/a
In article <1214932566.991337@news1nwk>, Eric Sosman wrote:
There is no way to examine an object and decide whether it
has or hasn't been initialized.
You could keep track of which pointers have been initialized. Then
if you get one you haven't seen yet, you can do the right thing.

I'm not suggesting that this is a *good* design, merely that it's
possible.
However, this problem mostly goes away if you adopt the more
usual arrangement. Since myadt_create() is the source of all
myadt_t instances, making sure that myadt_create() initializes
the new instances properly solves the problem.
Of course this pretty much requires malloc...

Which you address:
There are several things you could do. At one extreme, you
could write your own malloc() et al., perhaps backed by the memory
of one big statically-allocated array. At the other, you could
have a statically-allocated array of myadt_t instances, and could
keep track of which are currently in use and which are available.
This latter could be combined with just keeping track of initialized
pointers. Instead of statically allocating (say) 10 myadt_t objects,
you could statically allocate an array of 10 pointers to such
and keep your initialized pointer list in there.

Personally, I'm not a fan of information hiding. But if it really
wants to happen, some thought needs to be given to the expected
usage pattern before the exact interface can be decided on.

--
nw@hydaspes.if.org
Jul 2 '08 #9

P: n/a
Nathan Wagner wrote:
In article <1214932566.991337@news1nwk>, Eric Sosman wrote:
> There is no way to examine an object and decide whether it
has or hasn't been initialized.

You could keep track of which pointers have been initialized. Then
if you get one you haven't seen yet, you can do the right thing.

I'm not suggesting that this is a *good* design, merely that it's
possible.
I repeat: "There is no way TO EXAMINE AN OBJECT and decide
whether it has or hasn't been initialized." It may be possible
to decide by means of ancillary data, instrumentation, and so on
(none of which would be applicable to the O.P.'s situation), but
mere examination of the object is not sufficient.

--
Er*********@sun.com
Jul 2 '08 #10

P: n/a
On Tue, 01 Jul 2008 14:58:47 -0400, Eric Sosman
<Er*********@sun.comwrote:
>matt wrote:
[snip much excellent advice]
>
Consider writing error messages to stderr instead of to
stdout. Still better, consider writing no message at all, but
just returning the NULL and letting the client decide what to
do about the matter; the client has broader knowledge of the
context than you do.
Here I will slightly disagree. I agree with returning the NULL;
the client needs to know if there was a failure. Howver it is
also a good thing to know after the fact that there was a failure
and what kind of failure it was. Simply returning NULL discards
information about the nature of the failure.

Consider writing information messages to a log file instead of
either stdout or stderr. Better, consider incorporating an error
management and reporting module and let it take care of writing
the message to the log file. Thus, instead of,

fprintf(logfile,"Couldn't allocate space for a fooby");
use
errmsg("Couldn't allocate space for a fooby");

It's also good IMO to have an error exit routine that records
useful information to the log file.

As a refinement that I find quite useful, consider adding the
file name and line number where the message was generated. One
way to do this is to add the following code to the error
reporting include file

#include <assert.h>

#define LNELOC_HACK_STR(x) LNELOC_HACK_VAL(x)
#define LNELOC_HACK_VAL(x) #x
#define LNELOC_HACK_CAT(x,y) x##y
#define LINELOC __FILE__ ":" LNELOC_HACK_STR(__LINE__)

and use LINELOC as an argument for errmsg, e.g.,

errmsg(LINELOC,"Couldn't allocate space for a fooby");

or, if you prefer, hide LINELOC in a macro, e.g.,

ERRMSG("Couldn't allocate space for a fooby");

Richard Harter, cr*@tiac.net
http://home.tiac.net/~cri, http://www.varinoma.com
Save the Earth now!!
It's the only planet with chocolate.
Jul 2 '08 #11

P: n/a
Richard Harter wrote:
On Tue, 01 Jul 2008 14:58:47 -0400, Eric Sosman
<Er*********@sun.comwrote:
>matt wrote:
[snip much excellent advice]
> Consider writing error messages to stderr instead of to
stdout. Still better, consider writing no message at all, but
just returning the NULL and letting the client decide what to
do about the matter; the client has broader knowledge of the
context than you do.

Here I will slightly disagree. I agree with returning the NULL;
the client needs to know if there was a failure. Howver it is
also a good thing to know after the fact that there was a failure
and what kind of failure it was. Simply returning NULL discards
information about the nature of the failure.
[...]
Well, the verb I used was "consider." One-size-fits-all
frameworks for error handling just don't hold up well, and the
strategy that works well in one setting is not necessarily good
in the next. I would never maintain that silently returning an
error indicator is The One True Way -- just a way that fits
fairly well into a lot of other frameworks, and is thus worthy
of consideration.

The O.P.'s example was particularly interesting (or maybe
"startling") because he's apparently interested in embedded
systems. He asked about getting along without malloc(), and
it seems likely that he must also get along without <stdio.h>.
That's likely to have an influence on his error-reporting ...
When was the last time you read your toaster oven's stderr?

(On another forum, someone once reported seeing one of those
computer-controlled block-letter highway signs advising the
passing motorists to "Press F1 to continue" ...)

--
Er*********@sun.com
Jul 2 '08 #12

P: n/a
On Wed, 02 Jul 2008 12:58:55 -0400, Eric Sosman
<Er*********@sun.comwrote:
>Richard Harter wrote:
>On Tue, 01 Jul 2008 14:58:47 -0400, Eric Sosman
<Er*********@sun.comwrote:
>>matt wrote:
[snip much excellent advice]
>> Consider writing error messages to stderr instead of to
stdout. Still better, consider writing no message at all, but
just returning the NULL and letting the client decide what to
do about the matter; the client has broader knowledge of the
context than you do.

Here I will slightly disagree. I agree with returning the NULL;
the client needs to know if there was a failure. Howver it is
also a good thing to know after the fact that there was a failure
and what kind of failure it was. Simply returning NULL discards
information about the nature of the failure.
[...]

Well, the verb I used was "consider." One-size-fits-all
frameworks for error handling just don't hold up well, and the
strategy that works well in one setting is not necessarily good
in the next. I would never maintain that silently returning an
error indicator is The One True Way -- just a way that fits
fairly well into a lot of other frameworks, and is thus worthy
of consideration.
"Disagree" was not, perhaps, my happiest choice of wording. :-)

My view, for what it is worth, is that when programming an
application it is fairly important to decide on the error
handling strategy in advance.
>
The O.P.'s example was particularly interesting (or maybe
"startling") because he's apparently interested in embedded
systems. He asked about getting along without malloc(), and
it seems likely that he must also get along without <stdio.h>.
That's likely to have an influence on his error-reporting ...
When was the last time you read your toaster oven's stderr?
Isn't your toaster on the internet?

Embedded systems are a good illustration of the "decide on error
handling strategy in advance" principle. The code has to take
care of itself when it breaks and you have to decide what it
means to "take care of itself" and include that in the code.
Apply apple pie, motherhood, and the flag here.

>
(On another forum, someone once reported seeing one of those
computer-controlled block-letter highway signs advising the
passing motorists to "Press F1 to continue" ...)
Chortle.
Richard Harter, cr*@tiac.net
http://home.tiac.net/~cri, http://www.varinoma.com
Save the Earth now!!
It's the only planet with chocolate.
Jul 2 '08 #13

P: n/a
On 2 Jul 2008 at 16:33, Richard Harter wrote:
Simply returning NULL discards information about the nature of the
failure.

Consider writing information messages to a log file instead of either
stdout or stderr.
Surely a simpler solution for a library function is to set errno?

Jul 2 '08 #14

P: n/a
On Jul 3, 3:21 am, Antoninus Twink <nos...@nospam.invalidwrote:
On 2 Jul 2008 at 16:33, Richard Harter wrote:
Simply returning NULL discards information about the nature of the
failure.
Consider writing information messages to a log file instead of either
stdout or stderr.

Surely a simpler solution for a library function is to set errno?
Simpler and prevalent in Linux/Unix environment; it is more of a
personal preference but many a times I return defined constants.
Something like:
#define NODE_NOT_FOUND -10
#define EMPTY_LIST -11

int search(int i) {
/* if list empty */
return EMPTY_LIST;
/* if not found */
return NODE_NOT_FOUND;
}

But of course, errno is a better choice in certain scenarios, like
returning NULL and setting the errno on error; otherwise returning the
valid pointer.

Jul 3 '08 #15

P: n/a
On 07/01/2008 08:58 PM, Eric Sosman wrote:
matt wrote:
>On 07/01/2008 07:16 PM, Eric Sosman wrote:
>> However, this problem mostly goes away if you adopt the more
usual arrangement. Since myadt_create() is the source of all
myadt_t instances, making sure that myadt_create() initializes
the new instances properly solves the problem. Your client has
no way to create a myadt_t instance on its own, so your client
has no opportunity to forget to initialize one.
You are right. It's unlikely the client to forget to initialize an instance
of myadt_t...
>>
What do you mean with "properly"? Adopting this solution should suffice

myadt_t *
myadt_create(void)
{
myadt_t * ptr;
ptr = malloc (sizeof(myadt_t));

Consider `ptr = malloc(sizeof *ptr);' as a safer alternative,
"safer" because there's less chance of an accidental mismatch.
Many's the time I've seen errors like

msghead_t *p = malloc(sizeof(msghead_t));
msgbody_t *q = malloc(sizeof(msghead_t));

... which the suggested idiom almost always avoids.
> if (ptr == NULL) {
printf("Memory couldn't be allocated\n");

Consider writing error messages to stderr instead of to
stdout. Still better, consider writing no message at all, but
just returning the NULL and letting the client decide what to
do about the matter; the client has broader knowledge of the
context than you do.
> return 0;
}
ptr->x = 10;
ptr->y = 10;
return ptr;
}

... isn't it?

Yes, this is what I meant: myadt_create() either returns
NULL or returns a pointer to a myadt_t instance that has been
initialized and is ready for use. There is no possibility that
the program can create a myadt_t instance in any other way,
hence no possibility of an uninitialized instance.
but, hey, errare humanum est! Please consider this main() program:

#include <stdio.h>
#include "module.h"
int
main()
{
myadt_t * ADT;
int status;

/*
* a lot of work here... (1)
*/

myadt_get_status(ADT, &status);
printf("status: %d\n", status);
return 1;
}

Here, being a very absent-minded man :), because of (1) I forgot to
initialize an instance of myadt_t. As result I obtain:

matt@isaac:~/work/c/information-hiding$ ./exe
status: 953015467

So, I wonder what do you think about the following solution:
============
module.c
============
....
struct myadt_t {
int x;
int y;
int uninitialised_data;
};
....

myadt_t *
myadt_create(void)
{
...
ptr->uninitialised_data = 0;
return ptr;
}
....

int
myadt_get_status(myadt_t * ptr, int * status)
{
if (ptr->uninitialised_data) {
printf("Working with uninitialized data struct\n");
return 0;
}
...
return 1;
}

In practice, IMHO, it's highly unlikely that the pointer ADT (in main())
contains the address of a memory location in which the integer corresponding to
"int uninitialized_data" has all its 32 bits set to zero. Therefore, the test

if (ptr->uninitialised_data) ...

always returns true, except after the function myadt_create() has been
called. Indeed, running the program I get:

matt@isaac:~/work/c/information-hiding$ ./exe
Working with uninitialized data struct

Would this solution be so awful?
Jul 3 '08 #16

P: n/a
To the benefit of the OP, I would recommend the book:

C Interfaces and Implementations: Techniques for Creating Reusable
Software
by David R. Hanson - Princeton University
Publisher: Addison Wesley Professional
Pub Date: August 20, 1996
Print ISBN-10: 0-201-49841-3
Print ISBN-13: 978-0-201-49841-7
eText ISBN-10: 0-321-56280-1
eText ISBN-13: 978-0-321-56280-7
Pages: 544

It has sections on well established idioms for ADT's: atoms, lists,
etc...

We have a printed and online version of the book at work and it has
proven itself worthy of recommendation.
Jul 3 '08 #17

P: n/a
matt wrote:
On 07/01/2008 08:58 PM, Eric Sosman wrote:
>[...]
Yes, this is what I meant: myadt_create() either returns
NULL or returns a pointer to a myadt_t instance that has been
initialized and is ready for use. There is no possibility that
the program can create a myadt_t instance in any other way,
hence no possibility of an uninitialized instance.

but, hey, errare humanum est! Please consider this main() program:

#include <stdio.h>
#include "module.h"
int
main()
{
myadt_t * ADT;
int status;

/*
* a lot of work here... (1)
*/

myadt_get_status(ADT, &status);
printf("status: %d\n", status);
return 1;
}

Here, being a very absent-minded man :), because of (1) I forgot to
initialize an instance of myadt_t. As result I obtain:

matt@isaac:~/work/c/information-hiding$ ./exe
status: 953015467
This program does not create an uninitialized myadt_t
instance. In fact, it creates no myadt_t instances at all.
It contains a different error involving the failure to
initialize a myadt_t* variable (an error some compilers
will detect and warn about, by the way).

C lacks the "read programmer's mind" construct, and can't
prevent you from failing to initialize a pointer, or pointing
it at garbage, or dumping garbage into a pointed-to object. If
you're looking for a magical construct that forestalls all kinds
of mistakes, C's not the place to look.
So, I wonder what do you think about the following solution:
[... add a field that's zero in "correct" instances ...]

In practice, IMHO, it's highly unlikely that the pointer ADT (in
main()) contains the address of a memory location in which the integer
corresponding to "int uninitialized_data" has all its 32 bits set to
zero. Therefore, the test

if (ptr->uninitialised_data) ...

always returns true, except after the function myadt_create() has been
called. Indeed, running the program I get:

matt@isaac:~/work/c/information-hiding$ ./exe
Working with uninitialized data struct

Would this solution be so awful?
Your experience differs from mine, I guess. It's seemed to
me that an uninitialized variable is a little more likely to
hold all-bits-zero than anything else -- it's far from certain
to hold zeroes, but since many operating systems will zero out
a fresh memory page when first handing it to a program, zeroes
are by no means uncommon.

A variant that's sometimes used is to choose some "unlikely"
value as the signature for a properly-initialized struct. Zero
is too likely, so is minus one (often formed when a loop counts
downward to zero), and so are smallish positive integers. On
many systems, pointers to things wider than char often look like
even numbers when (mis)interpreted as integers, so using an odd
value may be a good idea. Put it all together and a reasonable
choice for a signature value would be large, odd, and negative;
add a touch of whimsy and you get things like 0xDEADBEEF (which
would be a poor choice because someone's probably used it already).

Still, you've got to be aware that no signature value is
100% immune from confusion. In fact, stuffing a known value
into properly-initialized structs may actually disguise an
uninitialized struct that later just happens to occupy the same
memory locations.

--
Eric Sosman
es*****@ieee-dot-org.invalid
Jul 3 '08 #18

P: n/a
On Jul 1, 10:16*am, Eric Sosman <Eric.Sos...@sun.comwrote:
matt wrote:
Hello group,
* I'm trying to become familiar with the information hiding design
rules, and I have a lot (3) of questions for all you experts. AFAIK, a
generic module has 2 files:
================
* * module.h
================
#ifndef __MODULE_HDR_INCLUDED__
#define __MODULE_HDR_INCLUDED__

* * *Poor choice of macro name: Identifiers that start with two
underscores (or with an underscore and a capital letter) are
"reserved for any use," meaning that you shouldn't use them.


typedef struct myadt_t {
* * void * pvt;
} myadt_t;
/* Define the public interface to myadt_t objects */
int myadt_create(myadt_t *);
int myadt_destroy(myadt_t *);
int myadt_get_status(myadt_t *, int *);
#endif
[...]
* My question are the following:
* * 1) Is this a good way to proceed or is there way that is universally
recognized as *THE* way to use the information hiding technique in ANSI-C ?

* * *"There are nine and sixty ways of constructing tribal lays,
* * * And every single one of them is right!"

* * *That said, the use of the "wrapper" struct is fairly unusual
and doesn't seem to add much. *An approach more commonly seen is

* * * * /* myadt.h */
* * * * typedef struct myadt_t /* no content */ myadt_t;
* * * * myadt_t * myadt_create(void);
* * * * int myadt_destroy(myadt_t *);
* * * * int myadt_get_status(myadt_t *, int *);

* * *In this formulation, myadt_t is an "incomplete type:" the
compiler knows its name and knows how to manipulate pointers to
it, but doesn't know how big it is nor what it looks like on the
inside. *The details of what the struct actually contains remain
hidden in your implementation module:

* * * * /* myadt.c */
* * * * #include "myadt.h"
* * * * struct myadt_t {
* * * * * * int x;
* * * * * * int y;
* * * * };

... so your implementation can use them, but the clients can't.


* * 2) With the code above, I can't prevent the use of uninitialized
data structures. That is, if I changed the main() to
#include <stdio.h>
#include "module.h"
int
main()
{
* * myadt_t ADT;
* * int status;
* * myadt_get_status(&ADT, &status); /* <-- PROBLEM HERE! */
* * printf("status: %d\n", status);
* * return 1;
}
* * I would expect that the message "Working with uninitialized data
struct\n" be displayed on the screen, but this does not occur because
ptr->pvt is not initialized to NULL. So, which test condition could I
use in function myadt_get_status() to prevent the use of uninitialized
data struct?

* * *There is no way to examine an object and decide whether it
has or hasn't been initialized. *If it hasn't been given a value,
then the attempt to pluck a value from it produces undefined
behavior. *Even if you dodge the U.B. by examining the object's
individual bytes, the "garbage" in the object might resemble what
you'd see in a properly initialized object.
Can you or someone else please explain to me how examining an object
to see whether it has or hasn't been initialized produces undefined
behavior.
Chad
Jul 3 '08 #19

P: n/a
Chad wrote:
On Jul 1, 10:16 am, Eric Sosman <Eric.Sos...@sun.comwrote:
>[...]
There is no way to examine an object and decide whether it
has or hasn't been initialized. If it hasn't been given a value,
then the attempt to pluck a value from it produces undefined
behavior. Even if you dodge the U.B. by examining the object's
individual bytes, the "garbage" in the object might resemble what
you'd see in a properly initialized object.
Can you or someone else please explain to me how examining an object
to see whether it has or hasn't been initialized produces undefined
behavior.
An uninitialized object with automatic storage duration
has an indeterminate initial value (6.2.4p5, 6.7.8p10, 6.8p3).
So, too, does any object in memory acquired from malloc() or
realloc() but not yet stored to (7.20.3.3p2, 7.20.3.4p2).

An indeterminate value can be a trap representation (3.17.2).
Taken along with the above, this means an uninitialized object
may hold a trap representation.

Reading a trap representation via an lvalue that is not
of character type -- hence the mention of accessing the bytes
individually -- produces undefined behavior (6.2.6.1p5).

--
Eric Sosman
es*****@ieee-dot-org.invalid
Jul 3 '08 #20

P: n/a
On Jul 3, 6:13 am, Eric Sosman <esos...@ieee-dot-org.invalidwrote:
Chad wrote:
On Jul 1, 10:16 am, Eric Sosman <Eric.Sos...@sun.comwrote:
[...]
There is no way to examine an object and decide whether it
has or hasn't been initialized. If it hasn't been given a value,
then the attempt to pluck a value from it produces undefined
behavior. Even if you dodge the U.B. by examining the object's
individual bytes, the "garbage" in the object might resemble what
you'd see in a properly initialized object.
Can you or someone else please explain to me how examining an object
to see whether it has or hasn't been initialized produces undefined
behavior.

An uninitialized object with automatic storage duration
has an indeterminate initial value (6.2.4p5, 6.7.8p10, 6.8p3).
So, too, does any object in memory acquired from malloc() or
realloc() but not yet stored to (7.20.3.3p2, 7.20.3.4p2).

An indeterminate value can be a trap representation (3.17.2).
Taken along with the above, this means an uninitialized object
may hold a trap representation.

Reading a trap representation via an lvalue that is not
of character type -- hence the mention of accessing the bytes
individually -- produces undefined behavior (6.2.6.1p5).

--
I think I might be getting in over my head on the following question.
The OP had

int
myadt_get_status(myadt_t * ptr, int * status)
{
private_myadt_t * pvt;
if (!ptr->pvt) {
printf("Working with uninitialized data struct\n");
return 0;
}
pvt = (private_myadt_t *) ptr->pvt;
*status = pvt->x + pvt->y;
return 1;

}
I thought that !ptr->pvt was testing the the value associated with the
object and not the object itself.

Chad
Jul 3 '08 #21

P: n/a
Chad wrote:
>
I think I might be getting in over my head on the following question.
The OP had

int
myadt_get_status(myadt_t * ptr, int * status)
{
private_myadt_t * pvt;
if (!ptr->pvt) {
printf("Working with uninitialized data struct\n");
return 0;
}
pvt = (private_myadt_t *) ptr->pvt;
*status = pvt->x + pvt->y;
return 1;

}

I thought that !ptr->pvt was testing the the value associated with the
object and not the object itself.
There's more than one "the object" in play here. "The object"
whose initialized/uninitialized state the O.P. wants to test is
the myadt_t instance that is pointed to by the first parameter,
that is, *ptr. His test consists of examining the pvt element of
that struct, ptr->pvt -- which is another "the object," embedded
inside the first one. If the struct is not initialized, then
neither are any of its component elements, so "the object" ptr->pvt
is uninitialized. Trying to fetch a value from the uninitialized
object ptr->pvt produces undefined behavior. (On most machines
these days, the U.B. amounts to retrieving some "garbage value"
rather than to producing a trap or making demons fly out of your
nose. Even so, the test is unreliable: As written, *any* non-zero
garbage will be accepted as meaning "initialized.")

Yet another "the object" is the parameter itself, ptr, and it
would also be U.B. to use the value of ptr if the variable had
not been initialized. (Likely outcome: ptr "points to a random
location," which may or may not be accessible and whose contents
are unreliable even if they exist.) In one sense we can argue that
ptr cannot possibly be uninitialized, because it's initialized from
the corresponding argument value provided by the call: A function
call initializes the function's parameters from the provided argument
values. Still, the initialization could be invalid: Perhaps the
argument expression itself relied on uninitialized variables, or
maybe the caller managed to call myadt_get_status() just like that,
with no arguments at all, or maybe the first argument was a void*
pointer that didn't point at a myadt_t instance. So even though
in one sense ptr "must" be initialized, it may still be "effectively
uninitialized" in an erroneous program.

The moral (if there still is one) is that correctness is a
global property of a program, not purely a local property of an
isolated section of code. Using local sanity checks of various
kinds is a Good Thing, but is not 100% guaranteed effective.

--
Er*********@sun.com

Jul 3 '08 #22

P: n/a
On 2 Jul, 14:25, Nathan Wagner <n...@hydaspes.if.orgwrote:
Personally, I'm not a fan of information hiding. *
nor me. I just manipulate the quarks directly.

--
Nick Keighley
Jul 3 '08 #23

P: n/a
On 3 Jul, 13:57, Chad <cdal...@gmail.comwrote:
Can you or someone else please explain to me how examining an object
to see whether it has or hasn't been initialized produces undefined
behavior.
from just a couple of days ago
http://blogs.msdn.com/oldnewthing/ar...2/8679191.aspx

"Uninitialized floating point variables can be deadly"

--
Nick Keighley
Jul 3 '08 #24

P: n/a
On 2 Jul, 13:52, Thad Smith <ThadSm...@acm.orgwrote:
matt wrote:
In other words, how often are Information Hiding Design Rules used in
microcontrollers programming for real-time applications?

Not often, in my experience.
ERT covers a lot of ground these days... I've seen an ERT that made
some attempt
to info hide. The different parts communicated by message passing even
thoough they could have called functions directly. the different
layers
(think communication protocols) hid information from each other.
>*Part of the reason is that most applications
are written by fewer people,
fewer than what? I think this system had about 10 at peak.
And they weren't the same 10. Joke for new starter "you were
still at school when this macro was defined"

with a shared set of rules and priority of
goals that tend to preclude using extra code or execution time resources to
enforce abstract data typing or little benefit.
its been ported to different architectures several times.

My typical practice is to define the full data structure in the module
header file, then document, with comments, that all operations with the foo
data should be done by calling the foo module functions. *If I expect the
module to only need one data structure (i.e., THE serial output buffer), I
will typically code it as static memory in the foo module, instead of
allocating an instance.
as I said ERT covers a lot of ground. How many lines of code are there
in your phone? Your car? An A380.

I know, none of these are Embedded Systems because they all have a
GUI!
--
Nick Keighley

Jul 3 '08 #25

P: n/a
On Wed, 2 Jul 2008 22:21:23 +0000 (UTC), Antoninus Twink
<no****@nospam.invalidwrote:
>On 2 Jul 2008 at 16:33, Richard Harter wrote:
>Simply returning NULL discards information about the nature of the
failure.

Consider writing information messages to a log file instead of either
stdout or stderr.

Surely a simpler solution for a library function is to set errno?
Simpler, yes. Useful? In general, no.

Richard Harter, cr*@tiac.net
http://home.tiac.net/~cri, http://www.varinoma.com
Save the Earth now!!
It's the only planet with chocolate.
Jul 3 '08 #26

P: n/a
rahul wrote:
On Jul 3, 3:21 am, Antoninus Twink <nos...@nospam.invalidwrote:
>On 2 Jul 2008 at 16:33, Richard Harter wrote:
>>Simply returning NULL discards information about the nature of the
failure.
Consider writing information messages to a log file instead of either
stdout or stderr.
Surely a simpler solution for a library function is to set errno?

Simpler and prevalent in Linux/Unix environment; it is more of a
personal preference but many a times I return defined constants.
Something like:
#define NODE_NOT_FOUND -10
#define EMPTY_LIST -11

int search(int i) {
/* if list empty */
return EMPTY_LIST;
/* if not found */
return NODE_NOT_FOUND;
}

But of course, errno is a better choice in certain scenarios, like
returning NULL and setting the errno on error; otherwise returning the
valid pointer.
Ah, but what value do you store in errno? The values that
are guaranteed to be meaningful are EDOM, EILSEQ, and ERANGE, and
that's all. You could choose other values, positive or negative,
but then the use of perror() and strerror() becomes problematic.
(Choosing a positive value is particularly perilous because you
might bump into a platform-specific error code like EIEIO.)

By the way, you shouldn't use EMPTY_LIST as an identifier in
a module where <errno.his #include'd, because the implementation
is allowed to define E* macros in addition to the required three.

--
Er*********@sun.com
Jul 3 '08 #27

P: n/a
On 3 Jul 2008 at 17:32, Eric Sosman wrote:
Ah, but what value do you store in errno? The values that are
guaranteed to be meaningful are EDOM, EILSEQ, and ERANGE, and that's
all. You could choose other values, positive or negative, but then
the use of perror() and strerror() becomes problematic.
This is just paranoid nonsense. Sensible platforms will provide many,
many useful error numbers, and perror() and strerror() will work just
fine with them.

For example, Linux provides 120 errors for you to choose from, many of
which are inherited from POSIX:

E2BIG Argument list too long
EACCES Permission denied
EADDRINUSE Address already in use
EADDRNOTAVAIL Address not available
EAFNOSUPPORT Address family not supported
EAGAIN Resource temporarily unavailable
EALREADY Connection already in progress
EBADE Invalid exchange
EBADF Bad file descriptor
EBADFD File descriptor in bad state
EBADMSG Bad message
EBADR Invalid request descriptor
EBADRQC Invalid request code
EBADSLT Invalid slot
EBUSY Device or resource busy
ECANCELED Operation canceled
ECHILD No child processes
ECHRNG Channel number out of range
ECOMM Communication error on send
ECONNABORTED Connection aborted
ECONNREFUSED Connection refused
ECONNRESET Connection reset
EDEADLK Resource deadlock avoided
EDEADLOCK Synonym for EDEADLK
EDESTADDRREQ Destination address required
EDOM Mathematics argument out of domain of function C99)
EDQUOT Disk quota exceeded
EEXIST File exists
EFAULT Bad address
EFBIG File too large
EHOSTDOWN Host is down
EHOSTUNREACH Host is unreachable
EIDRM Identifier removed
EILSEQ Illegal byte sequence C99)
EINPROGRESS Operation in progress
EINTR Interrupted function call
EINVAL Invalid argument
EIO Input/output error
EISCONN Socket is connected
EISDIR Is a directory
EISNAM Is a named type file
EKEYEXPIRED Key has expired
EKEYREJECTED Key was rejected by service
EKEYREVOKED Key has been revoked
EL2HLT Level 2 halted
EL2NSYNC Level 2 not synchronized
EL3HLT Level 3 halted
EL3RST Level 3 halted
ELIBACC Cannot access a needed shared library
ELIBBAD Accessing a corrupted shared library
ELIBMAX Attempting to link in too many shared libraries
ELIBSCN lib section in a.out corrupted
ELIBEXEC Cannot exec a shared library directly
ELOOP Too many levels of symbolic links
EMEDIUMTYPE Wrong medium type
EMFILE Too many open files
EMLINK Too many links
EMSGSIZE Message too long
EMULTIHOP Multihop attempted
ENAMETOOLONG Filename too long
ENETDOWN Network is down
ENETRESET Connection aborted by network
ENETUNREACH Network unreachable
ENFILE Too many open files in system
ENOBUFS No buffer space available(XSI STREAMS option))
ENODATA No message is available on the STREAM head read queue
ENODEV No such device
ENOENT No such file or directory
ENOEXEC Exec format error
ENOKEY Required key not available
ENOLCK No locks available
ENOLINK Link has been severed
ENOMEDIUM No medium found
ENOMEM Not enough space
ENOMSG No message of the desired type
ENONET Machine is not on the network
ENOPKG Package not installed
ENOPROTOOPT Protocol not available
ENOSPC No space left on device
ENOSR No STREAM resources(XSI STREAMS option))
ENOSTR Not a STREAM(XSI STREAMS option))
ENOSYS Function not implemented
ENOTBLK Block device required
ENOTCONN The socket is not connected
ENOTDIR Not a directory
ENOTEMPTY Directory not empty
ENOTSOCK Not a socket
ENOTSUP Operation not supported
ENOTTY Inappropriate I/O control operation
ENOTUNIQ Name not unique on network
ENXIO No such device or address
EOPNOTSUPP Operation not supported on socket
EOVERFLOW Value too large to be stored in data type
EPERM Operation not permitted
EPFNOSUPPORT Protocol family not supported
EPIPE Broken pipe
EPROTO Protocol error
EPROTONOSUPPORT Protocol not supported
EPROTOTYPE Protocol wrong type for socket
ERANGE Result too large C99)
EREMCHG Remote address changed
EREMOTE Object is remote
EREMOTEIO Remote I/O error
ERESTART Interrupted system call should be restarted
EROFS Read-only file system
ESHUTDOWN Cannot send after transport endpoint shutdown
ESPIPE Invalid seek
ESOCKTNOSUPPORT Socket type not supported
ESRCH No such process
ESTALE Stale file handle
ESTRPIPE Streams pipe error
ETIME Timer expired
ETIMEDOUT Connection timed out
ETXTBSY Text file busy
EUCLEAN Structure needs cleaning
EUNATCH Protocol driver not attached
EUSERS Too many users
EWOULDBLOCK Operation would block
EXDEV Improper link
EXFULL Exchange full

Jul 3 '08 #28

This discussion thread is closed

Replies have been disabled for this discussion.