Overview
PyPi module | N/A |
git repository | https://bitbucket.org/arrizza-public/swig-cpp |
git command | git clone git@bitbucket.org:arrizza-public/swig-cpp.git |
Verification Report | https://arrizza.com/web-ver/swig-c++-report.html |
Version Info |
|
- installation: see https://arrizza.com/setup-common
Summary
A discussion and project for using SWIG C++ for Python, Ruby and javascript functions.
I had a need to call the same C++ functions from multiple scripting languages: python, ruby, javascript. This project is an example of how to set that up in JetBrain's CLion and CMake
Note: the javascript is working but not using the SWIG generated source file (see javascript/index.cpp and nwrap.h for temporary content). It is not generating a working javascript_example.node file that registers the module.
Some of the issues were:
- configuring the individual cmake subprojects for each scripting language
- separating the output files for each scripting language
Other good sources for SWIG & python:
Next Steps
For SWIG documentation, see https://www.swig.org/Doc3.0/SWIG.html#SWIG
$ swig -version
SWIG Version 4.0.1
Compiled with g++ [x86_64-pc-linux-gnu]
Configured options: +pcre
Please see http://www.swig.org for reporting bugs and further information
- current calls only use simple functions. Need to try on various other data structures:
- strings
- arrays
- structs
- unions
- JSON
- enums (i.e. read-only)
- pointers (is this needed?)
Common
-
The source code is in src directory. The example.cpp and .h come from (with minor changes) https://valelab4.ucsf.edu/svn/3rdpartypublic/swig/Doc/Manual/Introduction.html
-
example.i
is used across all sub-targets and is used by swig to configure what is made available to the language-
I used
#include "../src/example.h"
and%include
. The assumption is that all and only exported functions and variables are named in the example.h file. This may not be true for all projects or all scripting languages. -
the functions in example.c are simple. No unions, no structs, no char* or kinds of pointers, no arrays, i.e. simple. Other C constructs may cause some additional definitions to be needed.
-
Do a clean rebuild and test
- ./do_install - insures latest ruby, python, node are installed
- ./do_clean - wipes out all the output directories and the cmake build.
- ./do_build - runs cmake using the CLion command lines
- ./doit - runs scripts for all languages
Overall:
./do_install
./do_clean
./do_build debug all
./doit
# expected stdout:
./doit debug all
==== versions
Python 3.10.13
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-gnu]
v10.19.0
==== run
: python ruby node
fact : 720 720 720
my_mod : 3 3 3
before : 3.0 3.0 3.0
after : 5.1 5.1 5.1
sum1 : 261 261 261
sum2 : 261 261 261
append : abc-ok abc-ok abc-ok
app before : 3.0 3.0 3.0
app after : 5.2 5.2 5.2
app append : xy-app xy-app xy-app
==== convert to hex
python : 01 02 03 FF 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
ruby : 01 02 03 FF 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
node : 01 02 03 FF 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
Note that ./doit
or ./do_build
defaults to the incorrect target, so you must specify debug all
- fact - factorial, passes in an integer, returns an integer
- my_mod - modulus, passes in two integers, returns an integer
- before - gets the default value of global variable my_variable
- after - sets my_variable and then gets it again
- sum1 - sums an array of integers
- sum2 - sums an array of bytes (uint8_t)
- append - concats "-ok" to given string
- app before - App::my_variable default value
- app after - App::my_variable is set then gets it again
- app append - App.append() call, concats "-app" to given string
- second section shows to_hex() output
Installation:
Note: I ran this only on Ubuntu 20.04.1 LTS. Things may be different in other Ubuntu versions or other OS.
All installations are handled by the ./do_install script
$ ./do_install
python3.10 is already the newest version (3.10.13-1+focal1).
<snip>
python3.10-venv is already the newest version (3.10.13-1+focal1).
<snip>
ruby-dev is already the newest version (1:2.7+1).
<snip>
nodejs is already the newest version (10.19.0~dfsg-3ubuntu1.3).
<snip>
swig is already the newest version (4.0.1-5build1).
Note: I have not tested a clean installation of these packages. I may have necessary pip, gem, npm global modules already installed that I have not specified here.
Run test.py
Output directory: out/py
The import loads the factorial function, the mod function and cvar. "cvar" is used to access global C variables. Note these are read/write.
from out.py.example import fact, my_mod, cvar
Expected output:
$ python3.10 test.py
# see doit output above, column starting with "python"
Run test.rb
Output directory: out/rb
The require
is very simple in ruby, it loads the .so:
require_relative './out/rb/example.so'
Expected output:
$ ruby test.rb
# see ./doit output above, column starting with "ruby"
Run test.js
For good examples of calls in node_wrap.h, see https://github.com/nodejs/node-addon-examples For node_api.h doc & usage: https://nodejs.org/api/n-api.html#n_api_usage
Output directory: out/js
The require
is very simple in javascript, it loads the .node:
const example = require("./out/js/example.node")
Expected output:
$ node test.js
# see ./doit output above, column starting with "javascript"
If you get a failure:
internal/modules/cjs/loader.js:1144
[snip]
Error: /snap/core/current/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.32' not found
[snip]
code: 'ERR_DLOPEN_FAILED'
You need to update the GLIBC libraries that are installed. To determine which ones use this:
$ strings debug/libnode_example.so | grep GLIBC
GLIBCXX_3.4.32
GLIBCXX_3.4.31
GLIBCXX_3.4.29
GLIBCXX_3.4
GLIBC_2.4
GLIBC_2.14
GLIBC_2.2.5
Use this to determine which is currently installed:
ldd --version
ldd (Ubuntu GLIBC 2.39-0ubuntu8.3) 2.39
Then use apt to install:
# TODO