Next Previous Contents

8. Other Annotations

8.1 #clear

Grammar

#clear parameter_list

Description

This directive deletes all annotations which match the given parameter_list.

Example

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.

8.2 #copy

Grammar

#copy parameter_list { parameter_list [,   ...] }

Description

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.

Example

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.

8.3 #define

Grammar

#define C_identifier substitution_text

Description

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.

Example

        #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.

8.4 #ignore

Grammar

#ignore

    symbol_name_list

#end

Description

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.

Example

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

8.5 #inline_c

Grammar

#inline_c [(init)]

    C_translation_unit

#end

Description

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.

Example

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.

8.6 #prototype

Grammar

#prototype

    function_prototype_list

#end

Description

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.

Example

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.

8.7 #rename

Grammar

#rename SLang_regular_expression C_identifier

Description

This directive supports renaming one or more functions in the same manner as does the -rename command line option.

Example

        #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.

8.8 #retmap

Grammar

#retmap [(omit)] C_type_expression

    code_fragment

#end

Description

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.

Example

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);

        ...

Built-in #retmap Annotations

As 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.

8.9 #typedef

Grammar

#typedef C_identifier C_identifier [;]

Description

This directive supports the definition of simple types, as described in section TypeDefinitions, directly within a SLIRP interface file.

Example

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.

Built-in Type Definitions

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.

8.10 #undef

Grammar

#undef C_identifier

Description

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.

Example

        #define BLAH  1
        #undef BLAH             % this S-Lang comment is ignored


Next Previous Contents