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"