SWIG - C

Overview

PyPi module N/A
git repository https://bitbucket.org/arrizza-public/swig-c
git command git clone git@bitbucket.org:arrizza-public/swig-c.git
Verification Report https://arrizza.com/web-ver/swig-c-report.html
Version Info
  • Ubuntu 20.04 focal, Python 3.10
  • Ubuntu 22.04 jammy, Python 3.10

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

For C++ version, see https://arrizza.com/swig-c++

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.c 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

# 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
language  :  fact  my_mod  before after
          : ------ ------ ------ ------
python    :    720      3    3.0    5.1
ruby      :    720      3    3.0    5.1
javascript:    720      3    3.0    5.1

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

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, line 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, line 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, line starting with "javascript"

- John Arrizza