I'm writing an abstraction layer over an OS's ability to do non-blocking
reads and writes on various things. The basic idea is that the caller
will be able to let the I/O happen in the background, and will poll
for completion using something along the lines of *nix's select() or
Win32's WaitForMultiple Objects, and when the OS signals that a read has
completed or a write will be accepted the abstraction layer will be called
to actually do the I/O call and call any code that's interested in it.
The read and write handling have some basic asymmetry, so the abstraction
layer's data and the interface to the code that requests reads and writes
will be different depending on which direction the data is going, but
the basic poll-and-call-handler pattern in the main loop will be the
same either way.
What I'd like to do is something like this:
--------
/*in overlapped.h*/
struct overlapped_cook ie
{
sync_type need_attention;
struct overlapped_data *internal;
};
--------
/*in overlapped-read.c*/
struct overlapped_data
{
/*Internal data for read bookkeeping*/
};
struct overlapped_cook ie *setup_read(/*args*/)
{
struct overlapped_cook ie *ret=malloc(siz eof *ret);
if(!ret)
return NULL;
ret->internal=mallo c(sizeof ret->internal);
if(!ret->internal)
{
free(ret);
return NULL;
}
/*Continue with initialization*/
}
--------
/*in overlapped-write.c*/
struct overlapped_data
{
/*Internal data for write bookkeeping*/
};
struct overlapped_cook ie *setup_write(/*args*/)
{
struct overlapped_cook ie *ret=malloc(siz eof *ret);
if(!ret)
return NULL;
ret->internal=mallo c(sizeof ret->internal);
if(!ret->internal)
{
free(ret);
return NULL;
}
/*Continue with initialization*/
}
--------
The idea is that the main loop can do something like:
--------
/*These arrays are populated by other code to keep an up-to-date list of
reads and writes (possibly to be extended to other events) that are
being waited for
*/
sync_type sync[N];
int (*handler[N])(struct overlapped_cook ie *);
struct overlapped_cook ie *all_overlapped[N];
/*main loop, minus error handling:*/
while(!are_we_d one_yet())
{
int which_one=do_wa it(sync,num_fil led);
handler[which_one](all_overlapped[which_one]);
}
--------
I have two questions about this:
(1) Is it legal and well-defined for external code to use a pointer to
the same incomplete struct type for what is actually different types
in different translation units, as long as no read struct ever gets
passed to the write functions and no write struct ever gets passed to
the read functions?
(I'm fairly sure, but not certain, that the struct pointer representation
requirements make this valid.)
(2) Is this considered acceptable, maintainable, and not aesthetically
unpleasant by other C programmers? Or, at least, no worse than the
alternative of forcing the main poll loop to handle reads and writes
separately?
(This one I'm not so sure about.)
(2b) Is there a better way to do this?
dave
--
Dave Vandervies dj******@csclub .uwaterloo.ca
I _am_ consistent - if one of those other pointer guide writers came
here and asked for comments, they'd get chewed out just as badly.
--Richard Bos in comp.lang.c