Interesting article, but I wouldn't say introducing a 3rd language is the easiest way to call C code from Python, that distinction goes to the CFFI library by Armin Rigo et al. I've used it to wrap several C libraries and it's amazingly powerful. Bonus it works with CPython and PyPy.
First there’s PSL ctypes,[1] which is supposedly inferior to cffi to some extent, but hey it comes with your CPython installation, no third-party dep required.
Or you can write a C/C++ extension module.[2] A bit more heavy-handed and there’s a learning curve, but once you’ve done it once or twice it’s really not hard, and it affords you great flexibility.
The solution in the blog post is an extension module. Written for you, by the compiler. Instead of a learning curve and "it's really not hard", it's "no learning curve" and "I struggle to find how this could be easier".
I would take a tested and true solution sanctioned by CPython core devs any day over a novelty third party library that gets another language toolchain involved.
Also, the solution seems to only cover what’s possible with ctypes already. If don’t need the flexibility there’s little reason to hand roll an extension module. (Not saying ctypes doesn’t have its problems; e.g. when you pass in a wrong number of arguments instead of throwing a TypeError it segfaults.)
ctypes does not parse C headers and doesn't know about enums or function parameter types — only that they have a symbol in the .dynsym section of a .so.
Maybe you're thinking of cffi? Cffi does basically what this blog post covers, except without D. It requires a C compiler at some stage in the process, but you can pregenerate during 'build' phase and ship a header snapshot to avoid requiring the C compiler at runtime.
No, ctypes does not. It can't handle macros, and it requires users to define the structures in Python themselves. It's miles away from what's in the blog post.
I'm curious about what ctypes' supposed inferiorities are. Last time I used it was to implement native printing in Windows (long story short: needed to print to a ZPL printer, and Qt doesn't seem to offer any ability to just send raw data to a printer, so I implemented it myself with the Winspool API), and it was a breeze (comparatively speaking, i.e. as much of a breeze as Windows API programming can be).
If ctypes can handle the Windows APIs, then I'm convinced it can handle just about anything.
ctypes allows you to trivially mistype C objects, send the wrong types and number of arguments to C functions, etc. Cffi just imports C headers and gives you more or less what TFA illustrates (without D).
I mentioned two completely different solutions, it’s not clear what “these” is referring to (both?). Also, both are clearly supported on Windows and used by tons of successful libraries and applications, so without details about your use case it’s not a very useful comment.
After a week, I've finally had time to look at cffi. It's not even remotely as easy. I tried using it just now to call nanomsg and gave up immediately. I can't just give it the headers, I have to pass it a string which is valid C code. I can't read the headers (separately!) and pass that in either because it can't handle preprocessor directives. It's a mess.
I don't understand how the OP could write this article and not talk about FFI? What a glaring oversight. (FWIW: The FFI module works extremely well in NodeJS as well.)
> I wrote a little project called dpp because I’m lazy
I can confirm that Atila is a very lazy man. He's always figuring out ways to have his computer do the work for him. He's so successful at avoiding work, people keep giving him more work to do, making him a very hard working lazy man.
> always figuring out ways to have his computer do the work for him
One of my earliest programming mentors mused that the reason I might well become a decent programmer was because I was both extremely stubborn and horrifically lazy. He'd observed that I would re-write code until it was the most efficient possible to automate a needed task that could save me time/effort in the future.
I think arrogance should replace impatience, which has redundancy with laziness. (Or possibly hubris, which is the weakest.) Arrogance makes you start projects that you are not skilled enough for, and grow in the doing.
Impatience is about writing code that is responsive, potentially by using some form of prediction to pre-load things. It's not about a programmer anticipating potential new features and implementing those.
I'm currently working on a project using Cython to wrap and expose a large and complex C API (CEF) to Python. One of the most time-consuming, tedious, and error prone tasks is translating functions and structures to Cython syntax, including managing the GIL. Also, keeping the translated API up to date with new releases.
I'm curious to see how this D approach compares, both in workload required to expose and use an API, and the flexibility of the end result. There are a lot of quirks and special syntaxes in Cython to allow for not just interop between C and Python, but also writing C in Python-like syntax mixed with actual Python. This is an area I assume this approach would be lacking.
Regardless, as someone who loves mixing C and Python, this is an encouraging reason to look more at D.
Can you clarify your question? I'm using Cython to write extension modules that directly interface with CEF's C API. CEF is about 40k lines of C++ that programmatically controls Chromium, which itself is written in C and C++.
Email me at fred(underscore)weigel(at)hotmail(dot)com
I interfaced CEF to Java in 2000 lines of C++
Simple, easy API. I run CEF in a separate process, and use
the Window manager (Linux and Windows) to cause the Chrome
windows to overlay the application windows. Standard pipes
are used between the controlling process and CEF.
Email me if you are interested. As a ps -- I can use CEF from shell scripts as well. Anything with a C FFI works.
I see. Thanks, that does change my understanding of your situation. My revised question with your clarifications in mind is, is there a reason you're not calling CEF's C api directly from Python? Are your extension modules doing some heavy lifting themselves, or are you using Cython just to handle the glue between the language interface?
> is there a reason you're not calling CEF's C api directly from Python?
Such as by using CFFI? Cython's strength, in my opinion, is in the fact that it is its own language resembling both C and Python that compiles to down to C, rather than just an FFI.
> Are your extension modules doing some heavy lifting themselves, or are you using Cython just to handle the glue between the language interface?
I would say the majority of the application logic is written in plain Python, and Cython makes interfacing with it, creating higher level interfaces, and marshaling data far easier than with CFFI or using the C API directly. This is pretty subjective though.
I often use C++ and pybind11 to do this. pybind11 takes an approach very similar to boost::python (template magic). It works rather well.
Though most of the C code I write can compile as C++ also.
Why write in C then? It's still the lingua franca in microcontroller land... Just want to reuse the code on PC and Embedded Linux, and Python makes high-level testing much nicer.
Pretty much this. Add to that pybind11's exceptional numpy compatibility (including easily mapping C types to structured numpy dtypes that can be e.g. converted to a dataframe), and there's not many choices left if you're not planning on reinventing a wheel or two.
Funny reading this now, I literally just implemented something in 15min using C++/pybind11 (and operates on numpy arrays) that is an order of magnitude faster than when I used numpy operations.
Both pybind11 and boost::python involve writing code. This solution doesn't, save for listing the names of the files to be made available to Python (or C#, or Excel, or...).
Neither ctypes nor cffi involves writing glue code. In fact you don’t even need another source file, let alone a whole other language toolchain (wait, you actually need two other source files plus your third-party dpp library).
I'm not sure I understand your comment. Someone said they use pybind11, someone responded to that by saying pybind11 requires writing code, and you responded by saying ctypes and cffi don't.
OP presents their solution as an “easy” solution that doesn’t require glue code. Except there are already established native solutions that actually don’t require glue code.
Sure, I’ll be more specific: there’s no glue code for function calls. Whatever function you want to call doesn’t need to be declared up front, you just call it at runtime, and it’s looked up dynamically. However, data structures like arrays and structs need to be declared, if needed.
It's really crazy how many ways there are to implement FFI in Python. I started in the "old days" (Makefile, a bunch of CPython C code to implement argument parsing, etc), switched to swig (experienced both the good and bad sides), hoped that GCC-XML would make it easier to convert complicated C++ classes to Python interfaces (big mistake), then hoped that LLVM 'would solve the problem', saw Google release CLIF (still can't compile it from scratch on a modern linux system), and the large number of "late-binding" approaches. Of all of them, I was most skeptical of Cython but have slowly come to appreciate its sublime cleverness.
D & Nim (especially Nim) are very appealing from a language perspective, but the lack an IDE undermines the advantages they bring.
I personally won't consider using a language for a meaningful project unless it has a decent debugger, and reasonable refactoring; ie. at least find references & rename.
Beef is building on an IDE, which is a refreshing take, but it's still too early.
Using that .spacemacs file you should be able to launch a fully prepped for D! The only other dependencies are a D compiler and DCD (https://github.com/dlang-community/DCD) built and in your PATH. I'd be happy to answer any questions if something doesn't work.
Other IDEs with a languageserver implementation can make use of DLS https://github.com/d-language-server/dls. VSCode and sublime are two I've had good success with.. but I always end up back with emacs ;)
As for a debugger, you can debug any D program with GDB/LLDB (depending on if you compile with dmd/lcd). Then you can use any graphical debugger that uses gdb. Nemiver is my favorite, then there's a few web based ones that are nice: https://www.gdbgui.com/
Also had really good luck doing D with Spacemacs, but Spacemacs is a hit or miss to setup for me. Other times I just use Sublime Text for the syntax highlighting.
I can't remember what I used for Nim, I think I either used Nano or vim just because I've only tested it once or twice. I havent used it heavily yet, though I do have an interest in Nim, since I do enjoy Python programming.
My own pet peeve about D is the lack of local documentation. If I install my system's compiler packages¹ I receive basically no documentation at all. That is a huge difference to the experience with nim/rustc/go/$others which all provide a nice fat $lang-doc package in their Suggests field, and all of those contain full standard library docs/tutorials/etc.
1. gdc or ldc on Debian. The experience /may/ be different with the dmd repositories, but being external they're of less interest to me.
D comes with an interesting utility called `dman`. Run it from the command line and as an argument give it a D topic and it'll open a browser on the topic.
dman dman
will, of course, open the browser on a page about dman.
Which, as I noted, is still a very different experience from go/nim/rust in Debian. It wasn't a pet peeve about no documentation, it is about how I can interact with it compared to to other languages.
Which leads me to wonder why we don't have dmd{,-doc} in Debian now, given it has been ~3 years since the compiler was open sourced. I can't even find an ITP in a quick search :/
Nim works very well with Emacs and Visual Studio Code at the very least, also Vim (from reports by others) - including debug support. What are you missing?
it should work on nim source level as well: if there are any problems ,that's a bug,so please report it. (disclaimer: me & my partner also work on a bit more advanced commercial debugger environment which also targets nim, so I guess there are many kinds of efforts)
VisualD is pretty damn good, visual code has a few D plugins that are ok to excellent when they work (which is most of the time)
There is also a D specific IDE which I can't remember the name of which is probably best if you just want something that works (it's pretty lightweight)
So behind the scenes is a utility that scans the #included headers, runs the C preprocessor on them, then uses Clang code to pick out the declarations, wraps them for calling from D, and redefines any C macro values as D constants? Do I understand that correctly?
(The other half of the puzzle being a tool called autowrap that wraps the resulting D for consumption by Python.)
I'm not sure whether I personally would ever use this, but it's a very interesting exploration of the subject.
could you explain the weird fascination of the D community with Excel ? there are so many "D" things for Excel and it seems really weird as an outsider - I don't remember the last time I've seen an actual Excel in the wild and not libreoffice or openoffice
> could you explain the weird fascination of the D community with Excel
No, because I'm not aware of one existing. The only reason I wrote excel-d is because of business reasons.
> actual Excel in the wild and not libreoffice or openoffice
Where I work, and in many many other places, it's all Excel. It's actually the most used programming language in the world. Maybe it shouldn't be, but it is.
I've been using Cython since it was called Pyrex, and I'm perpetually annoyed with rewriting c/c++ headers in Cython. This seems neat. But the reason that I use Cython is that I can wrap complex data structures (including templated c++ classes) and do processing in a fuzzy boundary between Python and c/c++.
The "code-free" approach here seems to forbid such a fuzzy boundary. Am I wrong?
I can't really comment without an example. I don't think this approach stops you from doing anything. It can (and has) allowed Python to call into templated D code, as long as you give the particular instantiation you're interested in an alias.
Since Cython compiles to C++, it can use C++ templates directly, without the need for creating aliases to the specific instantiations you're gonna use. As far as I understand, you'll still have to write Cython bindings, though:
https://cython.readthedocs.io/en/latest/src/userguide/wrappi...
No, but I used the equivalent in Lua. I was under the impression that no solution handled preprocessor macros, but this thread corrected me. I might do a follow-up post on this.
"Envious of C++’s and Objective C’s credible claim to be the only languages that can seamlessly interoperate with C (due to header inclusion and compatible syntax)"
The last time I looked at the Nim documentation, one had to manually declare C and C++ functions. I don't see how that's different from pretty much any other language that can call C (i.e. pretty much all of them). Then there's preprocessor macros.
That's nice, but not the same - users have to intend to write a Python extension in Nim. With autowrap, you can call unmodified D code (and as shown in the blog, C code as well with dpp). That means code that was never meant for consumption from another language can be made available.
You don't need to modify the code, just add a wrapper.
Nothing prevents generating a wrapper automatically but 99% of the time that is not very useful.
The large majority of Python extension contain Python-specific code to access or return Python objects or act in a pythonic way.
> You don't need to modify the code, just add a wrapper.
Still not the same.
> Nothing prevents generating a wrapper automatically but 99% of the time that is not very useful. The large majority of Python extension contain Python-specific code to access or return Python objects or act in a pythonic way.
Except, and I cannot stress this enough, at work we're using this to succesfully call into D production code without any Python-specific anything. It just works. The article wasn't even about that, it's about making it work just as seamlessly for C.
Okay, but what if I want to call python libraries from C (or any other language)? What we need is some sort of .net-like global interop in the Unix world.
You can't really do that without including the entire Python runtime in your application, and cpython is (so I've heard) absolutely horrid at being embedded in things.
Why can’t we create a --server mode standard where every language can be started up in that mode and serves a standard API with standard linker types (where possible)?
Extending is ok, embedding is indeed quite horrid. Tcl got that right. Yu want to embed multiple interpreters in the same address space with no sharing -- no problem. The mantle has been passed to Lua I think, but Guile is pretty good and not as minimalistic as Lua (that's bit of an understatement though).
If I want to make an application composed of C, C#, and Visual Basic, there nothing stopping me in Windows. You try to pull that shit in Unix though, and you get a whole mess of problems. This is why Unix is clearly just so inferior. /s
On second thought, I'll wait for two new programming languages to appear that make it even easier to do the interface, one making the Python-to-D interface easier, and the other making the D-to-C interface easier.
Maybe those two languages already exist, but someone hasn't posted the solution on HN yet.
Using three new awesome programming languages to seamlessly interface Python and C would solve an enormous number of interop problems.
How many languages do we need again? The amount of time we spend playing around with special case languages and frameworks. Would be nice if machine languages followed the lead of natural languages, with a single one slowly pushing the rest to the sidelines.
What a terrible world that would be. For a while, 20 years ago, it looked like Java was going to be the one language to rule them all; dodged that bullet.
Kitchen sink languages are my least favorite. Give me something with a tight focus on a problem space.
There were some studies about the impact of the loss of some natural languages. Some represent a complete different ways of handling the world that are worth keeping.
https://cffi.readthedocs.io/en/latest/