Does this confirm to ISO C?

  • Thread starter МакÑим Фомин
  • Start date
Ð

МакÑим Фомин

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.

The first issue of this technique is that client code cannot hold
allocation.
 
E

Eric Sosman

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
}
 
J

Jens Thoms Toerring

МакÑим Фомин said:
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.
The first issue of this technique is that client code cannot hold
allocation.

Looks extremely dirty to me, especially naming both the same.
And yes, the user can't allocate such a structure, nor can he
make copies - and there's nothing that would allow you to
control that the client doesn't do it anyway and mess up your
whole scheme. And you can only pass pointers to the struc-
ture between your and the clients code (otherwise the client
would need a full definition or things would get messed up
completely if the clients code would receive a larger struc-
ture than it is made to expect). Why not then simplify life
a lot by making it a completely opaque structure and export a
function that allows the client to set the visible member?
While you're at it you could also supply a function for allo-
cation and deallocation. Looks a lot safer to me and will cost
you not much more than the work of adding a rather small number
of functions. With a typedef you could even make it invisible
to the client that it's dealing with just pointers to that
kind of structures.
Regards, Jens
 
B

Ben Bacarisse

МакÑим Фомин said:
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

Just to clarify: you mean pointers to these structs will be passed, yes?
You don't say so but without passing pointers it is certain to fail so
it seems like a reasonable assumption.
Thus, when mentioned functions process struct X, they may access to
hidden integer, however external code even doesn't suspect that there
is one.

The first issue of this technique is that client code cannot hold
allocation.

If the allocation is "behind the wall" so to speak, have you considered:

struct X { int visible; };

struct Y { struct X x; int hidden; };

The allocator makes a struct Y but passes &Y.x to the client. When the
client passes &Y.x back to the other side of the wall, the conversion
from struct X * back to struct Y * is certain to work.
 
F

Fred

Just to clarify: you mean pointers to these structs will be passed, yes?
You don't say so but without passing pointers it is certain to fail so
it seems like a reasonable assumption.



If the allocation is "behind the wall" so to speak, have you considered:

  struct X { int visible; };

  struct Y { struct X x; int hidden; };

The allocator makes a struct Y but passes &Y.x to the client.  When the
client passes &Y.x back to the other side of the wall, the conversion
from struct X * back to struct Y * is certain to work.

And what happens if one declares an array of X's, then tries to
access
x[1].visible ?
 
B

Ben Bacarisse

It's better to snip sigs.
And what happens if one declares an array of X's, then tries to
access
x[1].visible ?

Exactly what you'd expect happens. I know that was a rhetorical
question but I'm not sure what else to say. Yes, arrays don't work with
this scheme, but they won't work with any scheme where the size of the
actual allocation is hidden from the "client" side. Presumably the OP
accepts that restriction and would use and array of pointers instead.
 
E

Eric Sosman

And what happens if one declares an array of X's, then tries to
access
x[1].visible ?

Exactly what you'd expect happens. I know that was a rhetorical
question but I'm not sure what else to say. Yes, arrays don't work with
this scheme, but they won't work with any scheme where the size of the
actual allocation is hidden from the "client" side. Presumably the OP
accepts that restriction and would use and array of pointers instead.

This is one reason the "visible" part is often entirely empty,
so the caller gets a pointer to a completely opaque object (formally,
an "incomplete type") and cannot possibly create a free-standing
instance on his own.

True, given a `struct X*' the caller cannot then access its
"visible" member(s). Instead of just peeking and poking at those
elements the caller must resort to accessor functions provided by
the library, "getters and setters" as our O-O friends might say.
That's a disadvantage, but usually not a crippling disadvantage.
 
P

Phil Carmody

Eric Sosman said:
On 9/12/2011 3:01 PM, Ben Bacarisse wrote:
This is one reason the "visible" part is often entirely empty,
so the caller gets a pointer to a completely opaque object (formally,
an "incomplete type") and cannot possibly create a free-standing
instance on his own.

It's a minor nit, but I think I'd prefer to say that there
was no visible part in such a situation, rather than the
visible part being empty. The mathematician in me disagrees
with the rest of me, of course.

Phil
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top