On Fri, 30 Oct 1998, Chris Gray wrote:
>[Joachim Pileborg/The Arrow:]
>>[...description of mudule interfaces...]
[snip Chris's endorsement]
> >The function array is a NULL terminated array of pointers to
> >interface_function structures. The interface_function struture contains
> >the following fields:
> > o Funciton name
> > o Pointer to the function
> >The function name is a predefined constant that is interface specific.
> >For example: The Net basic module could have the following function
> >names: MOD_NET_CONNECTTO, MOD_NET_WRITETO, and MOD_NET_READFROM.
>>I'd like to add parameter and result type information. Even if this
>stuff is statically predetermined, having it available at run-time is
>useful if there is a MUD-internal language that can display it. Or
>allow an on-line programmer to call a module exporting it!
>> >MUDs must have at least the following interfaces to be DevMUD compliant:
> > o Module
> > The Module interface contains functions to get info about the
> > module and configuring the module.
> > o Net
> > The Net module is an abstract interface, containing just dummy net
> > functions. Modules inheriting this interface must provide real
> > implementations for these functions.
>>Are you really speaking of what a MUD must implement in order to be
>compliant with DevMUD? Or are you speaking of what modules must
>implement in order to be usable with a DevMUD core? In the latter case,
>why must all modules implement the Net interface?
core vs non-core again. Clearly mud hackers will need to be able to run test
muds on non-networked machines. The question is, how do we support them?
If networking is just another module, they can drop out the tcp module and
replace it with the "read from stdin" dummy net-module (or something).
Unsupported functions like gethostbyname would then have to fail...
> >If a module inherits an interface not in the interface database, the
> >module is unloaded and an error message should be logged on the console
> >and sent as a message to all loaded modules.
>>Why? If a module is loaded, and it implements an interface that no-one else
>has heard of, so what? Perhaps the very next module to be loaded needs
>that interface, and the two are being loaded as a pair. Just add the
>interface to the table of interfaces, and have it ready for use. I also
>think that the only interfaces that the core need already know about are
>those which it exports, or which it generically requires of all modules
>(e.g. your Module interface).
yes, I agree with Chris, there is absolutely no reason why new interfaces
should not be added at runtime. I do have a niggling question, though: if
Module A uses Interface B, and Module C implements interface B, how
does A get ahold of the proper declarations for Interface B? That is, if B has
function "string foo (double)", and A wants to use it, does A have to declare a
function pointer
string (*foo) (double);
and do the dynamic resolution when it's loaded? Then every module that uses
interface B needs to declare the prototype for foo, and at this point you might
think, "they should all include a header file". This might be the way to go.
I would do this using static constructors in C++, so it would all happen
magically at load-time without much work on the module writer's part. In
C, something like the following could work (I tested this with egcs-1.0.3, I
would assume it is legal...) I have neglected interfaces per se
and just looked at function pointers.
#include <stdio.h>
int _foo (int a, const char *b)
{
/* let's pretend I'm really from another module */
return a + atoi (b);
}
void *_lookup (const char *returns, const char *name, const char *args)
{
/* let's pretend I'm part of the Module Connector */
return _foo;
}
/* God bless those macros */
#define declare(ret,name,args) ret (*name) args
#define resolve(ret,name,args) name = _lookup (#ret, #name, #args)
#define foo_args (int, const char *)
declare (int, foo, foo_args);
int main (void)
{
resolve (int, foo, foo_args);
printf ("2 + 2 = %d\n", foo (2, "2"));
return 0;
}
>After thinking through this, and some earlier discussions, too, my
>previous idea of having a header file containing prototypes for the
>interfaces isn't needed at all. As Joachim says, each interface is
>self describing, and the core simply matches up modules that export
>such an interface with those that require it.
as I suggest above, headers might still come in handy if you have fifty modules
using interface B, and don't want to keep their prototypes in sync.
>Just how the core matches up requirers with providers still needs some
>discussion. This is a good start!
in Perl, I can "use Foo" and Foo's symbols then appear in my namespace.
Let me propose the following:
1. All modules, no matter what their language, have some glue in C or C++ which
is responsible for hooking them into the core.
2. That glue should be fairly easy to write and understand. We can write
something like perl's xsubpp to automate its creation; different languages will
require different sorts of glue code, and we would need to write autogenerators
for each such language.
3. In order to import an interface, the glue code should #include a
header file containing the proper prototypes and typedefs
4. The glue code should include an _init (DLLMain on Win32) which resolves
all symbols from external modules and aborts the load if any symbols are
unresolved.
5. If the module has some initialization of its own, it can define some
specially named function (let's say "bootstrap") which the glue code will
look for and call if appropriate.
thoughts?
Jame