[comp.lang.c] A dilemma with handles

markd@iti.org (Mark Delany) (10/02/90)

A bit of a dilemma that I'd like some advise on please.

I'm currently developing a portable library that provides a high-quality
link across async lines. 

The interface is purposely similar to stdio/Unix-IO in that you have
routines like zzopen, zzwrite, zzread, etc. 

As an example, the major call sequences are:

	handle = zzopen(...);
	bytes = zzwrite(handle, ...);
	bytes = zzread(handle, ...);
	result = zzclose(handle);

I'm sure you get the drift.

Now my question relates to the definition of "handle".  Within the
library this is simply a pointer to a structure that holds all the
goodies necessary to achieve the results.

Unfortunately, this structure is quite complicated and littered with
system dependencies.  Complications and system dependencies that I don't
want visible in the caller's environment.  Hopefully the reasons are
obvious for this, but in case they're not, the structure has a variety
of #ifdef's for all the Unix variants which I don't want the user to
have to define correctly prior to use.

In other words, because the real definitions of "handle" is full of
system dependencies, I want to define a *dummy* "handle" for the
purposes of the user's view of the library - especially considering
prototypes. 

To finally get to my question. 

Is the idea of defining a dummy definition acceptable or should I burden
the user with the full definition?

If a dummy definition *is* acceptable, then presumably I should define
it as "void *".  Yes, no?


--

------------         -----------------     ---------------------
Mark Delany          markd@Aus.Sun.COM     ...!sun!sunchat!markd
------------         -----------------     ---------------------

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/03/90)

In article <markd.654876849@sunchat>, markd@iti.org (Mark Delany) writes:
> Now my question relates to the definition of "handle".  Within the
> library this is simply a pointer to a structure that holds all the
> goodies necessary to achieve the results.

Remember that C allows you to use "incomplete types".  You can have
a header file

	/* pubstuff.h */
	typedef struct BlackMystery *handle;

	extern handle zzopen(char *title, char *mode);
	...
	/* end of pubstuff.h */

which your users can get at, and a header file

	/* realstuff.h */
	#include "pubstuff.h"

	struct BlackMystery
	    {
		/* system-dependent stuff spelled out here */
	    };

	/* other declarations */
	/* end of realstuff.h */

and then the file in which you implement, say, zzopen can
	#include "realstuff.h"

When a user program #includes "pubstuff.h" they get handles which are
pointers to an incomplete type.  However, the C compiler will know
everything that it needs to manage these pointers, because "all struct
pointers smell the same".  There are some big advantages to doing it
this way.
	- you don't have to tell any lies; just conceal some of the truth
	- you don't need any casts
	- Lint remains your friend:
	  When you Lint user code against your library there won't have
	  been any casts needed, so you _will_ benefit from type checking
	- user code will be unable to declare variables of type
	  struct BlackMystery, and it will be unable to deference handles.
	  For example, given
		handle p, q;
	  the assignment
		p = q;
	  will be allowed and will work correctly, but
		*p = *q;
	  will be disallowed and rejected at compile time.

This approach isn't a _dummy_ declaration, just an _incomplete_ one.

-- 
Fixed in the next release.

karl@haddock.ima.isc.com (Karl Heuer) (10/03/90)

In article <3877@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
>In article <markd.654876849@sunchat>, markd@iti.org (Mark Delany) writes:
>> Now my question relates to the definition of "handle".  Within the
>> library this is simply a pointer to a structure that holds all the
>> goodies necessary to achieve the results.
>
>[Use an incomplete struct type]

I agree.  If you use `void *', the compiler will probably silently allow them
to be assigned to/from arbitrary pointer types; you're making it a lot easier
for the user to make certain kinds of mistakes.  With an incomplete type, the
user gets the full benefit of type-checking.

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

sarima@tdatirv.UUCP (Stanley Friesen) (10/04/90)

In article <markd.654876849@sunchat> markd@iti.org (Mark Delany) writes:
>
>Now my question relates to the definition of "handle".  Within the
>library this is simply a pointer to a structure that holds all the
>goodies necessary to achieve the results.
>...
>In other words, because the real definitions of "handle" is full of
>system dependencies, I want to define a *dummy* "handle" for the
>purposes of the user's view of the library - especially considering
>prototypes. 
>...
>Is the idea of defining a dummy definition acceptable or should I burden
>the user with the full definition?
>
>If a dummy definition *is* acceptable, then presumably I should define
>it as "void *".  Yes, no?

I think a dummy handle is probably a good idea (it will discourage users
from trying to depend on the implementation).   How to declare it is
difficult, since ANSI compliance is probably wanted.


What I did was something like this:

In the public header file:

	typedef struct str_tag_name handle_t;

	handle_t *xxopen();


That is I wnnet ahead and gave the users the *name* of the structure,
and, since pointers to structures can be declared given only the name,
the user does not actually need the full definition of the struct.

In the private header file (used by the package implementation):

	#include <public.h>

	struct str_tag_name {
		int	str_member;
		/* And so on, defining the struct	*/
	};

This gives the implementation the info it needs to do its work.  
The advantage is that no type punning is involved - everything is declared
properly according to its real type, but the implementation is still hidden.



-----------------------
uunet!tdatirv!sarima				(Stanley Friesen)