[ooc-compiler] PROPOSAL: Interface to C++ objects and methods
Brought to you by:
mva
|
From: Stewart G. <sgr...@ii...> - 2006-01-12 09:10:31
|
Hi Folks,
I'm interested in being able to use existing C++ APIs from Oberon-2.
Currently, oo2c has a limited ability to call methods of C++ objects.
When one declares a record with the VTABLE flag, oo2c builds a C++-style
virtual method table for all instantiated objects, and calls methods of
that object via VTABLE dispatch. This means (for example) that you can
call methods of COM interfaces, and even implement your own COM objects.
This is fine if one follows a design methodology like COM, which ensures
that there is a language-independent binary interface to your API. COM
uses abstract interfaces and VTABLE method dispatch. It also has a
language-independent type mechanism (ie. IUnknown::QueryInterface).
Unfortunately, most C++ coders seem completely uninterested in language
interoperability. The biggest obstacle with existing C++ APIs is the use
of static (ie. non-virtual) methods. Its only really possible to call
these methods from a C++ compiler, since the symbol names are mangled to
include type information.
The approach that I'm proposing is to:
1) Introduce a [STATIC] method flag. This causes calls to methods to be
dispatched statically, rather than using type-descriptor lookup.
2) Write (by hand, or automatically) a FOREIGN implementation that can
be processed by the C++ compiler. This dispatches method calls to the
C++ objects.
I'll try to illustrate this by means of an example. Suppose we have a
simple C++ library:
-- Foreign.h --
class T {
public:
virtual void Virtual(void);
void Static(void);
};
-- end Foreign.h --
-- Foreign.cpp --
#include <stdio.h>
#include "Foreign.h"
void T::Virtual(void) {
printf("T::Virtual\n");
}
void T::Static(void) {
printf("T::Static\n");
}
-- end Foreign.cpp --
Using a VTABLE declaration, I can call method "Virtual", but not method
"Static". Now OOC already has a concept of static methods. A method is
called statically (ie. without using a type-descriptor method lookup) if
it is known not to be overridden, or if it is a special predefined
method like INIT. I'm proposing to add a "STATIC" procedure flag, which
specifies that a method is to be treated as a static method (ie. it has
procClass = staticMethod) rather than a virtual method. The address of
the method is always based on an objects static type.
This allows us to write:
-- ForeignStatic.Mod --
MODULE ForeignStatic [ FOREIGN "C"; LINK FILE "ForeignStatic.cpp"; LIB
"foreign"; LIB "stdc++" END ];
TYPE
TDesc* = RECORD [VTABLE] END;
T* = POINTER TO TDesc;
PROCEDURE (t : T) Virtual*;
PROCEDURE (t : T) [STATIC] Static*;
PROCEDURE NewT* () : T;
END ForeignStatic.
-- end ForeignStatic.Mod --
Now to make things work, we need the FOREIGN implementation. For the
above library, it looks like this:
-- ForeignStatic.cpp --
#include "Foreign.h"
extern "C" {
#include <ForeignStatic.d>
#include <__oo2c.h>
#include <setjmp.h>
void ForeignStatic__TDesc_Virtual(ForeignStatic__T t) {
((T*) t)->Virtual();
}
void ForeignStatic__TDesc_Static(ForeignStatic__T t) {
((T*) t)->Static();
}
void OOC_ForeignStatic_init(void) {
return;
;
}
void OOC_ForeignStatic_destroy(void) {
}
ForeignStatic__TDesc* ForeignStatic__NewT(void) { return
(ForeignStatic__TDesc *) new T(); }
}
/* --- */
-- end ForeignStatic.cpp --
To generate this, I compiled a "stub" module using oo2c, and manually
inserted a few things.
- Included the header for the C++ library.
- The "extern C" is necessary so that the C++ compiler does not mangle
the symbol names.
- Inserted dispatch statements in any methods that are to be called
statically. Actually, "Virtual" is alreadly dispatched by OOC through
the VTABLE, but I could have allowed C++ to do the virtual dispatch here
by declaring it STATIC.
- Inserted a constructor.
This allows one to do this sort of thing:
-- TestForeignStatic.Mod --
MODULE TestForeignStatic;
IMPORT ForeignStatic;
PROCEDURE TestForeignStatic1;
VAR t : ForeignStatic.T;
BEGIN
t := ForeignStatic.NewT();
t.Virtual;
t.Static;
END TestForeignStatic1;
BEGIN
TestForeignStatic1;
END TestForeignStatic.
-- end TestForeignStatic.Mod --
That is, I can call virtual or static methods of objects as if they are
native objects. Note that I don't need to change the calling conventions
used within OOC. It already handles virtual and static dispatch. The
wrapper functions simply redirect the method dispatch using the host
compiler.
Anyway, with a bit of fiddling I managed to get it to work. The STATIC
flag was easy to implement, but it would be nice if this process could
be streamlined somehow. Ideally, the FOREIGN wrapper implementation
should be generated automatically by OO2C. This should not be hard to do
- If we can get some consensus regarding how it should work I'm willing
to have a go at it. Since the wrapper requires the use of
implementation-specific symbol names and structures it would make sense
for the compiler to provide this feature, rather than an external tool.
1) There would need to be a declaration that this is a special type of
FOREIGN module (eg. "WRAPPER", or "PROXY"?). Records declared within are
assumed to have no type descriptors. Stub procedures are generated that
dispatch methods in the host ("C++") language.
2) There should be a list of headers to include at the top of the object
file.
3) For each record type, we need an optional "link name" saying what the
proxy C++ type is called. This would be used to generate the type casts
in the wrapper methods.
4) For each method, we need an optional "link name" saying what the
method of the proxy C++ type is called. In the case of an overloaded
method, there will be multiple Oberon-2 names that map to the same C++ name.
5) There would need to be a way of declaring constructors which would be
dispatched via new().
What do people think about these issues? Any suggestions, or violent
objections? ;-)
Cheers,
Stewart
|