Swig – Connect C/C++ programs with high-level programming languages
swig.org
swig.org
CLIF is way nicer for C++/Python. If you need other languages then best of luck (Zig, Rust and D are easier than Java and Go at least).
See https://projects.om-office.de/frans/swig_demo/-/tree/master/... (disclaimer: very old and special exception handling)
IMHO this is a good practice anyway, because this way you make sure, your API looks the same in every binding.
And this way you get a binding for Python, Java, JavaScript, C#, Lua, and much more
This has been my experience as well. Just like I would argue that code which has been written to be testable is better than code which hasn’t, a “public API” which has been written to be consumed by another tool is better than one that hasn’t
But today I would not recommend anyone start using it if they have a choice, and it seemed prudent to post that warning on a submission of Swig's homepage without context.
Also, structure of the resulting module, classes, methods was kind of unpythonic.
All of that works, especially for Google internally, which uses CLIF quite extensively, but that makes it not so great for external projects.
And then a new version of Irrlicht came out, and I basically had to start from scratch. All the definition files no longer matched the headers so they had to be recreated.
And then a new version of Lua came out and broke modules. The binding was written as a module so that had to be redone too.
I got way more familiar with the internals of swig than I ever wanted to. Each language it supports has it's own little parser/runtime integration, so I had to learn a lot about how swig and lua interact.
If I were to do it again I'd create a proper C binding to the C++ interface, and then bind that to whatever language I wanted. They almost all support C. C++ is a whole other beast.
The next time I wanted to bind Lua to a C++ application, I just hand wrote the bindings. Boring, repetitive, shovel code. Once I had a template, it would take me just a few minutes to hook up new interfaces. If there was ever a problem with one, it was usually a typo and took 30 seconds to find and fix.
That's the generally recommended way of exposing your C++ library to any kind of non-C++ code.
I'm not aware of any software which directly helps with that, unfortunately. You either do it manually or write a custom script. Here's a recent example of the latter, from Dear ImGui:
If the API surface is not too large that’s one of the things ChatGPT is usually competent enough to ‘type out’ for you most of the way (delta some errors).
Then I had a pure python codebase that I could run on any python version without recompilation, and I no longer had the complication of having to deal with swig's different versions across different distribution releases.
For much bigger projects I could imagine that the situation can be different.
Using a subprocess and pipes wouldn't work with a .so file. I'd need to write a C wrapper to implement a way to call functions and marshall parameters in binary…
Basically CORBA over a pipe rather than a socket!
When you have a lot of C functions to call, having to duplicate all the declarations in Python code gets old. That's where the third-party cffi module comes in. It can parse straightforward C headers and use the appropriate types automatically. However, for complex headers (lots of macros, for example), it has to run a C compiler, which requires the machine which will run the script to have a functioning C toolchain. When using cffi, it can also be pretty easy to crash the interpreter with a use-after-free bug.
If you're already invested in the Cython ecosystem, you can use that for calling into C too. However, getting into Cython just to call a few C functions would be cracking a nut with a sledgehammer.
Finally, the nuclear option is to write a C extension module. Extension modules receive unconverted Python objects directly and can deeply integrate with the CPython interpreter. However, they also need to be compiled for a specific version of CPython, and won't necessarily work in alternative Python implementations. If you can get the job done with ctypes or cffi, you should avoid extension modules.
I'm always impressed on how simple it is to use an object I defined in c within python.
g-i is algo available for JavaScript, in fact gnome-shell it's written in JS
It's extremely easy if you stick to Gobject convention (this is a good thing).
In my old age though I've soured on it a little. The original problem I used it for, generating bindings for hundreds of generated pure aggregate data structures (for a serialized data format), is kinda SWIG's best case scenario.
The more complex (or more modern) your code gets, the more involved the types, the more the inherent complexity of the problem will overwhelm even the mighty SWIG and you'll find yourself wishing to use the native C ABI of whatever language you're binding to.
That said, if you're trying to generate bindings for several languages, I think working with SWIG still might be a worthwhile effort.
If you're just targetting python, and it's your first venture into building python extensions, it's fantastic. After a while, it's worth trying out building an extension without the use of swig. Much of the time, it's easier to use than swig is - but you do have to implement all the bells and whistles yourself. If you're writing in C or C++, chances are you like that kind of thing anyway.
Now days unless it is required for performance I doing up grpc to handle library calls. I’m that lazy. Lazier than a node developer that imports the world for a hello world health check.
Fun times.
I took different approach. Because I only needed to support these two languages, there’s no separate interface definition language, and no code generator for interfaces. Instead, users are expected to write both language projections manually.
Then there’s a runtime code generator on the .NET side of the interop which builds runtime callable proxy types for interfaces implemented in C++, also virtual tables for C# objects consumed by C++.
Made putting serviceable graphical interfaces to some of the CLI apps we were using.
A python native module may be providing python semantics on top of a dynamic library (libressl), but if some python code decide to dynamically load the shared library to make direct C ABI calls, I guess this is built-in, isn't it?
There are probably better/easier ways to do that now, but back then Swig was the only game in town and for us it worked out pretty well.
I thought I could leave it that way until one day a manager came and questioned why we had terrible unit test coverage. It turned out we had separate tools for measuring code coverage in C++ and Go and it was impossible to make them work together.
I wish more things were like sol2.
I thought the "level" of a programming language was referring to the level of abstraction it presents rather than how memory allocation is performed. Low-level would be very close to machine language. C and C++ have very different levels of abstraction, for instance, and being able to express that seems useful.
True, but that's a different sort of "abstraction" than what I'm talking about. I'm talking about how abstracted the processor itself is. Libraries and the like don't really enter into it -- this is about the language proper.
Machine language has no abstraction whatsoever. What you write is literally the code that the CPU executes.
Assembly language is one higher level of abstraction. There's still mostly a 1 to 1 correspondence to machine language, but some of that gets hidden for human convenience. You're now writing with symbols instead of numeric op codes, and you have concepts like macros, which have no machine language equivalent.
C is a higher level than that. Every C statement can easily be expressed directly in assembly, but more common operations (loops, subroutines, etc) have a shorthand that makes them easier to write. There is no longer a 1 to 1 correspondence with assembly or machine language, but it's not terribly far from assembly. Still, it's abstracted enough that the language is no longer tied to a specific processor.
C++ is yet another level up the ladder. In a sense, C++ is to C what assembly is to machine language.
And so forth.
I suppose that a plausible (but highly imperfect) rule of thumb for how high up the ladder of abstraction a language sits is how many machine language instructions are required to implement a given language construct. The more required, the higher the level of abstraction. High level languages also have a lot more (indeed, mostly consist of) constructs that simply don't exist at the machine language level.
I guess for the large part the inter-op here is from (mostly) interpreted languages, which are certainly higher level than C++.
Maybe instead of it being a binary choice between high-level and low-level languages, there is a middle ground. Fairly easy to argue that as the case.
After reading the first sentence on the site, though, I think this was just a somewhat clumsy rephrasing. "SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages." "A variety of high-level programming languages" implies there are some high-level languages that SWIG cannot connect C/++ with, and it's open to interpretation whether C/++ are among the high-level languages that SWIG doesn't connect C/++ with.
There is: mid-level languages. I was taught (and still think of) C as a mid-level language; things like assembly are low-level languages; C++, Python, Java, etc., are all high-level languages.
But I'm a graybeard, and these are the meanings that I've learned. It's possible that the definitions have shifted and nobody told me.