1

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)

Mizux
  • 8,222
  • 7
  • 32
  • 48
Zack Lee
  • 2,784
  • 6
  • 35
  • 77
  • I will offer bounty for anyone who answers this. – Zack Lee May 30 '19 at 14:09
  • Show your SWIG interface file. – Mark Tolonen May 31 '19 at 08:08
  • @MarkTolonen I don't have anything special in it. – Zack Lee May 31 '19 at 10:59
  • Well, the basic one I threw together compiled with no errors, so I'll show you mine if you show me yours :^) Anyway I'm on Windows so check the predefined macros for your compiler match one of the #if defined you're expecting. Also, it was the compiler not the swig command that failed for me when I forced the _MSC_VER to be undefined to simulate the issue so that indicates your .i might be incorrect. Provide all the information to reproduce the issue. – Mark Tolonen May 31 '19 at 16:25
  • @MarkTolonen Please see my edited post. – Zack Lee Jun 01 '19 at 02:11
  • 1
    I reproduced it with your .i. I was using `%inline %{ #include "test.h" %}` which *should* be equivalent, but it only fails with separate #include and %include of the header. So it **is** special :^) Not sure yet why one works and one fails, but did you try using the suggested `-cpperraswarn` and continue building? the wrap file still gets generated. You may need `-D` and define the symbols your compiler will use to generate the code correctly. – Mark Tolonen Jun 01 '19 at 03:30
  • 1
    Tell swig where it is stored, add `-I/usr/include`. If you don't have /usr/include/machine/endian.h then [install it](https://stackoverflow.com/a/20813118/17034). – Hans Passant Jun 01 '19 at 12:48
  • @HansPassant Thanks but I'm using macOS 10.14 and it does not create the /usr/include folder anymore. – Zack Lee Jun 01 '19 at 14:16
  • That's surely why you need to install it. – Hans Passant Jun 01 '19 at 14:54
  • @Hans I reproduced the error on Windows so it isn't a case of needing the file installed (at least for the current error). It happens during the SWIG step before the compiler will care if the file is installed. – Mark Tolonen Jun 01 '19 at 20:45
  • 1
    @Zack did you try using `-cpperraswarn` and continuing the build? It's the compiler that will care about the preprocessor error. SWIG should be able to ignore it. As Hans said, the *compiler* will care if the file needs to be present, and may need to be installed or the path included. – Mark Tolonen Jun 01 '19 at 21:12
  • @MarkTolonen Yeah it works when I do that. Thanks. So is it safe to just ignore the error by SWIG? – Zack Lee Jun 02 '19 at 07:24
  • Yes, it is just a code generator. Move on and compile and test the result. Swig doesn’t normally process #include files inside files you have %included, so it won’t receive the definition inside endian.h, but the compiler will process normally. You could also %include the endian.h file if needed. – Mark Tolonen Jun 02 '19 at 15:30

1 Answers1

3

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
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251