|Subject||Re: [Firebird-Architect] Re: External procedures: implementation|
> >>A C++ API is okay for Java and .NET that the project will distributeI found that pure-virtual classes with all stdcall methods worked
> >>the engines. But C++ programmers will need to use the same compiler
> >>vendor/version or compile everything all the server.
> >>The API is very small. Why not use C and distribute the C++ bridge
> >>for external engine writers?
> >I hope that Vlad can tell something more here - he was able to create
> >a Delphi-based external procedure with this API.
fine at least between MSVC 7.1 and Delphi 6 and Delphi 9. If it will
not work on another platform or compiler we can implement plain C
functions on the top of this classes and export it. Something like
virtual Y stdcall A(...) = 0;
Y stdcall X_A(void *ptrX, ...)
X *x = (X*)ptrX;
> In general, it isn't possible to fix C++ from different compilers. OnI hope this is not so strong now.
> Solaris, for example, it isn't possible to link modules compiled by the
> Sun Forte compiler and the gnu C++ compiler, and if I remember
> correctly, neither can dynamically load libraries produced by the
> other. In practice, any code that is going to be executed within the
> engine must be compiled by the same compiler as the engine itself.
> For engine extensions, I don't have any problem defining the interfaceAgreed
> using C++ classes. If somebody wants to produce an external procedure
> facility in Delphi, for example, it's not big deal to expect them to
> code a tiny interface modules to handle the calling sequence coming in
> and throwing the proper exceptions on the way out.Oh no, please, exceptions must not cross unit boundaries. There are
mechanism to carry exception information (better than status vector, i hope).
So - all exceptions must be handled in compilation unit which raised it
> I think there'sAgreed
> widespread agreement that the public APIs should be language neutral,
> but I don't think this should be extended to facilities to run inside
> the engine.
> It would be nicer if you threw OSRIExceptions than messed around withI replaced ISC_STATUS with class ErrorObject:
> status vectors which seem oh, so, 20 years ago.
virtual void _stdcall clear() = 0;
virtual bool _stdcall addCode(const long) = 0;
virtual bool _stdcall addString(const char*, const long) = 0;
class ExternalFunction: public ExternalPointer
virtual void _stdcall execute(ErrorObject*, int n, PARAMDSC**, PARAMDSC* = 0) = 0;
Engine (Firebird) has its own implementation of class ErrorObject and pass
instance of it in every call of external classes. When external method catch its
own exception it call ErrorObject::addCode and\or ErrorObject::addString (as
many times as needed) to pass error information. ErrorObject implementation
copied this information into engine memory and, after call returns, throws an
Firebird native exception:
a) Firebird side:
ExternalFunction* fun = ...
ResultSet* rs = fun->execute(&err, n, inputs, output);
err.check(); // here we throws an exception if needed
b) external engine (Delphi) side:
procedure TExternalFunctionImpl.Execute(Err: TErrorObject; n: Integer;
var Inputs: PParamDsc; var RetVal: TParamDsc);
msg : String;
on E : Exception do
msg := Format('[%s] %s', [E.ClassName, E.Message]);
Err.addCode(isc_external_exception); // any isc_xxx code
> >>Why ExternalResource class exist? Why create a "close" method thatThis method called destructor in real implementations. Therefore
> >>we know that will do "delete this"?
> >>Use a virtual destructor.
i renamed it to 'release' ;)
> >Not only "delete this". If I open a connection to another databaseYes
> >(Oracle for example) and start pumping records into Firebird, I need
> >some method to tell me "now you can stop and disconnect". I think this
> >would be possible to achieve with virtual destructor too, but explicit
> >method call seems to be logical here.
> I think a mechanism for registering callbacks would be a better way toThis is how it worked in prototype.
> handle this. Perhaps a two level handshake, one when the external
> procedure facility is loaded to register call back hooks and another to
> invoke a procedure.
> Or should it be per procedure?I don't think it is needed