On 9/11/2011 3:49 PM, Максим Фомин wrote:
> I want to hide several fields within structure from "client" code. Can
> I use following (just example):
>
> - declare struct X { int visible; }; in "export.h" and several
> functions, which take struct X as an argument;
> - "export.h" is included by client code;
> - declare struct X { int visible; int hidden; }; in "private.h";
> - "private.h" is included in .c file which contains definitions of
> functions which work with struct X
>
> Thus, when mentioned functions process struct X, they may access to
> hidden integer, however external code even doesn't suspect that there
> is one.
It's not guaranteed to work. True, the "visible" element will
appear at the same offset in both versions of "struct X," and if
"visible" actually encompasses several elements they will also appear
at the same offsets as long as both versions agree.[*] But the two
versions might have different alignment requirements -- not very
likely in what you've shown, but change hidden to a "double" and it
is entirely plausible that the complete "struct X" could require
stricter alignment than the abbreviated version.
[*] Actually, the Standard only *guarantees* this if the two
versions both appear in the same union. But nobody's ever seen an
actual compiler where it wouldn't hold, even without the union.
Besides, there's a better way (which also avoids the confusion
of having two different versions of "struct X" floating around).
Use two different structs, one public and one private, and embed
the public version inside the private:
struct public {
int visible;
...
};
struct private {
struct public export;
int hidden;
FILE *stream;
char *stuff;
...
};
Your library code should deal in "struct public *" pointers, which
you know always point to the "export" element of a "struct private"
instance. So internally
void foo(struct public *bar) {
struct private *baz = (struct private*)bar;
baz->hidden = 42;
}
> The first issue of this technique is that client code cannot hold
> allocation.
The usual way to deal with this is to have the library manage
the storage, and let the client deal only with pointers to it:
struct public *factory(void) {
struct private *baz = malloc(sizeof *baz);
baz->stream = fopen("file.dat", "r");
baz->stuff = malloc(42);
...
return &baz->export;
}
void oubliette(struct public *junk) {
struct private *baz = (struct private*)junk;
fclose(baz->stream);
free(baz->stuff);
...
free(baz); // or free(junk) in simple cases
}
--
Eric Sosman
d