| DMI Basics | Features Profile | DMI Client Interface | ScriptVar | DMI Server | Using DMI in C++ | DMI with CfgScript | DMI Server Objects | Subclassing | Delegates |
ACDK provides a standard interface to Objects constructors, methods and member variables.
The usage of the DMI interface depends on the language mapping.
There are different language mappings for the different
client languages like ACDK C++, Lisp, Perl, Tcl, Python, etc.
Beside the different language mappings there is a common abstract
interface with methods like New, invoke, invoke_static, peek, peek_static,
poke and poke_static.
Object New(ClassName, Params)
|
Creates a new instance of a class.
The New maps directly the the public constructor of the ACDK class.
- Object is the new created object instance.
- ClassName is the name of the class like "acdk/lang/StringBuffer".
- Params are optional params given to the constructor.
RetVal invoke(Object, MethodName, Params)
|
Call a public, non-static (virtual or non-virtual) method of the given object.
- RetVal return value of the call.
In case the method returns no value, a VOID replacement value will be returned.
- Object is a instance of a ACDK object.
- MethodName is the name of the method to call. F.e. "append".
- Params are optional params given to the method.
RetVal invoke_static(ClassName, MethodName, Params)
|
Call a public, static method of the given object.
- RetVal return value of the call.
In case the method returns no value, a VOID replacement value will be returned.
- ClassName is the name of the class like "acdk/lang/Integer".
- MethodName is the name of the method to call. F.e. "toHexString".
- Params are optional params given to the method.
RetVal peek(Object, MemberName)
|
Reads a public, non-static member of the given object.
- RetVal the member variable.
- Object is a instance of a ACDK object.
- MemberName is the name of the member.
RetVal peek_static(ClassName, MemberName)
|
Reads a public, static member of the given class.
- RetVal the member variable.
- ClassName is the name of the class like "acdk/lang/System".
- MemberName is the name of the member like "out".
void poke(Object, MemberName, NewVal)
|
Writes a public, non-static member of the given object.
- Object is a instance of a ACDK object.
- MemberName is the name of the member.
- New is the new member variable.
void poke_static(Object, MemberName, NewVal)
|
Writes a public, static member of the given class.
- ClassName is the name of the class like "acdk/lang/System".
- MemberName is the name of the member.
- New is the new member variable.
For other DMI specific features, the language mappings
differs, resp. some features are not supported in
some language mappings.
ACDK supports polymorphic function via DMI.
The standard DMI uses dynamic overloading.
Not the declared type of an argument will be called
but the real type.
class TestClass
: extends acdk::lang::Object
{
ACDK_WITH_METAINFO(TestClass)
public:
void foo(int i);
void foo(IN(RObject) obj);
void foo(IN(RInteger) obj);
};
RObject testclass = new TestClass();
testclass->invoke("foo", inOf(42)); // invokes void foo(int i);
RObject obj = new String("");
testclass->invoke("foo", inOf(obj)); // invokes void foo(IN(RObject) obj);
obj = new Integer(42);
testclass->invoke("foo", inOf(obj)); // invokes void foo(IN(RInteger) obj);
// and not void foo(IN(RObject) obj);
|
C++ enumeration as arguments or return type are mapped to ints.
Although possible in C++ a function with same name and different
enum types or an integer is not possible in ACDK DMI.
The argument of an enumeration can be passed as int or as
String with valid enumeration value name. The String contains
either the enumeration value string, or with leading namespace
to avoid ambiguities. The return value or out parameter are
always return as integer.
The metacompiler can only handle enumeration, which are
declared in the same header of the class. If the enumeration
is defined in another header file, the enumeration has to
be declared before used as parameter (see OtherEnum below).
Inside the DMI dispatching implementation unknown classes
are tried to be loaded via classloader. This is not the case
for unknown enumeration and enumeration values.
enum TestEnum
{
TestEnumVal1 = 42,
TestEnumVal2 = 43
};
ACDK_DEF_ENUM(TestEnum);
// defined in other header
enum OtherEnum;
class TestClass
: extends acdk::lang::Object
{
ACDK_WITH_METAINFO(TestClass)
public:
void foo(TestEnum en);
foreign foo(int i); // not dmi-able, conflicts with first foo
void bar(OtherEnum en);
};
RObject testclass = new TestClass();
testclass->invoke("foo", inOf(42)); // OK
RString enval = "TestEnumVal2";
testclass->invoke("foo", inOf(enval)); // also OK
|
If you define enumeration in shared libraries
you have to use instead of the ACDK_DEF_ENUM macro
the ACDK_DEF_LIB_ENUM macro.
Please refer also to Enum Types.
Default arguments are supported on DMI server side.
Given ACDK class:
class TestClass
: extends ::acdk::lang::Object
{
ACDK_WITH_METAINFO(TestClass)
public:
void foo(IN(RObject) obj, int count = -1);
}
|
two DMI entry points will be generated:
void foo(IN(RObject) obj);
void foo(IN(RObject) obj, int count);
|
The DMI client implementation must support Named Parameters.
- ACDK DMI C++ client.
- ACDK CfgScript.
// C++/CfgScript
void callIt(IN(RString) str, int ival);
// CfgScript
obj->callIt(ival: 42, str: "asdf");
obj->invoke("callIt", NamedArgs(NamedArg(ival, 42) << NamedArg(str, inOf("asdf"))));
|
// C++/CfgScript
void callIt(IN(RString) str = "", int ival = 42);
// CfgScript
obj->callIt(ival: 1);
// C++
obj->invoke("callIt", NamedArgs(NamedArg(ival, 42)));
|
The Any rest parameter is similar to the eclipse ... in C/C++.
It must be the last parameter in the method declaration.
All passed arguments, which are not consumed by previous parameters are
collected by the rest parameter.
// C++
void callIt(IN(RString) str, IN(acdk::lang::dmi::RDmiObjectArray) rest);
// CfgScript
void callIt(String str, Rest rest);
// C++
obj->invoke("callIt", "first", inOf("second"), inOf(42));
// CfgScript
obj.callIt("first", "second", 42);
|
The dispatching routine try to find a method with many as possible
non-rest parameters:
// CfgScript
class MyClass {
static void callRest(Rest rest); // first method
static void callRest(float r, Rest rest); // second method
}
MyClass.callRest(42, "asdf", 43.3); // this calls the second method,
// because it can bind the int to the
// float r variable
MyClass.callRest("asdf", "asdf", 43.3); // this calls the first method
#pragma cast f2i2f false
MyClass.callRest(42, "asdf", 43.3);
|
Named parameter are similar to the Rest Parameter, but collects the
named arguments.
// C++
void callIt(IN(RString) str, IN(acdk::lang::dmi::RDmiNamedArgArray) rest);
// CfgScript
void callIt(String str, NamedRest rest);
// C++
obj->invoke("callIt", "first", inOf("second"), inOf(42));
// CfgScript
obj.callIt("first", "second", 42);
|
IN, OUT and INOUT are argument attributes, which declarares the
direction of the argument passing.
- IN parameter are passed from caller to callee.
This is also the default, if the argument has no attribute.
- OUT parameter are passed from callee to caller.
- INOUT parameter are passed from caller to callee and
back.
As said before parameter without any directional attributes, like IN, OUT or INOUT
are handled like IN parameter.
This is not the whole story.
void foo(RStringBuffer sb, int i)
{
// this assigns a new instance
sb = new StringBuffer("asdf");
// this is legal, but the caller doesn't
// recognise, that sb had changed
i = 42; // this is OK, but caller
// will not be effected
}
void foo(IN(RStringBuffer) sb, IN(int) i)
{
// although sb is in, you can call function
// which may modify the internal value of the reference
sb->append("asdf");
// but this is illegal in C++ and you will receive a compiler error
// in CfgScript this is legal and works the same way like the paramater
// without any in/out attribute
sb = new StringBuffer("asdf");
}
|
In the ACDK standard libraries most parameter with an object reference are declared
as IN parameter. The main reason is simply performance, because the
passed argument has not be copied.
To support OUT and INOUT the client language mapping must
support references.
With OUT parameters it is possible to pass multiple return value
back to the caller.
// C++
bool parseFileAndLine(IN(RString) line, OUT(RString) fname, OUT(int) lineNo)
{
int idx = line->lastIndexOf(':');
if (idx == -1)
return false;
fname = line->substr(0, idx);
lineNo = Integer::parseInt(line->substr(idx + 1));
return true;
}
// CfgScript
bool parseFileAndLine(IN(RString) line, OUT(RString) fname, OUT(int) lineNo);
// or
bool parseFileAndLine(in String line, out String fname, out int lineNo);
|
The BYVAL and BYREF attributes are used by DMI remoting interfaces to specify
how the parameter should be tranfered. In which case a argument is passed
by default is specific to the particular remote DMI implementation.
The BYVAL and BYREF attributes has no effect on lokal invokations.
As you seen implicit in the samples, DMI supports class with multiple
methods with the same method name but different argument number and/or types.
// CfgScript / C++
class AClass
{
void foo(int i);
void foo(int i, int x);
void foo(IN(RString) s);
void foo(IN(RObject) s);
void foo(double d);
void foo(float f, bool isFlaot = true);
}
class BClass
: extends AClass
{
void foo(short s);
};
RAClass cls = new BClass();
// CfgScript
float f = 42.5;
cls.foo(f);
// same in C++
cls->invoke("foo", inOf(f));
|
The thrilling question: which foo will be called?
The short answer: It depends on the implementation
of the DMI server.
ACDK provides a standard implementation (can be found in StdDispatch.cpp)
which is used in the C++, CfgScript and RDMI ( Remote DMI) implementation.
In the standard implementation void foo(float f, bool isFlaot = true) will be called.
The standard implementation inspect all methods with theire parameter types -
the return type will be ignored - and try to find the best matching method signature.
Therefore all arguments types will be compared with the parameter types and
a type distance will be calculated (exact match has the type distance 0).
The method with the least type distance for all methods will be selected.
Methods with default parameter will be split into multiple methods:
// C++/CfgScript
void foo(float f, bool isFlaot = true);
// DMI view:
void foo(float f);
void foo(float f, bool isFlaot);
|
In C++ the selection proper version of foo (see above) will be steered
by the declared types:
// C++
RObject obj = new String("asdf");
cls->foo(obj);
|
will call void foo(IN(RObject) s); .
Calling the method via dmi the sitation is different:
// C++
RObject obj = new String("asdf");
cls->invoke("foo", inOf(obj));
// CfgScript
cls->foo(obj);
|
will call void foo(IN(RString) s); , because the method
will be selected at runtime using the real type of the argument
and not the declared.
This mechanism is called double dispatching or multimethods
because method will not only selected in virtual calls by the real type of the object (cls in
our example), but only by the real type of the objects passed as arguments.
The central aspect of selecting the proper method is the type distance
between argument values and parameter declarations.
Type distance is just a more detailed description of the casting capilities
between types. The casting features of a DMI server can be controled
by a implementation of the acdk::lang::dmi::DmiClient interface.
In CfgScript there are #pragm cast
statements to control casts, and type distance between types, so it can
be controled if bool type can be cast to int types and vice versa, cast character
types to integer types, cast basic types to object types (autoboxing) and vice versa,
and even cast String types to basic types try do decode "12.3" to 12.3 (like Perl or Tcl).
Some DMI Server objects - CORBA or COM components -
doesn't support overloaded methods. In case of
native COM or CORBA the question simply doesn't exists.
In the case COM or CORBA is used as a bridge to serve
for example ACDK C++ classes as COM objects, a namemangling
has to be introduce to address a concrete foo-version.
ACDK supports also operator methods via DMI:
// C++
class StringBuffer
{
// ...
RStringBuffer operator<<(IN(RString) str);
};
// CfgScript
class Average
{
// ...
Average operator+(int value);
}
|
The operator are exported via namemangling.
Calls an operator function via DMI:
// C++
Object sb = new StringBuffer();
sb->invoke("operator_pl", 42);
// CfgScript
Average average = new Average();
average.operator_pl(42);
|
|