How to inject code at the top and bottom of each method generated in cpp wrap file by SWIG?

55 views Asked by At

I would like to add code to the SWIG generated wrappers in cpp wrap file. For example adding global mutex to prevent C# garbage collector from changing some shared variable.

SWIGEXPORT int SWIGSTDCALL CSharp_swig_generated_method___(void *jarg1) {

    int jresult;    //SWIG generated

    // inject code here like mutex begin

    /*
        SWIG generated code
    */
 
    jresult = result;

    // inject code here like mutex end

    return jresult;
}

I have tried %exception directive

%exception %{
    MUTEX_BEGIN
    $action
    MUTEX_END
%}

But this only puts the actual method call between the mutex statements. I would also like to put the additional SWIG generated code like creating a result variable, assigning jresult etc. between the mutex statements as well. Only the declaration of jresult and the return statement needs to be outside the mutex calls.

1

There are 1 answers

0
Vasile On

A rather painful exploration of SWIG's entire documentation, will easily reveal that SWIG was never designed with the ability of grouping the following 3 sub-stages of its processing, into one, logical, and customizable in the same time, group:

  1. Marshalling of C# args into C++ args, in preparation for issuing the to-be-wrapped C++ method call.
  2. The actual to-be-wrapped C++ method call.
  3. Marshalling the output args and/or retVal from the underlying C++ method call back to the C# land. No, it always considers each of those 3 stages as a separate thing.

Also, SWIG's support for setting some "use always single threading" context is missing completely. It just ... doesn't seem to care, and puts the burden to the developer / SWIG user.

While SWIG's support for other target languages than C#, Pyton for example, seems to feature stronger semantics for customizations involving the ability to add a developer-defined "prologue" and "epilogue" code. For example, search in the SWIG's official docs for keywords such as: %pythonprepend, %pythonappend, and %feature("action").

That being said, there is apparently a rather obscure SWIG feature which could be employed to solve the problem that is being asked. It is the modifiers optional part of SWIG's %typemap% syntax. Which can be better described as "Typemaps Parameterized by Optional Temporary / Local Variables".

This and a bit weird SWIG syntactic construct is first mentioned in some obscure, old entry in the SWIG's general CHANGELOG. A search for Added a local variable extension to the typemap handler in that change log will yield a log entry dated 5/4/97 and mentioning something like: "This mechanism [...] also makes it possible to write thread-safe typemaps."

This is a rather important finding, which the leads us to this reference in SWIG's official documentation (for version 4.1): https://www.swig.org/Doc4.1/SWIGDocumentation.html#Typemaps_defining

We can thus employ this "Typemaps Parameterized by Optional Temporary / Local Variables" feature for a %typemap(out) definition, like this:

%typemap(out)
    nameOfCppType1ToOverride (CUSTOM_RAII_MACRO_BEGIN CUSTOM_RAII_MACRO_CTX)
    [, nameOfCppType2ToOverride (CUSTOM_RAII_MACRO_BEGIN CUSTOM_RAII_MACRO_CTX)
    [, ... ]
    [, nameOfCppTypeNToOverride (CUSTOM_RAII_MACRO_BEGIN CUSTOM_RAII_MACRO_CTX)
%{
    $result = $1;
    CUSTOM_RAII_MACRO_END
    if (pending_exceptions)
        return $null;
%}

... whereCUSTOM_RAII_MACRO_BEGIN and CUSTOM_RAII_MACRO_END denote two C pre-processor macro definitions, to designate RAII / aquiring access to some synchronization primitive such as a mutex, for serializing access in a thread-safe way, and leaving/releasing such primitive, respectively.

... and CUSTOM_RAII_MACRO_CTX is just a third, dummy, empty C pre-processor macro definition, defined for example like this:

#define CUSTOM_RAII_MACRO_CTX

The CUSTOM_RAII_MACRO_CTX is defined only for the purpose of tricking SWIG into thinking that it denotes the name of a temporary, local variable, for SWIG to generate using a standard C/C++ typename varName declaration statement. You see what we did here?

I've applied the idea stated above, pragmatically, in a project and for the use case exactly mentioned in the original question. The part of the work that is the most involving consists of:

  1. Making sure to tell SWIG to forget / erase of of the previous %typemap(out) definitions that it may have currently, by writing:
%typemap(out) SWIGTYPE; // Clear ALL previously defined "out" typemaps, for ALL types, if any.
  1. Making sure to add output "Typemaps Parameterized by Optional Temporary / Local Variables" definition overrides for all of the built-in C++ types. Suggested list of types to override: bool, char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, long long, unsigned long long, float, double, ULONG, DWORD.

  2. Add similar overrides for App Domain-specific, user-defined types that are simple aliases to built-in C++ types, and of non-pointer and non-reference kind. Example: time_t in the land of standard C library. Or HRESULT for Windows-based programming.

  3. Add overrides for the reference (i.e. typename&) flavor of all the types overridden in Step 2 and 3.

  4. Add these special types after the two groups of output type overrides shown above:

%typemap(out)
    SWIGTYPE * (...),
    SWIGTYPE [] (...),
    SWIGTYPE & (...),
    SWIGTYPE const& (...),
    SWIGTYPE && (...),
    void * (...)
%{
    $result = $1;
    CUSTOM_RAII_MACRO_END
    if (pending_exceptions)
        return $null;
%}
%typemap(out) SWIGTYPE *const& (...)
%{
    $result = *$1;
    CUSTOM_RAII_MACRO_END
    if (pending_exceptions)
        return $null;
%}
%typemap(out) const enum SWIGTYPE & (...)
%{
    $result = (int)*$1;
    CUSTOM_RAII_MACRO_END
    if (pending_exceptions)
        return $null;
%}
%typemap(out) enum SWIGTYPE (...)
%{
    $result = (int)$1;
    CUSTOM_RAII_MACRO_END
    if (pending_exceptions)
        return $null;
%}
  1. Finally, one needs to make sure that there aren't other SWIG filename.i interface definition files who might introduce their own %typemap(out) customized directives, and which may intersect / get in conflict with the type overrides suggested above. The SWIG #include and %include inclusion order is very important in this context.

Hope this helps.