[comp.lang.c] Info Hiding in C

unicorn@uxh.cso.uiuc.edu (Harry E Miller) (04/17/91)

Hi there,

I started learning C++ and one of the most interesting things about
it is information hiding.

For my project at work, I am developing a dynamically allocated
string library, that will eventually incorporate expanded memory.
(I'm working with Turbo C++ in C mode.)

I wish to include optional information hiding within this library,
so that the programer can only get string information from a
function (not a macro!).

I've tested the following three files, and they seem to work.
They compile without complaint with all warning flags on.

I would like any comments (flames if this is just crap) on
this way of (optionally) hiding the string structure.

Also why won't this work?

typedef void String[6];

The compiler gives me a "Not an allowed type" error.
 

I just made a fool of myself debugging a FORTRAN program (uch!!!) with
a distinguished colleague (sp?), so go ahead and flame away if you wish!

Please e-mail this to me.

Harry Miller aka "The Four-Fingered Bandit"
unicorn@uxh.cso.uiuc.edu  - lots of u's running around these hills

You can tell how far we have to go, when FORTRAN is the
    language of supercomputers.
         -- Steven Feiner


/* ------------------- cut here for file unicstr.h --------------- */

/* file unicstr.h */

#ifdef INFO_HIDING

/* programmer can hide the structure String so that he has to
   use functions to access the data
*/

/* typedef void String[6];  will not work! why? */

typedef char String[6];   /* Hidden string */

#else

	                  /* ------------------------------------- */
typedef struct _string {  /* 0x00 | string structure               */
        char *str;        /* 0x02 | pointer to the char array      */
        unsigned length;  /* 0x04 | current length of str          */
        unsigned alloc;   /* 0x06 | amount allocated to str        */
} _String;                /* 0x07 | end of _string structure       */
	                  /* ------------------------------------- */

typedef _String String;

#endif

/* alloc and dealloc prototypes */
String *StrAlloc(char *str, unsigned amount);
void     StrDealloc(String *str);

/* get info prototypes */
char *get_str(const String *str);
unsigned get_len(const String *str);
unsigned get_alloc(const String *str);


/* ------------------- cut here for file unicstr.c --------------- */

/* file unicstr.c */

#ifdef INFO_HIDING
#undef INFO_HIDING
#endif

#include <alloc.h>
#include <string.h>
#include "unicstr.h"

String *StrAlloc(char *strptr, unsigned amount)
{
String *s;
unsigned len;

  len = strlen(strptr);

  if ((s = (String *) malloc(sizeof(String))) == NULL)
     return (NULL);

  if (amount < len)        /* will allocate at least enough */
     amount = len+1;       /* for the string to fit into    */

  if ((s->str = (char *) malloc(amount)) == NULL)
     return (NULL);

  strcpy(s->str,strptr);
  s->length = len;
  s->alloc = amount;

  return (s);
}


void StrDealloc(String *str)
{
  free(str->str);
  free(str);
}


char *get_str(const String *str)
{
  return (str->str);
}


unsigned get_len(const String *str)
{
  return (str->length);
}


unsigned get_alloc(const String *str)
{
  return (str->alloc);
}

/* ----------------- cut here for file test.c -------------------- */

/* file test.c */

#define INFO_HIDING

#include <stdio.h>
#include "unicstr.h"

void main()
{
String *s1, *s2;

  s1 = StrAlloc("This is test 1", 20);
  s2 = StrAlloc("This is test 2", 10);

  printf(" %s\n length = %u\n allocated amount = %u\n", get_str(s1),
         get_len(s1), get_alloc(s1));

  printf(" %s\n length = %u\n allocated amount = %u\n", get_str(s2),
         get_len(s2), get_alloc(s2));

  StrDealloc(s1);
  StrDealloc(s2);
}

jeenglis@alcor.usc.edu (Joe English) (04/17/91)

unicorn@uxh.cso.uiuc.edu (Harry E Miller) writes:

>I wish to include optional information hiding within this library,
>so that the programer can only get string information from a
>function (not a macro!).
> [...]
>Also why won't this work?
>
>typedef void String[6];

It's not allowed.  Why?  Basically, because it doesn't
make sense.  'void' means, informally, 'no value'; you're
trying to declare an array of six nothings.

Anyway, on to this:

>#ifdef INFO_HIDING
>[...]
>typedef char String[6];   /* Hidden string */
>#else
>typedef struct _string {  /* 0x00 | string structure               */
>        char *str;        /* 0x02 | pointer to the char array      */
>        unsigned length;  /* 0x04 | current length of str          */
>        unsigned alloc;   /* 0x06 | amount allocated to str        */
>} _String;                /* 0x07 | end of _string structure       */
>typedef _String String;
>#endif

First of all, a 'struct _string' is not necessarily six
bytes long, even when sizeof(char *) = sizeof(unsigned) = 2.
It may happen to be six bytes long under the current version
of your particular compiler in that particular memory model,
but that won't always be the case.   (You said you were using
Turbo C on a PC, right?  Try, I think, 'tcc -mc' instead of
plain 'tcc'.  The code won't work anymore.)  A better way
to do this is to use:

struct _string {
    ...     /* same as above */
};

typedef char String[sizeof(struct _string)];

and it will work on any platform/compiler/memory model.

The rest looks basically OK.  In my opinion it's overkill,
though;  here's how I usually do information hiding in C:

typedef struct Foo {
    /* PUBLIC: */
    ... publically usable members go here ...
    /* READONLY: */
    ... publically readable members go here ...
    /* PRIVATE: */
    ... private stuff goes here ...
} Foo;

... prototypes for functions manipulating Foos go here ...

That's all the information hiding I need.  Namespace clutter
isn't any more of a problem than it is in C++, since all
of struct Foo's member names are in their own namespace.
True, the compiler won't generate an error if I try to access
or write to the wrong members, but then again it doesn't issue
a warning when I futz around with the contents of a FILE * 
either, but I use stdio all the time without any loss of
data abstraction.  The words "PRIVATE" and "READONLY" in large
friendly letters are sufficient to let me and anyone else
who reads the .h file know that certain members are none
of my business.  I don't find it productive to play tricks
with the compiler to keep me from shooting myself in the foot.

(NB: For *complete* information hiding, there shouldn't really
be a "READONLY" section.  I use it a lot myself, but I'm starting
to wonder if it's a bad habit...)


--Joe English

  jeenglis@alcor.usc.edu

scs@adam.mit.edu (Steve Summit) (04/18/91)

In article <1991Apr17.004044.22940@ux1.cso.uiuc.edu> unicorn@uxh.cso.uiuc.edu (Harry E Miller) writes:
>I started learning C++ and one of the most interesting things about
>it is information hiding.
>I wish to include optional information hiding within [a] library,
>so that the programer can only get string information from a
>function (not [by using] a macro!).
>I would like any comments (flames if this is just crap) on
>this way of (optionally) hiding the string structure.
>
>Also why won't this work?
>typedef void String[6];

As has been explained, you can't have arrays (or any variables,
struct members, etc.) of type void.  If void had a size, it would
be 0, and C is still spooky about 0-sized objects.  (void can
only be used as the base for pointer types, as a cast for
"throwing away" a value, and as a placeholder, meaning "no
parameters," in a prototype-form function parameter list.
There's probably another use I forgot.)

As Joe English has explained, using an array of char rather than
an array of void, and using sizeof() to make it the right size,
would probably work for your publicly-visible [Hi, Mark!] struct
placeholder, but it seems marginal (and reminiscent of Dennis
Ritchie's "unwarranted chumminess with the compiler").

Information hiding is fine; I use it myself occasionally.
Assuming that the structure type you wish to "hide" is always
referred to in the calling code via a pointer (the usual case),
you can use a different technique.  In the header file, the
entire declaration of the structure's "shape" is #ifdef'fed out
(except for "privileged" modules):

	#ifdef BLORT_DEFINE

	struct blort
		{
		int beercooler;
		int beckendorf;
		int blarneystone;
		};

	#endif

	extern struct blort *blortalloc();

(Obviously, BLORT_DEFINE is #defined only by the source files
implementing the "blort" module, not by users/callers.)

C is perfectly happy dealing with pointers to structures whose
shape isn't known; this technique makes explicit, deliberate use
of that fact.  (lint -h says "warning: struct blort never
defined," however.  I either use grep -v to eliminate this
message, or turn on BLORT_DEFINE #ifdef lint .)

Another trick I'll use, to "hide" a data type further, is to
provide a typedef:

	typedef blort *blortd;

Calling programs then refer to the abstract blortd type ("blort
descriptor"), and don't need to know whether it is a struct
pointer, a small int index into a table somewhere, or something
else.

                                            Steve Summit
                                            scs@adam.mit.edu

rob@meaddata.com (Robert E. Lancia) (04/18/91)

In article <16645@chaph.usc.edu> jeenglis@alcor.usc.edu (Joe English) writes:
>unicorn@uxh.cso.uiuc.edu (Harry E Miller) writes:
>
>>I wish to include optional information hiding within this library,
>>so that the programer can only get string information from a
>>function (not a macro!).
>> [...]
> [...]
>struct _string {
>    ...     /* same as above */
>};
>
>typedef char String[sizeof(struct _string)];
>
>and it will work on any platform/compiler/memory model.
> [...]
>--Joe English
>  jeenglis@alcor.usc.edu


This won't work because if you check the original article, you'll
see that his #ifdef's hide the struct _string.

#ifdef INFO_HIDING
    typedef char String[???];
#else
    typedef struct _string {
     ...
    } String;
#endif

When the compiler needs to know the array size, the struct _string isn't
visable.



--
|Robert Lancia                 | The above opinions    | Mead Data Central
|(513) 297-2560                | may not necessarily   | Data Services Division
|rob@pmserv.meaddata.com       | be MDC's.  Heck, they | P.O. Box 308
|...!uunet!meaddata!pmserv!rob | may not even be mine. | Dayton, Ohio  45401

newsuser@oliver.SUBLINK.ORG (Ugo Cei) (04/19/91)

unicorn@uxh.cso.uiuc.edu (Harry E Miller) writes:

>I wish to include optional information hiding within this library,
>so that the programer can only get string information from a
>function (not a macro!).
[...]
>#ifdef INFO_HIDING

>/* programmer can hide the structure String so that he has to
>   use functions to access the data
>*/
[...]
>typedef char String[6];   /* Hidden string */

>#else

>	                  /* ------------------------------------- */
>typedef struct _string {  /* 0x00 | string structure               */
>        char *str;        /* 0x02 | pointer to the char array      */
>        unsigned length;  /* 0x04 | current length of str          */
>        unsigned alloc;   /* 0x06 | amount allocated to str        */
>} _String;                /* 0x07 | end of _string structure       */
>	                  /* ------------------------------------- */

>typedef _String String;

As someone else pointed out, declaring an array of six chars for your
"opaque" string type puts on your code an ugly and unnecessary
dependence on the particular machine and compiler. What I have been
doing recently in cases like this (and I would very much like comments
on it) is to use incomplete types in a public header file, and their
complete definition in a private header. The modules implementing my
objects include the private header, while "client" programs will
include just the public header, like this:

-----

/*
 * file: String.h - public definitions for String class
 */

#ifndef _STRING_
#define _STRING_

typedef struct _String String;
  
...

/* Prototypes */

String * CreateString( ... );
...

#endif

-----

/*
 * file: StringP.h - private definitions for String class
 */

#ifndef _STRINGP_
#define _STRINGP_
#include "String.h"

struct 
  {
    ...
  }
_String;
  
#endif

-----

/*
 * file: String.c - implementation of the String class
 */

#include "StringP.h"

String *
CreateString( ... )
{
  ...
}

... other function to manipulate String's and access their fields

-----

/*
 * file: main.c - demonstration of usage of String class
 */

#include <String.h>

int
main(void)
{
  String * str;

  str = CreateString(...);
  ...
}

-----

Then, if you want to disable information hiding, just include
"StringP.h" instead of "String.h".

>  if (amount < len)        /* will allocate at least enough */
             ^^^
>     amount = len+1;       /* for the string to fit into    */

No, it won't, if amount == len. Your test should look like

   if(amount <= len)

Besides, I don't really grasp the need for the "amount" field in your
structure. Can't you just live with "len" ?

Cheers.
-- 
**************** | Ugo Cei            | home:    newsuser@oliver.sublink.org
*    OLIVER    * | Via Colombo 7      | office:  cei@ipvvis.unipv.it
**************** | 27100 Pavia ITALY  |       "Real Programs Dump Core"