Implementation Overview
Before you can harness the power of the SCEX calculation engine, you first need to be able to call the function(s) contained in the library from your development language.
Functional Interface
To make use of the SCEX, you will need to import one (and only one!) function
from the library into your programming environment. Any programming language
which can access functions inside shared libraries on your SCEX platform of
choice (current platforms supported include win-32, win-64 and linux-x86.64) and
has the appropriate data types necessary to call the xmlcalc_ddp()
routine
should be able to make use of the SCEX.
The only function exported from the SCEX library that is required for
implementation (and hence the only one you need to import) is called
xmlcalc_ddp()
. Here is the function declaration for xmlcalc_ddp()
, in Delphi
(object pascal, and the language in which the SCEX is written):
function xmlcalc_ddp(
xmlIn, xmlOut: PAnsiChar;
lenIn, lenOut: integer;
var reqIn, reqOut: integer;
DataDirPath: PAnsiChar
): integer; stdcall;
Note that for the Linux platforms, the stdcall
calling convention is replaced
with cdecl
.
The variable names are found to the left of the :
, with the variable type
found to its right. This function uses two types of variables: PAnsiChars
and
integers
. Integers are 32-bits in size, and are signed. PAnsiChars
are
pointers to ANSI character buffers, and in C are expressed as char *
. The
var
modifier indicates that the reqIn
and reqOut
variables are being
passed by reference instead of by value (like the variables xmlIn
, xmlOut
,
lenIn
and lenOut
are).
Now, let’s see how to go about importing this function in five different languages: Delphi, Visual C++, C#, vb.net, and Java.
Delphi
As the SCEX DLL is written in the Delphi programming environment, it is to be expected that importing the function into a Delphi application is a fairly simple process.
Place the following function declaration in the interface section of a Delphi unit:
function xmlcalc_ddp(
xmlIn, xmlOut: PAnsiChar;
lenIn, lenOut: integer;
var reqIn, reqOut: integer;
DataDirPath: PAnsiChar
): integer; stdcall; external 'SCEX.DLL'
In this same unit, place the following code in the interface section. Please note that there is no semicolon at the end of this line, and that the SCEX DLL must reside in the same location as your application for this method to work.
Visual C++
To use the xmlcalc_ddp()
routine in your Visual C++ application, you will need
to dynamically load the library in your code and get the process address for the
xmlcalc_ddp()
function in that Library. This is best done in your
application’s initialization routines, so that once your application has started
the function is ready to accept calls. When you terminate the application, then
you free up the dynamically loaded library in your code’s termination routine.
The relevant Windows API calls which are used in this process are:
LoadLibrary()
, GetProcAddress()
, and FreeLibrary()
. For more information,
please see the sample code (installed when you install the SCEX Sample Source
Code component during the SCEX installation).
C#
To import that xmlcalc_ddp()
function into your C# project from scratch, you
will need to use System.Runtime.InteropServices
. Furthermore, you will also
need to be familiar with the StringBuilder
class in System.Text
, which is
the C# equivalent of a character buffer.
For a complete SCEXWrapper class, please see the sample code (installed when you install the SCEX Sample Source Code component during the SCEX installation).
Java
As of release 2018-07-0 of the SCEX, a Java Native Interface function is
included in the SCEX library for all supported platforms, and a Java class which
implements the JNI call is provided in the sample source code directory
java\Scex.java
(note that if you did not install the sample source code during
installation, then this file will not be present).
To illustrate the use of the Scex.java
class, we have also included a simple
Java class (Main.java
) which creates an instance of the Scex
class, sets up
an SCEX XML query, and then calls the SCEX and displays the result code and
resulting XML.
Note that when using the JNI interface to the SCEX, you must always specify a
fully qualified path name for the DTD file, and you must also always specify the
DataDirPath
attribute for those XML queries which accept one.
If you wish to modify any aspects of the Scex.java
file, please carefully read
the comments included in it. The JNI function included in the SCEX DLL expects
and requires many different facets of class Scex
to be as described, and
modifying these will result in a non-functional interface. It is our
recommendation that Scex.java
be left as is unless you are very familiar with
the JNI.
Implementing the xmlcalc_ddp()
Function
If you are directly calling the xmlcalc_ddp()
function exported from the SCEX
DLL (instead of using one of the wrapper classes provided with the sample source
code), then the next step is to understand the function’s parameters and return
results. If you are using one of the provided wrapper classes, then
implementation is a bit easier.
Calling xmlcalc_ddp()
Directly
If you wish to directly access the xmlcalc_ddp()
function, then there are
three facets which you must consider: variable declarations, memory allocation
and result interpretation.
Variable Declarations
There are four classifications of variables passed to the xmlcalc()
routine:
pointers to character buffers, allocated buffer sizes, required buffer sizes,
and the default data directory path.
The xmlIn
and xmlOut
variables are pointers to character buffers which your
application has allocated (see Memory Allocation, below). The input buffer
xmlIn
is filled by your application before calling xmlcalc_ddp()
, and its
contents represent an XML query sent to the SCEX. The result of the query will
be returned in the xmlOut
buffer, so long as no errors are encountered (see
Result Interpretation, below).
The lenIn
and lenOut
variables are the lengths of the associated buffers. It
is critical that your application correctly set the values of these variables to
represent the allocated lengths of the input and output buffers, respectively.
By informing the SCEX of the size of the input and output buffers, buffer
over-runs and the errors that they cause will be non-existent.
The reqIn
and reqOut
are return parameters used to indicate the necessary
size of the input and output buffers. These two result parameters are very
useful when you have not allocated enough space for the input or output buffers,
as indicated by a given function result code (see Result Interpretation, below).
Finally, the DataDirPath
specifies a default data directory for the SCEX to
use if one is not supplied in the XML input. Please see documentation on the
DataDirPath
attribute in the chapter covering Equal Payment Loans for the
meaning of this variable.
Memory Allocation
As noted above, providing a buffer into which both the SCEX and your example can copy xml code is of paramount importance. The exact function calls and variable types that you must vary from language to language. Here are a few samples:
-
Delphi: To allocate memory for the character buffers which will hold the
xmlcalc_ddp()
input and output, you can useGetMem(buffer, length)
, where buffer is of typePAnsiChar
(a pointer to character) andlength
is the requested length of the character buffer, in bytes. Once you are done with the character buffers, make sure to free up the memory which was dynamically allocated using theFreeMem(buffer)
procedure, where buffer is again aPChar
. -
C, Visual C++: To allocate memory for the input and output buffers in C and Visual C++, use the standard library functions
malloc()
andfree()
. See the Visual C++ sample application for an example of how the buffers are dynamically allocated. -
C#, vb.net: Use the
StringBuilder
class fromSystem.Text
. See sample code for an example of its use (and a more user friendly wrappers aroundxmlcalc()
).
Result Interpretation
After calling xmlcalc_ddp()
, you should always check the integer return value
to ensure that the call finished successfully, and if it did not, what kind of
error occurred. Please see Table 3.1 on page 13 for a comprehensive list of
result codes accompanied by a description of each.
Result Code | Description |
---|---|
0 | Successful calculation. Results are in xmlOut . |
1 | Not enough input space reserved. More buffer space is required to write the XML inputs. The minimum size required for the input buffer is returned in the reqIn parameter. |
2 | Not enough output space reserved. More buffer space is required to write the XML outputs. The minimum size required for the output buffer is returned in the reqOut parameter. |
3 | XML Output is not well formed. XML output violates at least one rule defining well-formed XML data. |
4 | XML output is not valid. |
-1 | XML input is not well formed. XML input violates at least one rule defining well-formed XML data. |
-2 | XML input is not valid. (See the various query interface chapters for definitions of valid XML input). |
-3 | An exception occurred within the SCEX. |
Optional API Functions
Besides the very important xmlcalc_ddp()
routine exported from the SCEX, there
are other helpful routines which may be of use during development. Each of these
routines is described below.
scex_calc_alloc()
and scex_calc_free()
The scex_calc_alloc()
routine behaves identically to the xmlcalc_ddp()
routine, with the exception that the library function will automatically
allocate the necessary output buffer. When first calling this routine, ensure
that the output buffer is initialized to NULL
and the size of the output
buffer is set to zero.
Using this function will prevent calling xmlcalc_ddp()
twice when the space
allocated by the calling application for the output buffer is too small, and
thus can effectively cut the calculation speed in half in these instances.
Since the library is allocating the space necessary for the XML output buffer,
the library also must be responsible for freeing this allocated space. Thus,
there is the associated scex_calc_free()
library function.
Note that many of the sample code wrappers provided have been enhanced to use these new routines to ensure maximum performance.
Here are the function declarations for scex_calc_alloc()
and
scex_calc_free()
, in Delphi (object pascal, and the language in which the SCEX
is written):
function scex_calc_alloc( InBuff : PAnsiChar;
var OutBuff : PAnsiChar;
InBuffSize : integer;
var OutBuffSize : integer;
RootPath : PAnsiChar
): integer; stdcall;
function scex_calc_free( var OutBuff : PAnsiChar;
var OutBuffSize : integer
): integer; stdcall;