Pierre Phaneuf
2004-01-20 03:09:02 UTC
Okay, so I discussed a bit of this with Stéphane last friday and today,
and I'm still not sure what to do exactly...
There are two possible approaches:
- module gives you an IModule* (the current way)
- module has a ModuleInfo struct
The IModule Way
===============
Pros:
This is more forward-compatible, meaning that a module could always have
the option of trying to be loadable with older module loaders, if it
wanted to spend the effort. It would be forward-compatible in the same
way as other XPLC components can, by providing compatibility wrappers
when the loader requests an older interface.
Cons:
Heavier modules with more code in them. Less code is simpler, and I can
appreciate simple.
There is a circular reference in this design: the module cannot be
unloaded as long as there are references to objects with code from this
module, and the IModule* comes from this module.
The ModuleInfo Way
==================
Pros:
Lightweight and simple, it would be easy to make tools that analyze modules.
Cons:
If the loader encounters a module with a larger major version than it
supports, it can only croak.
-----------------------------------------------------------------
I like the ModuleInfo way a lot, as it is very simple and robust, but
having to deal with these version numbers isn't very XPLCish, which
actually has its own way of dealing with versioning (the IModule way
uses it).
The way I see this is like this. Implementation of IModule will likely
come from libxplc-cxx anyway. Supposing there is a major change in the
interface which would require bumping the major version, the IModule
implementation would have to provide a way to emulate the older
interface, for older module loaders. It could also decide to obsolete an
old interface by not implementing it anymore, but there would be little
reason to do so other than if this old interface become difficult to
emulate in the context of the current one.
If it is possible to emulate the old interface in the new one, it
implies that it only adds in a compatible way (not requiring extra
actions from the loader that it didn't do before, only optionally
requiring them to gain access to some new feature). So it sounds
plausible to me that any feature that could stay forward-compatible
using the IModule way could be done using the ModuleInfo way (my theory
is that it might be provable), and that the only times we'd need to bump
the major number would for changes in interface that couldn't possibly
be done, even with IModule. Does that sound right?
In this case, putting all this compatibility gunk in every single module
sounds like a lot of duplicate code going on when it could all be in one
place.
There is also nothing in particular that forces a module to follow the
very latest module major version, if it doesn't need the feature. The
macros could be done in such a way to easily allow usage of the oldest
interface that supports what the user needs.
I really, truly expect that bumping the major number should be a large
even, akin to bumping the soname of Linux's libc (anyone remembers
libc.so.5?).
There are some cases where you'd be just terminally screwed, but that's
the way it sometimes is. For example, a module that would use the
reflection information could still be validly loaded by a loader that
doesn't know about the reflection information and there would be just
nothing to play with. Not a catastrophic failure, but it'd be nice to
have a way of notifying the user... Have the function calls send the
maximum version numbers that the loader supports, would that be useful?
It couldn't react very much, there's not much space for interaction at
this level... Maybe if we added a bool return value to loadModule, which
could tell us that the loading "failed" (in the sense that the module
would be useless), allowing for possible user reporting?
I'm showing an obvious bias toward the ModuleInfo strategy, but I feel
that I don't really have super-solid arguments, so it's not clear.
and I'm still not sure what to do exactly...
There are two possible approaches:
- module gives you an IModule* (the current way)
- module has a ModuleInfo struct
The IModule Way
===============
Pros:
This is more forward-compatible, meaning that a module could always have
the option of trying to be loadable with older module loaders, if it
wanted to spend the effort. It would be forward-compatible in the same
way as other XPLC components can, by providing compatibility wrappers
when the loader requests an older interface.
Cons:
Heavier modules with more code in them. Less code is simpler, and I can
appreciate simple.
There is a circular reference in this design: the module cannot be
unloaded as long as there are references to objects with code from this
module, and the IModule* comes from this module.
The ModuleInfo Way
==================
Pros:
Lightweight and simple, it would be easy to make tools that analyze modules.
Cons:
If the loader encounters a module with a larger major version than it
supports, it can only croak.
-----------------------------------------------------------------
I like the ModuleInfo way a lot, as it is very simple and robust, but
having to deal with these version numbers isn't very XPLCish, which
actually has its own way of dealing with versioning (the IModule way
uses it).
The way I see this is like this. Implementation of IModule will likely
come from libxplc-cxx anyway. Supposing there is a major change in the
interface which would require bumping the major version, the IModule
implementation would have to provide a way to emulate the older
interface, for older module loaders. It could also decide to obsolete an
old interface by not implementing it anymore, but there would be little
reason to do so other than if this old interface become difficult to
emulate in the context of the current one.
If it is possible to emulate the old interface in the new one, it
implies that it only adds in a compatible way (not requiring extra
actions from the loader that it didn't do before, only optionally
requiring them to gain access to some new feature). So it sounds
plausible to me that any feature that could stay forward-compatible
using the IModule way could be done using the ModuleInfo way (my theory
is that it might be provable), and that the only times we'd need to bump
the major number would for changes in interface that couldn't possibly
be done, even with IModule. Does that sound right?
In this case, putting all this compatibility gunk in every single module
sounds like a lot of duplicate code going on when it could all be in one
place.
There is also nothing in particular that forces a module to follow the
very latest module major version, if it doesn't need the feature. The
macros could be done in such a way to easily allow usage of the oldest
interface that supports what the user needs.
I really, truly expect that bumping the major number should be a large
even, akin to bumping the soname of Linux's libc (anyone remembers
libc.so.5?).
There are some cases where you'd be just terminally screwed, but that's
the way it sometimes is. For example, a module that would use the
reflection information could still be validly loaded by a loader that
doesn't know about the reflection information and there would be just
nothing to play with. Not a catastrophic failure, but it'd be nice to
have a way of notifying the user... Have the function calls send the
maximum version numbers that the loader supports, would that be useful?
It couldn't react very much, there's not much space for interaction at
this level... Maybe if we added a bool return value to loadModule, which
could tell us that the loading "failed" (in the sense that the module
would be useless), allowing for possible user reporting?
I'm showing an obvious bias toward the ModuleInfo strategy, but I feel
that I don't really have super-solid arguments, so it's not clear.
--
Pierre Phaneuf
http://advogato.org/person/pphaneuf/
"I am denial, guilt and fear -- and I control you"
Pierre Phaneuf
http://advogato.org/person/pphaneuf/
"I am denial, guilt and fear -- and I control you"