if RxFuncAdd('ExternalMultiply', 'external', 'ExternalMultiply') <> 0 then
do
say RxFuncErrMsg()
exit E_ERROR
end
31 December 2015
In my previous tutorial I went in-depth on what has to be done to expose a foreign function to Rexx. The process was a bit long-winded, but by no means a difficult one. An astute reader, however, will have noted one small problem:
if RxFuncAdd('ExternalMultiply', 'external', 'ExternalMultiply') <> 0 then
do
say RxFuncErrMsg()
exit E_ERROR
end
That is an awful lot of boilerplate overhead that you have to use for each and every foreign function you wish to use. If only there were some way to add multiple foreign functions with a single call!
Well, there isn’t. Quite. You’ll need two calls, you see.
Let’s say I wanted to add a further external function to Rexx: ExternalAdd
,
say. It does exactly the same thing as ExternalMultiply
, but it adds its
arguments instead of multiplying them. Of course you’ll have to declare the new
function:
RexxFunctionHandler ExternalAdd;
And you’ll need to implement it:
APIRET APIENTRY ExternalAdd(PCSZ name, ULONG argc, PRXSTRING argv,
PCSZ queuename, PRXSTRING returnstring)
{
long long sum = 0;
long i;
for (i = 0; i < argc; i++)
{
sum += rexx_to_long(argv[i]);
}
long_long_to_rexx(sum, returnstring);
return RX_OK;
}
Again I’ve done the brain-damaged implementation that doesn’t error-check and is unsafe. And again I do this because I’m concentrating on concepts, not on the fiddly details that would obfuscate them when learning. |
Now, however, I am going to declare and implement one more function:
RexxFunctionHandler LoadFunctions;
APIRET APIENTRY LoadFunctions(PCSZ name, ULONG argc, PRXSTRING argv,
PCSZ queuename, PRXSTRING returnstring)
{
Registry *freg;
for (freg = FunctionRegistry; freg->ExternalName; freg++)
{
if (RexxRegisterFunctionDll(freg->ExternalName, LIBNAME,
freg->InternalName) != RXFUNC_OK)
{
return RX_ERROR;
}
}
return RX_OK;
}
This function is exposed specifically to register the rest of the functions in the API on behalf of the caller. This means that using the library and bringing in all of the exposed APIs looks like this:
if RxFuncAdd('LoadFunctions', 'externals', 'LoadFunctions') <> 0 then
do
say RxFuncErrMsg()
exit E_ERROR
end
call LoadFunctions
Of course if you look inside the LoadFunctions()
implementation, there’s data
structures in use that we’ve not looked at. These are simple enough:
#define LIBNAME "externals"
typedef struct {
PCSZ ExternalName;
PCSZ InternalName;
} Registry;
static Registry FunctionRegistry[] = {
{ "ExternalMultiply", "ExternalMultiply" },
{ "ExternalAdd", "ExternalAdd" },
{ NULL, NULL }
};
Registry
(arbitrarily named) is just a structure containing two (C-style)
strings: one for the "External name" (exactly as per RxFuncAdd()
in Rexx code)
and one for the "Internal name" (same). After that is an array of these that
has one member for each additional exported function. A pair of NULL pointers
ends the array.
A simple NULL-terminated array of strings would have done the job for us, except for the fact that there’s no guarantee that the external and internal names will match for all possible platforms this may get ported to. A bit of "unnecessary" duplication now makes fixing porting problems without code surgery much easier later. |
With this information we can now understand the working of LoadFunctions()
.
All it really does is use RexxRegisterFunctionDLL()
on behalf of the user,
effectively calling RxFuncAdd()
in native code for the caller to spare the
endless boilerplate Rexx-side. There’s nothing preventing the user from calling
RxFuncAdd()
manually (outside of common sense, I mean).
From this point it’s easy to figure out the rest. Complete code is appended.
The following blocks contain the full source code for the multiple external function implementation, the test driver, as well as a simple build script usable in a Linux environment.
/* externals.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INCL_RXFUNC
#include <rexxsaa.h>
/* helper function declarations */
static long rexx_to_long(RXSTRING);
static void long_long_to_rexx(long long, PRXSTRING);
/* external API function declarations */
RexxFunctionHandler ExternalMultiply;
RexxFunctionHandler ExternalAdd;
RexxFunctionHandler LoadFunctions;
/* symbolic return values */
#define RX_OK 0
#define RX_ERROR 1
/* function registry */
#define LIBNAME "externals"
typedef struct {
PCSZ ExternalName;
PCSZ InternalName;
} Registry;
static Registry FunctionRegistry[] = {
{ "ExternalMultiply", "ExternalMultiply" },
{ "ExternalAdd", "ExternalAdd" },
{ NULL, NULL }
};
/* external API functions */
APIRET APIENTRY ExternalMultiply(PCSZ name, ULONG argc, PRXSTRING argv,
PCSZ queuename, PRXSTRING returnstring)
{
long long product = 1;
long i;
for (i = 0; i < argc; i++)
{
product *= rexx_to_long(argv[i]);
}
long_long_to_rexx(product, returnstring);
return RX_OK;
}
APIRET APIENTRY ExternalAdd(PCSZ name, ULONG argc, PRXSTRING argv,
PCSZ queuename, PRXSTRING returnstring)
{
long long sum = 0;
long i;
for (i = 0; i < argc; i++)
{
sum += rexx_to_long(argv[i]);
}
long_long_to_rexx(sum, returnstring);
return RX_OK;
}
APIRET APIENTRY LoadFunctions(PCSZ name, ULONG argc, PRXSTRING argv,
PCSZ queuename, PRXSTRING returnstring)
{
Registry *freg;
for (freg = FunctionRegistry; freg->ExternalName; freg++)
{
if (RexxRegisterFunctionDll(freg->ExternalName, LIBNAME,
freg->InternalName) != RXFUNC_OK)
{
return RX_ERROR;
}
}
return RX_OK;
}
/* helper functions */
static long rexx_to_long(RXSTRING rexxval)
{
return strtol(RXSTRPTR(rexxval), NULL, 10);
}
static void long_long_to_rexx(long long val, PRXSTRING rexxval)
{
sprintf(RXSTRPTR(*rexxval), "%lld", val);
rexxval->strlength = strlen(RXSTRPTR(*rexxval));
}
/* test-externals.rx */
E_OK = 0
E_SYNTAX = 1
E_ERROR = 2
E_FAILURE = 3
E_HALT = 4
E_NOTREADY = 5
E_NOVALUE = 6
E_LOSTDIGITS = 7
E_UNKNOWN = 255
signal on syntax name error
signal on error name error
signal on failure name error
signal on halt name error
signal on notready name error
signal on novalue name error
signal on lostdigits name error
if RxFuncAdd('LoadFunctions', 'externals', 'LoadFunctions') <> 0 then
do
say RxFuncErrMsg()
exit E_ERROR
end
call LoadFunctions
say 'ExternalMultiply(2, 3, 4, 5, 6) returned' ExternalMultiply(2, 3, 4, 5, 6)
say 'ExternalAdd(2, 3, 4, 5, 6) returned' ExternalAdd(2, 3, 4, 5, 6)
exit E_OK
error:
type = condition('C')
if condition('I') = 'SIGNAL' then
say 'Error' type || '(' || rc || ') signalled on line' sigl || '.'
else
say 'Error' type || '(' || rc || ') called on line' sigl || '.'
say 'Description:' condition('D')
select
when type = 'SYNTAX' then
code = E_SYNTAX
when type = 'ERROR' then
code = E_ERROR
when type = 'FAILURE' then
code = E_FAILURE
when type = 'HALT' then
code = E_HALT
when type = 'NOTREADY' then
code = E_NOTREADY
when type = 'NOVALUE' then
code = E_NOVALUE
when type = 'LOSTDIGITS' then
code = E_LOSTDIGITS
otherwise
code = E_UNKNOWN
end
exit code
/* build-externals.rx */
'gcc -shared -fpic -o libexternals.so externals.c'