2020-03-04 17:42:26 +01:00
|
|
|
# SWIG Wrapper Generation
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
Using [swig](https://github.com/swig/swig) to generate wrapper it's easy thanks
|
|
|
|
|
to the modern [UseSWIG](https://cmake.org/cmake/help/latest/module/UseSWIG.html)
|
|
|
|
|
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`.
|
|
|
|
|
|
|
|
|
|
## Policy
|
2020-03-04 17:42:26 +01:00
|
|
|
|
|
|
|
|
UseSWIG is impacted by two policies:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2022-05-18 14:20:41 +02:00
|
|
|
* [CMP0078](https://cmake.org/cmake/help/latest/policy/CMP0078.html):UseSWIG
|
|
|
|
|
generates standard target names (CMake 3.13+).
|
|
|
|
|
* [CMP0086](https://cmake.org/cmake/help/latest/policy/CMP0086.html): UseSWIG
|
|
|
|
|
honors `SWIG_MODULE_NAME` via `-module` flag (CMake 3.14+).
|
2020-03-04 17:42:26 +01:00
|
|
|
|
|
|
|
|
That's why I recommnend to use CMake >= 3.14 with both policies set to new for
|
|
|
|
|
SWIG development.
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
## int64_t Management
|
|
|
|
|
|
|
|
|
|
When working on a `k8` (aka `x86_64`) architecture, you may face issue with
|
|
|
|
|
`int64_t` and `uint64_t` management.
|
2020-03-04 17:42:26 +01:00
|
|
|
|
|
|
|
|
First `long long` and `long int` are **different** types and `int64_t` is just a
|
|
|
|
|
typedef on one of them...
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
### Linux
|
|
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
On Linux we have:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```
|
|
|
|
|
sizeof(long long): 8
|
|
|
|
|
sizeof(long int): 8
|
|
|
|
|
sizeof(int64_t): 8
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
#### Gcc
|
|
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
First try to find where and how the compiler define `int64_t` and `uint64_t`.
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```sh
|
|
|
|
|
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
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```sh
|
2022-05-16 17:44:50 +02:00
|
|
|
gcc -dM -E -x c /dev/null | grep __INT64
|
2020-03-04 17:42:26 +01:00
|
|
|
#define __INT64_C(c) c ## L
|
|
|
|
|
#define __INT64_MAX__ 0x7fffffffffffffffL
|
|
|
|
|
#define __INT64_TYPE__ long int
|
|
|
|
|
|
2020-11-19 09:20:36 +01:00
|
|
|
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
|
2020-03-04 17:42:26 +01:00
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
#### Clang
|
|
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```sh
|
|
|
|
|
clang -dM -E -x c++ /dev/null | grep INT64_TYPE
|
|
|
|
|
#define __INT64_TYPE__ long int
|
|
|
|
|
#define __UINT64_TYPE__ long unsigned int
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
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`
|
|
|
|
|
|
|
|
|
|
Ref:
|
|
|
|
|
https://web.archive.org/web/20190803041507/http://nadeausoftware.com/articles/2011/12/c_c_tip_how_list_compiler_predefined_macros
|
2020-03-04 17:42:26 +01:00
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
### MacOS
|
2020-03-04 17:42:26 +01:00
|
|
|
|
|
|
|
|
On Catalina 10.15 we have:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```
|
|
|
|
|
sizeof(long long): 8
|
|
|
|
|
sizeof(long int): 8
|
|
|
|
|
sizeof(int64_t): 8
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
#### Clang
|
|
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```sh
|
|
|
|
|
clang -dM -E -x c++ /dev/null | grep INT64_TYPE
|
|
|
|
|
#define __INT64_TYPE__ long long int
|
|
|
|
|
#define __UINT64_TYPE__ long long unsigned int
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
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`
|
|
|
|
|
|
|
|
|
|
Ref:
|
|
|
|
|
https://web.archive.org/web/20190803041507/http://nadeausoftware.com/articles/2011/12/c_c_tip_how_list_compiler_predefined_macros
|
|
|
|
|
|
|
|
|
|
### Windows
|
2020-03-04 17:42:26 +01:00
|
|
|
|
2022-05-18 14:20:41 +02:00
|
|
|
Contrary to macOS and Linux, Windows 64bits (x86_64) try hard to keep
|
|
|
|
|
compatibility, so we have:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```
|
|
|
|
|
sizeof(long int): 4
|
|
|
|
|
sizeof(long long): 8
|
|
|
|
|
sizeof(int64_t): 8
|
|
|
|
|
```
|
|
|
|
|
|
2023-10-30 14:36:28 +01:00
|
|
|
#### Visual Studio 2022
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
Thus, in `stdint.h` we have:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```cpp
|
|
|
|
|
#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;
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
### SWIG int64_t management stuff
|
2020-03-04 17:42:26 +01:00
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
First, take a look at
|
|
|
|
|
[Swig stdint.i](https://github.com/swig/swig/blob/3a329566f8ae6210a610012ecd60f6455229fe77/Lib/stdint.i#L20-L24).
|
|
|
|
|
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](https://github.com/swig/swig/blob/3a329566f8ae6210a610012ecd60f6455229fe77/Lib/java/java.swg#L74-L77)
|
|
|
|
|
and
|
|
|
|
|
[SWIG CSHARP](https://github.com/swig/swig/blob/1e36f51346d95f8b9848e682c2eb986e9cb9b4f4/Lib/csharp/csharp.swg#L117-L120)
|
|
|
|
|
do not take it into account for typemaps...
|
2020-03-04 17:42:26 +01:00
|
|
|
|
|
|
|
|
So you may want to use this for Java:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```swig
|
|
|
|
|
#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:
|
2022-05-16 17:44:50 +02:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```swig
|
|
|
|
|
#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)
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
So `int64_t` (i.e. `long int` in this case) will be correctly bind to Java/.Net
|
|
|
|
|
primitive type `long`.
|
|
|
|
|
|
|
|
|
|
## swig_add_library()
|
2020-03-04 17:42:26 +01:00
|
|
|
|
|
|
|
|
You can use `OUTPUT_DIR` to change the output directory for the `.py` file e.g.:
|
2022-03-24 17:19:17 +01:00
|
|
|
|
2020-03-04 17:42:26 +01:00
|
|
|
```cmake
|
|
|
|
|
swig_add_library(pyFoo
|
2022-04-28 15:50:56 +02:00
|
|
|
TYPE MODULE
|
2020-03-04 17:42:26 +01:00
|
|
|
LANGUAGE python
|
|
|
|
|
OUTPUT_DIR ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}/Foo
|
|
|
|
|
SOURCES foo.i)
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
## Doxygen
|
|
|
|
|
|
|
|
|
|
Since swig 4.0, swig can now
|
2023-02-02 13:05:30 +01:00
|
|
|
[extract doxygen comments](http://www.swig.org/Doc4.1/Doxygen.html) from C++ to
|
2022-03-24 17:19:17 +01:00
|
|
|
inject it in Python and Java.
|
|
|
|
|
|
|
|
|
|
### Csharp documentation
|
2020-03-04 17:42:26 +01:00
|
|
|
|
2022-03-24 17:19:17 +01:00
|
|
|
note: Doxygen to csharp was
|
|
|
|
|
[planned](https://github.com/swig/swig/wiki/SWIG-4.0-Development#doxygen-documentation)
|
|
|
|
|
but currently is not supported.
|