|
From: Jeremy F. <je...@go...> - 2002-10-29 17:10:25
|
On Tue, 2002-10-29 at 08:23, Nicholas Nethercote wrote:
Hmm, I like this. Decoupling the interface version and the Valgrind
version is a v. good idea, as is the X vs. Y version changes compared to
the set of compatible versions. It also means the skin writer doesn't
need to worry about tracking core versions because it's done
automatically.
Yep.
> So long as the interface doesn't expose any structures which can change
> shape, this should work well.
Not sure if I understand this fully... only problem I can think of is that
the `details' struct changed so that the `core_interface' field was in a
different place. The `details' struct is the first argument to
VG_(pre_clo_init) so changing the other structs shouldn't matter, I think.
And if I make `core_interface' the first field in `details' that should
even work if more fields are added to the end of `details' later on?
Well, I was thinking more of how to make sure that the compiler will
pick up as many incompatible changes for itself. An example of a
troublesome interface would be where the skin creates an instance of a
structure and passes it back to the core. Also, a function which
changes the meaning of its arguments without changing their type would
cause a silent incompatibility (in that the compiler wouldn't notice).
But you're right; so long as the version discriminator field is first,
it doesn't much matter what goes on elsewhere (if you're willing to
break interface compatibility).
BTW, my understanding of what constitutes a backward (in)compatible change
is hazy with shared objects... here's what I'm guessing is backward
compatible:
add a local function to core: yes
If by "local function" you mean something which isn't part of the skin
interface, then yes.
add a global function to core: yes
change a global function's args in core: no
change a data structure (eg. struct) seen by skins: no
Maybe. If the struct is allocated by the core, only has new elements
added to the end, and they're initialized to sane default values, then
that's compatible. details and needs are examples of such structures
(so long as good discipline in making sure that new elements always go
at the end). You'd have to code it something like:
struct thing_v1 {
/* 1.0 functions */
int (*foo)(void);
/* 1.1 functions */
int (*bar)(int, int, char *);
void (*clunk)(char *, void *, int ****[], void (*foo)(int));
/* 1.2 functions */
int (*__obsolete_1)(int, int, void *);
};
Also, enums: always add new elements at the end. In fact, its better to
make sure that enums are always explicitly enumerated to get good
interface stability: enum { foo=0, bar=1, biff=4, bunk=3 }
There are sure to be other things I haven't thought of here.
It seems like there are two levels of incompatibility, too:
a. changes which require a skin to be recompiled
b. changes which require a skin's code to be changed
An example of (a) would be adding a new (optional) field to the `needs'
struct.
This doesn't need a recompile if the order of existing fields is
unaffected.
An example of (a) would be adding a new (compulsory) field to the
`details' struct.
Am I on the right track? Any other comments, etc? Thanks.
Yes. Not exposing structures as part of the interface is a really good
idea (OpenGL is a really nice example of an API which is extremely good
at maintaining long-term binary compatibility, partly because it exposes
no structures more complex than arrays of ints or floats).
J
|