#clear
#clear parameter_list
This directive deletes all annotations which match the given parameter_list.
The directive
#clear double *result
would delete the annotation applied above to ksink_mult2, causing
the generated wrapper to assume the default form (expecting 3 input
arguments, and returning nothing on the stack). The directive sequence
#clear double *result
#copy double *OUTPUT {double *result}
also eliminates the annotation, but then replaces it with a copy
of the default double* outmap.
#copy
#copy parameter_list { parameter_list [, ...] }
This directive provides a convenient way of targeting the application
of existing #argmap annotations to specific functions, without redeclaring
their prototypes. For each destination parameter_list pattern a copy is
created of every #argmap which matches the source parameter_list.
The parameter_list of each duplicate annotation is then changed to the
corresponding destination parameter_list. Note that any usage qualifier
specified in the original annotation will not be propagated, since
such content is usually tailored specifically to the original type.
The ksink_mult2 transformation given in the previous chapter could
have been accomplished more easily by copying the double *OUTPUT
mapping introduced in section
Builtin_Out_Maps:
#copy double *OUTPUT { double *result }
An error will be thrown if any destination parameter_list is not isomorphic
with the source parameter_list. That is, a single-argument copy cannot
be made from a multi-argument annotation, and vice-versa.
#define
#define C_identifier substitution_text
This directive provides a cleaner and more compact alternative to
the slirp_define_macro() function. It may be used to define
simple numeric and (quoted) string constants, or perform verbatim
substitution of non-quoted string literals, but not to define
parameterized macros. Attempts to #define a macro more than once,
without first using #undef, will be honored with a warning.
#define PLATFORM "unix"
#define VERSION 5
#define MY_PI 3.1415926535897932384
#define BEGIN_DECLS
The first three directives define a string, integer, and double
constant, respectively; the last ensures that if BEGIN_DECLS is
seen while processing source files it is replaced with the empty
string, instead of the definition specified within the source.
#ignore
#ignore
symbol_name_list
#end
This directive provides an alternative mechanism for instructing SLIRP
to bypass the generation of wrapper code for one or more functions,
macros, variables, or type definitions. For example, rather explicitly
adding symbol names to the ignored_functions, ignored_macros,
or ignored_variables arrays, you would instead place the symbol
names within an #ignore block.
This approach yields shorter and cleaner interface files, since the
names of all ignored symbols may be grouped within a single semantic
block (although this is not required, as multiple #ignore blocks are
permitted), symbol names remain unquoted, and comma delimiters may
be avoided.
Several kitchensink symbols can be omitted from its corresponding module via:
#ignore
ksink_mult % some functions ...
ksink_print_array_f, ksink_datum_new
KSINK_IGNORABLE_INT % a variable
ksink_datum_destroy % another function
#end
#inline_c
#inline_c [(init)]
C_translation_unit
#end
This directive supports the inclusion of essentially arbitrary C source
code within your interface file. Unlike with #argmap or #retmap
fragments, however, inlined source is injected verbatim into the
generated code; that is, no parameter or variable substitutions are
performed.
Normally inlined code blocks are inserted into the generated source
before any wrapper functions are defined, which permits #argmap and #retmap
annotations to reference their content. The init qualifier may
be used to specify that the code should instead be injected into the
module initialization function.
Suppose you were wrapping a C++ library which includes a ProcessGlobal
class that is intended to be instantiated only once per application.
Ordinarily, methods of this (or any) class would be called from S-Lang
scope by
passing in an instance of the class as its first argument. For example,
a report() method with a zero-argument C++ signature would be
invoked from S-Lang scope as
variable pg = ProcessGlobal_new();
ProcessGlobal_report(pg);
This is exactly how C++ invokes object methods, the only difference
being that in C++ the first argument, this, is generated by
the compiler and usually hidden from the programmer. You could
achieve a similar effect by instantiating a hidden ProcessGlobal
object within in your wrappers
#inline_c
ProcessGlobal *pg = new ProcessGlobal();
#end
and then omitting the ProcessGlobal* argument from each method
#argmap(in, omit) ProcessGlobal*
$1 = pg;
#end
to yield a wrapper (see examples/cpp for more details) such as
static void sl_ProcessGlobal_report (void)
{
ProcessGlobal* arg0;
if (SLang_Num_Function_Args != 0)
{Slirp_usage_err(13, 13); return;}
arg0 = pg;
arg0->report ();
}
As another example, consider that an annotation resembling
#inline_c(init)
if (H5open () != 0) return -1;
#end
is used to generate the HDF5 module, which ensures that the HDF5 library
is itself initialized when the S-Lang interpreter initializes the module.
#prototype
#prototype
function_prototype_list
#end
This directive supports the declaration of one or more function prototypes directly within a SLIRP interface file. These supersede prototypes declared within header files, and are used to steer the pattern matching and code generation processes by relabeling parameters to match annotations.
A third way to achieve the ksink_mult2 transformation
given in the previous chapter would be to redeclare it as
#prototype
void ksink_mult2 (double op1, double op2, double *OUTPUT);
#end
This allows SLIRP to match it against the built-in double *OUTPUT
argmap, and would yield the same wrapper code as before. Note that,
as in C, each function declaration must be terminated with a semicolon.
#rename
#rename SLang_regular_expression C_identifier
This directive supports renaming one or more functions in the
same manner as does the -rename command line option.
#rename sin vsin
#rename strlen vstrlen
The examples/vec sample code vectorizes
the C sin and strlen functions, among others. These
#rename directives are used in the example interface file to
ensure that the vectorized wrappers are distinguishable from
the corresponding native S-Lang intrinsics.
#retmap
#retmap [(omit)] C_type_expression
code_fragment
#end
By default SLIRP generates code to pass the return value from a wrapped
function back to S-Lang scope, typically by having the last statement
within the wrapper push an object of the appropriate type onto the stack.
The #retmap directive can be used to alter this scheme, allowing a
wrapper to return an object of different type, more than 1 object, or
even --- if the omit qualifier is specified --- no objects at all.
Suppose you were wrapping a library in which some functions were prototyped to return an integer status code, such as
typedef enum {
STATUS_NULL_POINTER_REF=-2,
STATUS_OUT_OF_MEMORY=-1,
STATUS_GOOD=0
...
} StatusCode;
int do_something(int i, int j);
int do_something_else(int i, int j);
To ensure robustness the values returned from these functions must
be checked, regardless of how unlikely the functions are to fail.
This yields S-Lang code such as
variable i = 100, j = 1, k = 200;
if (do_something(i, j) != STATUS_GOOD)
error("Could not do_something");
if (do_something_else(i, j) != STATUS_GOOD)
error("Could not do_something_else");
if (do_something_else(k, j) != STATUS_GOOD)
error("Could not do_something_else");
...
If failure is a relatively rare event then this (necessary) strategy
results in code that is not only longer and slower, but is also harder
to read. However, by using the omit qualifier to generate a
wrapper which swallows the return status code, such as
#retmap(omit) int
if (result != STATUS_GOOD)
SLang_verror(SL_INTRINSIC_ERROR,"Wrapper error: %d",result);
#end
we retain the same level of robustness (an error will still be signaled
whenever a library function returns something other than STATUS_GOOD)
while eliminating boilerplate safety code in S-Lang scope:
variable i = 100, j = 1, k = 200;
do_something(i, j);
do_something_else(i, j);
do_something_else(k, j);
...
#retmap AnnotationsAs a convenience SLIRP provides the following return maps:
#retmap NT_STR_ARRAY
push_null_term_str_array($1, $funcname, 0);
#end
#retmap NT_STR_ARRAY_FREE
push_null_term_str_array($1, $funcname, 1);
#end
which can be used to automate the generation of wrappers for functions
which return string arrays of known length. To see how these might be
used let's return to the Account* example given earlier in section
Pointers
char** account_get_users(Account *a);
only now suppose that the documentation for this hypothetical function
states that the final element of its return value is NULL. With this
knowledge we can determine
the length of the array by traversal, and thereby return a more useful
S-Lang array of String_Type, rather than merely an opaque string
pointer. To apply the appropriate return map simply redeclare the
function within your interface file, such as
#prototype
NT_STR_ARRAY account_get_users(Account *a);
#end
or, if you'd like the wrapper to free the C array and all of its
elements prior to returning,
#prototype
NT_STR_ARRAY_FREE account_get_users(Account *a);
#end
These annotations work because, as described below, NT_STR_ARRAY
and NT_STR_ARRAY_FREE are built-in SLIRP type definitions.
#typedef
#typedef C_identifier C_identifier [;]
This directive supports the definition of simple types, as described in section TypeDefinitions, directly within a SLIRP interface file.
Consider applying the finalizer
#argmap(final) KInt (int copy)
copy = $argnum;
printf("Finalizing arg %d of %s (with local copy)\n",copy,$funcname);
#end
to the kitchensink module. Because input header files (in this case ksink.h)
are not read until after SLIRP processes the interface file no type
map entry (associating KInt with int) will be defined when
this annotation is read. In such cases (in fact, whenever an unmapped type
is encountered) SLIRP will either fabricate a mapping (e.g. to void_ptr)
for it and proceed on its merry way (the default), or signal an
"unmapped type" error (when the -noautotype switch has been
specified). A #typedef annotation such as
#typedef int KInt;
can be used to steer the mapping if neither of these results are desired.
Types defined in this manner are only hints to SLIRP, and so are not
replicated within the generated code.
SLIRP provides the following built-in type definitions
#typedef char** NT_STR_ARRAY
#typedef char** NT_STR_ARRAY_FREE
which, as described above, are used to return NULL terminated arrays
of C strings as S-Lang arrays of String_Type.
#undef
#undef C_identifier
This directive is the companion to #define; it causes the named
macro to be ignored by removing it from the internal list of macro
definitions. This prevents the macro from being wrapped as a
constant (as described section
GeneratedConstants), and causes
preprocessing conditionals such as #ifdef to evaluate to zero.
Attempts to #undef an undefined macro will be silently ignored,
however specifying a malformed identifier will signal an error.
#define BLAH 1
#undef BLAH % this S-Lang comment is ignored