Article View: comp.std.c
Article #4385Re: "Size not needed?"
From: Richard Krehbiel
Date: Mon, 20 Nov 1995 12:55
Date: Mon, 20 Nov 1995 12:55
108 lines
3749 bytes
3749 bytes
erik@kroete2.freinet.de (Erik Corry) wrote:
>One thing I'd like to be able to do, but can't (tested with gcc)
>is to have a header file with the following:
> /*
> * Erik's semi-complete types suggestion
> */
> /* This struct is unknown outside the library */
> typedef struct private_part_s private_part;
> /* This struct is known outside the library and (together with
> * the functions) describes the interface to the library
> */
> typedef struct public_part_s
> {
> int x;
> int y;
> } public_part;
> /*
> * This struct is semi-complete, ie. it is incomplete, but you
> * can do '->pub' on a pointer to it.
> */
> typedef struct my_object_s
> {
> public_part pub;
> private_part priv; /* fails with incomplete type error */
> } my_object;
>
>What I'd like is for my_object to be a semi-complete type, where
>the size is unknown (so the whole fragile base 'class' problem is
>avoided), but a few of the elements are accessible (these could
>also be function pointers). This enables you to specify library
>interfaces which have a lot of OO features and are robust in terms of
>binary compatibility (since the size of the object cannot be used
>by the client code, the library implementer is never prevented from
>expanding the structure/interface).
Ah, here I may be able to help. I've used this technique with
success.
The public header:
--- public.h ---
typedef struct public_part_s
{
int x;
int y;
} public_part;
----------------
This is all that is exported. The implementation then uses:
#include "public.h"
typedef struct private_whole_s
{
public_part pub; /* publicly known members */
int q, r, s; /* privately known members */
} private_whole;
Now the library creates and uses private_whole for itself. When
passing a pointer to a library caller, it passes a pointer to the
public_part...
public_part *new_public_part(void)
{
private_whole *pv;
pv = (private_whole *)calloc(sizeof(*pv), 1);
if(pv == NULL)
return NULL;
/* initialize members... */
return &pv->pub; /* Caller gets public part handle */
}
When the library receives a pointer to a public_part from a library
user, it casts it to a private_whole:
void operate(public_part *pub)
{
private_whole *pv = (private_whole *)pub;
/* operate on pv-> members */
}
The standard requires that a pointer to the first member of a struct
must equal a pointer to the struct, therefore this is sanctioned.
Well, actually I'd probably use a macro that I've declared for my own
use...
#define CONTAINER_P(p,s,m) (s*)((char *)(p)-offsetof(s,m))
...which, for pointer p which is assumed to be a pointer to member m
of aggregate s, gives a pointer to s. There's a slight bit more type
checking than the simple cast (I'd love it if I could also be assured
that p is really the same type as &s.m). If the member is first,
typically the subtraction is optimized away since the offset is a
constant zero.
--
Richard Krehbiel, Kastle Systems, Arlington VA USA
rich@kastle.com (work) or richk@cais.com (personal)
Technical Details
Message-ID:
<1995Nov20.125350.9627@friend.kastle.com>Path:
arc1-ca.pugleaf.net!uu4news.netcom.com!friend!newsReferences:
<48gu0r$91o@news0.rain.rg.net> <1995Nov17.120551.23493@friend.kastle.com> <48iljp$jl5@news0.rain.rg.net> <DI9nH2.DIy@kroete2.freinet.de>Show Full Headers
Complete Article Headers
Newsgroups: comp.std.c Path: arc1-ca.pugleaf.net!uu4news.netcom.com!friend!news From: rich@kastle.com (Richard Krehbiel) Subject: Re: "Size not needed?" Message-ID: <1995Nov20.125350.9627@friend.kastle.com> Sender: news@friend.kastle.com (News) Reply-To: rich@kastle.com Organization: Kastle Development Associates X-Newsreader: Forte Free Agent 1.0.82 References: <48gu0r$91o@news0.rain.rg.net> <1995Nov17.120551.23493@friend.kastle.com> <48iljp$jl5@news0.rain.rg.net> <DI9nH2.DIy@kroete2.freinet.de> Date: Mon, 20 Nov 1995 12:55:05 GMT