Native bindings for these languages are available only on the level of functions. Even for Java, native methods of an object are defined as functions that receive a handle to the invoking object. Given a language with objects, possibly by adopting an object extension for the languages without objects, the problem is to find a proper correspondence between objects defined in the high-level (script) language and the native objects defined in C/C++.
In this (sub)section we will first study an extension of Prolog with objects, and then indicate a solution to establish a close correspondence between the (Prolog) objects and their native counterparts. In the next (sub)section, we will apply this approach to establish a correspondence between Java and C++ objects.
Objects (or classes of objects, if you prefer) are defined by a collection of clauses with a head predicate of the form class_method(This,...), specifying the class, method and object identity parameter. The actual invocation of the method takes the form self(This):method(...), where the colon acts as the familiar dot object access parameter. Note that the identity parameter (This) does not occur among the method parameters, but is instead contained in the object specifier. Instead of the keyword self, we may also use a class name to enforce a cast to specific object type when invoking the method. In the actual object extension, we also support object state instance variables, which are however not relevant for our discussion here.
Object methods may be defined as native by including a goal of the form native(Handler, Method, Result), where Handler specifies the (native) handler to be invoked, Method the actual request, and Result a variable to store the possible outcome of the request. When the Handler parameter is left unspecified, the handler defined for the object will be taken to effect the native call.
Let's look at some examples first, to augment this
admittedly concise description.
create midi object
Self = self(This),
Self:open('a.mid'),
Self:header(0,1,480),
Self:track(start),
Self:melody([48,50,51,53,55]), // c d es f g, minor indeed
Self:track(end), end track
:- use(library(midi:[midi,lily,music,process])).
:- declare(midi:object,class(midi),[handler]).
midi_midi(This) :- constructor
midi(This):handler(H), // gets Handler from class
declare(H,new(midi(This)),[],[],_).
native methods
midi_read(This,F) :- native(_,This,read(F),_).
midi_analyse(This,I,O) :- native(_,This,analyse(I,O),_).
midi_open(This,F) :- native(_,This,open(F),_).
midi_header(This,M) :- native(_,This,header(M,0,480),_).
midi_track(This,X) :- native(_,This,track(X),_).
midi_tempo(This,X) :- native(_,This,tempo(X),_).
midi_event(This,D,C,M,T,V) :- native(_,This,event(D,C,M,T,V),_).
cast to midi
Self:event(D,C,note_on,T,V),
Self:event(D,C,note_off,T,V).
midi_melody(This,L) :- self(This):melody(480,1,L,64).
midi_melody(_This,_,_,[],_).
midi_melody(This,D,C,[X|R],V) :-
Self = self(This),
Self:note(D,C,X,V),
midi_melody(This,D,C,R,V). direct invocation
C++ bindings
As outlined in section Reactor, in the hush framework we use an event-based
mechanism to effect foreign language bindings.
This means that the information concerning the native call
is stored in an event object that is passed to a handler,
which invokes the operator function
on the occurrence of an event.
In the code fragment below it is shown how native method
dispatching is taken care of in the operator
function of a C++ kit_object, for which
a corresponding object in Prolog is assumed to exist.
smart pointer
string method = e->_method();
if (method == "kit") { constructor
kit* q = new kit(e->arg(1));
_register(q);
result( reference((void*)q) );
} else if (method == "eval") {
long res = self->eval(e->arg(1));
result( itoa(res) );
} else if (method == "result") {
char* res = self->result( atoi(e->arg(1)) );
result(res);
} else { dispatch up in the hierarchy
return handler_object::operator()();
}
return 0;
}
public:
vm(event* e) {
int p = 0;
char* id = e->option("ref");
if (id) {
p = atoi(id);
}
_self = (T*) p;
}
virtual inline T* operator->() { return _self; }
private:
T* _self;
};