Search code examples
swig

Cannot include <endian.h> when generating SWIG bindings


Here's my preprocessor section in my C++ code which I want to generate SWIG bindings.

#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) \
|| defined(__OpenBSD__)
#include <machine/endian.h>
#endif
    
#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || \
defined(ANDROID)
#include <endian.h>
#endif
    
#ifdef __MINGW32__
#include <sys/param.h>
#endif
    
#ifdef _MSC_VER
/* _MSVC lacks BYTE_ORDER and LITTLE_ENDIAN */
#define LITTLE_ENDIAN 0x0001
#define BYTE_ORDER LITTLE_ENDIAN
#endif
    
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN)
#error No byte order defined
#endif
    
#if BYTE_ORDER == LITTLE_ENDIAN
# define HIOFFSET 1
# define LOWOFFSET 0
#else
# define HIOFFSET 0    /* word offset to find MSB */
# define LOWOFFSET 1    /* word offset to find LSB */
#endif

But when I try to generate SWIG bindings using the swig command, I get the following error:

Error: CPP #error "No byte order defined". Use the -cpperraswarn option to continue swig processing.

It seems when SWIG tries to generate the bindings, <machine/endian.h> is not being included for some reasons.

How can I fix this error? Do I need to add anything to the SWIG interface file?

I'm using macOS 10.14.4.


ADDED: Here's what my SWIG interface file looks like.

%module pd
%{
    #include "myBindings.h"
%}
%include "myBindings.h"

As you can see there's nothing special in it.

And I generate the bindings using the following command:

swig -c++ -lua -fcompact -fvirtual -I../../../libs/openFrameworks myBindings.i && mv myBindings_wrap.cxx myBindings.cpp

Then I get the above mentioned error. Even when I just leave #include <machine/endian.h> it still generates the same error. (No byte order defined)


Solution

  • The reason SWIG is failing is because it doesn't normally recurse into include files when you %include a file to wrap its interface, so the definitions defined in <machine/endian.h> are not processed.

    There are a number of options to remedy this:

    1. Use -cpperraswarn and ignore the error as suggested. SWIG will process the #if incorrectly, but it is the compiler that builds the value. SWIG will still expose the HIOFFSET and LOWOFFSET values in the wrapper, and the value will be correct.

    2. %include <machine/endian.h> explicitly. That may or may not work correctly depending on the content of the file, and may expose more variables in the SWIG wrapper than intended.

    3. Use -includeall with SWIG to explicitly recurse into all #include files. This is normally not wanted as SWIG will attempt to wrap all definitions. For example, you'd get everything in #include <stdio.h> if present.

    4. Explicitly define BYTE_ORDER and/or LITTLE_ENDIAN in the SWIG .i file to work around the issue.

    Here's a couple of examples:

    --cpperraswarn Solution:

    test.i

    %module test
    
    %{
    #include "test.h"
    %}
    
    %include "test.h"
    

    SWIG generates the warning, and the result is only HIOFFSET and LOWOFFSET are defined, but they are correct for my little-endian machine even with the error from SWIG. The compiler still processed the definition correctly.

    >>> import test
    >>> dir(test)
    ['HIOFFSET', 'LOWOFFSET', 'SWIG_PyInstanceMethod_New', '__builtin__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_swig_getattr', '_swig_property', '_swig_repr', '_swig_setattr', '_swig_setattr_nondynamic', '_swig_setattr_nondynamic_method', '_test']
    >>> test.HIOFFSET
    1
    

    %include endian.h Solution:

    In this case I have a Windows machine so I simulated the problem by creating an msvc_endian.h file and defining BYTE_ORDER and LITTLE_ENDIAN in it.

    test.h was modified to use the following snippet:

    #ifdef _MSC_VER
    #include "msvc_endian.h"
    #endif
    

    test.i explicitly processed the header:

    %module test
    
    %{
    #include "test.h"
    %}
    
    %include "msvc_endian.h"
    %include "test.h"
    

    msvc_endian.h

    #define LITTLE_ENDIAN 0x0001
    #define BYTE_ORDER LITTLE_ENDIAN
    

    SWIG now has no warning and the result is still correct, but due to processing the sub-header LITTLE_ENDIAN and BYTE_ORDER were also exposed as values in the wrapper:

    >>> import test
    >>> dir(test)
    ['BYTE_ORDER', 'HIOFFSET', 'LITTLE_ENDIAN', 'LOWOFFSET', 'SWIG_PyInstanceMethod_New', '__builtin__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_swig_getattr', '_swig_property', '_swig_repr', '_swig_setattr', '_swig_setattr_nondynamic', '_swig_setattr_nondynamic_method', '_test']
    >>> test.HIOFFSET
    1