Using swig to generate wrapper it's easy thanks to the modern UseSWIG module (CMake >= 3.14).
note: SWIG automatically put its target(s) in all
, thus cmake --build build
will also call
swig and generate _module.so
.
UseSWIG is impacted by two policies:
- CMP0078:UseSWIG generates standard target names (CMake 3.13+).
- CMP0086: UseSWIG honors
SWIG_MODULE_NAME
via-module
flag (CMake 3.14+).
That's why I recommnend to use CMake >= 3.14 with both policies set to new for SWIG development.
When working on a k8
(aka x86_64
) architecture, you may face issue with int64_t
and uint64_t
management.
First long long
and long int
are different types and int64_t
is just a
typedef on one of them...
On Linux we have:
sizeof(long long): 8
sizeof(long int): 8
sizeof(int64_t): 8
First try to find where and how the compiler define int64_t
and uint64_t
.
grepc -rn "typedef.*int64_t;" /lib/gcc
/lib/gcc/x86_64-linux-gnu/9/include/stdint-gcc.h:43:typedef __INT64_TYPE__ int64_t;
/lib/gcc/x86_64-linux-gnu/9/include/stdint-gcc.h:55:typedef __UINT64_TYPE__ uint64_t;
So we need to find this compiler macro definition
gcc -dM -E -x c /dev/null | grep __INT64
#define __INT64_C(c) c ## L
#define __INT64_MAX__ 0x7fffffffffffffffL
#define __INT64_TYPE__ long int
gcc -dM -E -x c /dev/null | grep __UINT64
#define __UINT64_C(c) c ## UL
#define __UINT64_MAX__ 0xffffffffffffffffUL
#define __UINT64_TYPE__ long unsigned int
clang -dM -E -x c++ /dev/null | grep INT64_TYPE
#define __INT64_TYPE__ long int
#define __UINT64_TYPE__ long unsigned int
Clang, GNU compilers:
-dM
dumps a list of macros.
-E
prints results to stdout instead of a file.
-x c
and -x c++
select the programming language when using a file without a filename extension, such as /dev/null
On Catalina 10.15 we have:
sizeof(long long): 8
sizeof(long int): 8
sizeof(int64_t): 8
clang -dM -E -x c++ /dev/null | grep INT64_TYPE
#define __INT64_TYPE__ long long int
#define __UINT64_TYPE__ long long unsigned int
with:
-dM
dumps a list of macros.
-E
prints results to stdout instead of a file.
-x c
and -x c++
select the programming language when using a file without a filename extension, such as /dev/null
Contrary to macOS and Linux, Windows 64bits (x86_64) try hard to keep compatibility, so we have:
sizeof(long int): 4
sizeof(long long): 8
sizeof(int64_t): 8
Thus, in stdint.h
we have:
#if _VCRT_COMPILER_PREPROCESSOR
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
First, take a look at Swig stdint.i.
So, when targeting Linux you must use define SWIGWORDSIZE64 (i.e. -DSWIGWORDSIZE64
) while
on macOS and Windows you must not define it.
Now the bad news, even if you can control the SWIG typedef using SWIGWORDSIZE64
,
SWIG Java and
SWIG CSHARP do not take it into account for typemaps...
So you may want to use this for Java:
#if defined(SWIGJAVA)
#if defined(SWIGWORDSIZE64)
%define PRIMITIVE_TYPEMAP(NEW_TYPE, TYPE)
%clear NEW_TYPE;
%clear NEW_TYPE *;
%clear NEW_TYPE &;
%clear const NEW_TYPE &;
%apply TYPE { NEW_TYPE };
%apply TYPE * { NEW_TYPE * };
%apply TYPE & { NEW_TYPE & };
%apply const TYPE & { const NEW_TYPE & };
%enddef // PRIMITIVE_TYPEMAP
PRIMITIVE_TYPEMAP(long int, long long);
PRIMITIVE_TYPEMAP(unsigned long int, long long);
#undef PRIMITIVE_TYPEMAP
#endif // defined(SWIGWORDSIZE64)
#endif // defined(SWIGJAVA)
and this for .Net:
#if defined(SWIGCSHARP)
#if defined(SWIGWORDSIZE64)
%define PRIMITIVE_TYPEMAP(NEW_TYPE, TYPE)
%clear NEW_TYPE;
%clear NEW_TYPE *;
%clear NEW_TYPE &;
%clear const NEW_TYPE &;
%apply TYPE { NEW_TYPE };
%apply TYPE * { NEW_TYPE * };
%apply TYPE & { NEW_TYPE & };
%apply const TYPE & { const NEW_TYPE & };
%enddef // PRIMITIVE_TYPEMAP
PRIMITIVE_TYPEMAP(long int, long long);
PRIMITIVE_TYPEMAP(unsigned long int, unsigned long long);
#undef PRIMITIVE_TYPEMAP
#endif // defined(SWIGWORDSIZE64)
#endif // defined(SWIGCSHARP)
So int64_t
(i.e. long int
in this case) will be correctly bind to Java/.Net primitive type long
.
You can use OUTPUT_DIR
to change the output directory for the .py
file e.g.:
swig_add_library(pyFoo
TYPE SHARED
LANGUAGE python
OUTPUT_DIR ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}/Foo
SOURCES foo.i)
Since swig 4.0, swig can now extract doxygen comments from C++ to inject it in Python and Java.
note: Doxygen to csharp was planned but currently is not supported.