Hacker News new | past | comments | ask | show | jobs | submit login

The fact C++ has no stable ABI is not what stops you from creating language bindings, because you need that kind of interoperability only for those symbols that need to cross the FFI. It's more about how the library is designed than the implementation language itself; for instance, Tensorflow is mostly written in C++, but its intended use is as a Python module. Also, by using GLib you take in so many GLib-specific types and assumptions it becomes extremely wonky using it from non-GLib code, and you often end up writing some kind of FFI anyway in order to wrap its behaviour. I've even seen some libraries ship both a C and a GLib wrapper, as if it was almost like a separate language.

LLVM is a good example of C++ being able to interface with other languages using its C FFI, being completely written in C++ but having bindings for almost every programming language. I can count among them at least C, C++, D, Rust, OCaml and Python, for instance, and while it has an "LLVM-C" library available, you are not necessarily forced to use it; you can just export what you need directly from C++ using a file with a few functions defined as extern "C".

Rust is probably even trickier to interface with C but it's still pretty doable, given that GNOME is now rewriting several components in Rust (see librsvg).

I still think one of the main reasons C++ was discarded so quickly at the time, despite Qt and KDE being older than GNOME and in C++ already, was being born from a culture that had at the time already rejected the adoption of C++.

GNU has historically always discouraged its projects, especially those they cared about the most, from using C++. It's well documented how strongly RMS despised the language and how he actively pushed people towards using C for such tasks. I think it's still written in somewhere in the GNU website, even.

Heck the GCC developers had to ask RMS the permission to switch to C++ a few years ago, due to GCC becoming harder and harder to maintain. Ironically, it used internally its own private C implementation of dynamic vectors, while being one of the main implementers of the STL. I think it even had some sort of garbage collection inside, and that they largely reimplemented an object system too using C and macros (it's been a while since I read the GCC sources, so my memories could be incorrect). RMS wasn't really that happy about it, but it grumpily said they could.




The lack of standard ABI makes harder to call C++ from foreign-function interfaces or from dlopen/LoadLibrary dynamic loading functions. It lack of ABI also does not allow linking against object-codes built by the other compilers or even older versions of the same compiler.

In C, a symbol name matches the function name, if you have a function called 'void my_function()', the symbol will be 'my_function' and you will be able to load this function from a foreign function interface as libffi.load('my_function', ... types ...). The trouble with C++ bindings is that symbols are mangled, the function names are encoded, a function named 'Workaround1::get_callback()::callback' can be encoded (mangled) as _ZZN11Workaround112get_callbackEvE8callback - this encoding is specific to a given compiler ABI, in addition FFIs cannot load non-C types.

Tensorflow uses the hourglass design pattern, this library is implemented internally in C++, but does not exposes a C++ API, instead it exposes a C wrapper built with 'extern "C" and opaque pointers (void* pointers or incomplete types). This approach allows the library to be used in the same way as a C library and makes easier to call the library via FFI. Another benefit is the better binary compatibility as object-codes built with other compilers canlink against Tensorflow without any issues. Another library that uses this approach is the Squirrel embedded scripting language, that is implemented in C++, but only exposes a C API. So, this technique allows a C++ library to be used as C library and makes possible calling it through FFI.

I guess that in the case of LLVM it may also use this pattern of using C++ internally and only exposing a C API via extern "C". The disadvantage of this pattern is the need of lots cumbersome extern "C" wrappers for every member function of a class and for all exposed classes. LibClang LLVM may be helpful for automating the generation of this C wrapper code.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: