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

The part of GTK+ I've always hated the most is how much it relies on GObject, which is probably one of the worst ideas ever conceived by mankind. Reinventing a whole object system using C and shitload of macros is such an aberrant idea I cannot even start ranting on why it's an utter abomination. The fact it was born mostly from the hate the GNU project always harboured for C++ is what has always bewildered me the most. I understand that C++ in the nineties was a steaming pile of manure, but basically reinventing a poor man's copy of Objective-C just because they desperately wanted to write their stuff in C, and nothing else, is just too much.

And for what? GCC was basically the reference implementation of an Objective C compiler, a language that basically boils down to an object system hastily glued on top of C. I don't think it would have been so unreasonable to use that instead of rolling their own... I guess they had their reasons for not doing that, or not cooperating with GNUstep for that matters.

The irony is that has led to the creation of a million language bindings, such as Vala, Gtk#, PyGTK, Gtkmm, gjs, ... just to lower the massive learning curve of writing GTK applications in C. This is something Qt has clearly never struggled with, given that I've yet to see a relevant Qt app not based on C++ or (recently) QtQuick.




Those large amount bindings are possible due to the GTK be written in C, which allows creating binding to GTK via FFI - foreign function interface or native interface (borrowing the term from java JNI).

Creating bindings to QT is harder because Qt is written in C++ and C++ compilers does not have a standard ABI and FFIs foreign function interfaces can only load C-compatible functions and types. Another challenge is that older Qt (< Qt5) versions relied too much on MOC - Meta Object Compiler for reflection and callbacks.

The workaround for calling a C++ libraries from other languages is to create a C-API for the C++ library via 'extern "C"' wrappers with opaque pointers. Qt could be used by many other programming languages if it was shipped with an official C-API or interface as previously mentioned.

I a have a proof-of-concept code of a C-API for Qt5 Widgets and three client codes in C, D-language and Julia language which call the C wrapper library and build Qt5-Widgets user interfaces. This code is available at: https://caiorss.github.io/C-Cpp-Notes/CwrapperToQtLibrary.ht...

Another way to generate bindings to C++ libraries is by using SWIG that parses the C++ code generates native interface bindings.


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.


Objective-C might not use the C ABI, but you can certainly interact with it in a stable way (with no code modifications) entirely in C.


I mostly agree with you, whenever I want to put a simple native linux gui on top of some tool I'm disappointed by the difficulty of the GObject api. However, one advantage is that the C abi hasn't budged as the binary lingua franca. The C++ abi can change, but that only happens every decade or so, so it's not that bad. The worse part is that most FFI libraries always support C perfectly and C++ is always "experimental". So you can quickly throw together a library for a language that wraps GTK without worrying about the ABI. The downside is, obviously you want to smooth over GObject in MyCoolPyGTK, so each library develops its own API which you have to learn over and over, and you end up with 12 tabs open of the shoddy MyCoolPyGTK docs, GTK docs, GObject docs, and reverse engineering how they talk to each other behind the scenes. /rant


I really wish there was a standard stable ABI for namespaces, vtables, and maybe name mangling operators and overloaded functions. In other words, a standard ABI for OOP APIs. The problem with C++ abi isn't just that it can change, but that it is implementation defined,so different compilers produce different abis.


> However, one advantage is that the C abi hasn't budged as the binary lingua franca.

That’s nice in theory, but hasn’t gtk stopped providing API compatibility between minor releases?


Yeah, but that's a slightly different issue. C abi stability means you can use whatever off-the-shelf ffi your language supports.


That's not the point. The point is other languages don't have FFI for c++ because it doesn't have a stable/standard abi, so it's much harder to make bindings to qt than to gtk for most languages.


Version one might take a bit longer, but you don’t have to re-write the qt bindings every three months.

I’m all for ABI stability, but without API stability, it’s meaningless.


First of all Gtk doesn't require a rewrite every three months...

Secondly, at least for dynamic languages, you typically don't need to worry about wrapping Gtk's API, you just wrap GObject, and use introspection to dynamically generate classes.


Just every major release you have to start from scratch, again, including any tooling you might have had for previous version now requiring rewrite from scratch.


How does it compare against COM?


From my understanding, pretty badly. COM having an IDL and being way more explicit about things helps.


Objective C componentization model has its own traps. And too many of them unfortunately.

Object's ownership model in particular, when need of `[obj release]` is always a guess based on name of constructor function (name should contain "alloc", "new", "copy") but there are exceptions. I cannot even classify such architectural decision...

Among all practical UI systems I know, Windows's approach ( HWND and WndProc ) is the most flexible, stable on the long run. Despite its damn simplicity (or rather because of it?).


Objective C is not forced to have such design; if you don't import Foundation you can use plain libobjc and create your own root class, you can name methods however you want. Objective C historically boiled down to basically a preprocessor that transformed its Smalltalk-like additions to C to calls to objc_* functions and C structures. In the years, Apple has added syntax to interface with their own classes (i.e. @autorelease that AFAIK interfaces with NSAutoreleasePool), but that's about it.


GObject enables high level language bindings. They are an end goal, not a symptom.


It is perfectly reasonable to write a low level object system in something other than C++.


If everyone had rolled its own OOP in C now we would have dozens of incompatible and hard to interoperate object systems, all trying to do in a library the same stuff C++ and Objective C do at compiler level. I understand doing simple IsA relations with casts, or using function pointers for virtual dispatching, because they are sane and clean. These guys basically implemented reflection and duck typing in C.

I think it's one thing to wrap that sort of thing inside your program; it's after all your project and you can do whatever you deem appropriate to make it work. Using such a concoction in a library is kind of demanding everyone to deal with the mess you made if they wish to interact with your code, and it's a pretty different thing, IMHO. GObject is very far from the beauty and simplicity of C that every one knows and love. It's also arguably vastly clunkier and uglier that Objective C or C++ are.


How do you know those language bindings wouldn't exist if GTK+ used C++?


because none of them exist for qt, or fltk


This is arguably false; for instance, Qt has official first-party bindings for Python. It's just that the tools Qt has make them not that necessary. Qt has not one but 2 fully functional IDEs (QtCreator and KDevelop) and good designer tools that lower a lot the complexity of writing a GUI. QtQuick is also very nice.

Gnome has what? Anjuta? Glade was nice but it didn't avoid you the hassle of using GObject. I think Vala is a clear proof of what I'm trying to say. They knew the shortcomings of their system, and how difficult it was to use, so they basically wrote a language that hid the complexity of GObject under a thick layer of nicer syntax. It was also a way to discourage people from using C# and Gtk#...


sure, there are a lot of good qt tools, but they by and large tether you to c++. people would love to have good qt bindings for whatever language the rest of their project is in, but the fact that qt is implemented in c++ makes it hard.


No, it does not. The world is full of scriptable C++ programs and C++ libraries with dozens of language bindings. As I wrote before LLVM, perhaps the most important compiler toolkit out there, is completely written in C++ and almost every language out there has bindings to call into it.

If Qt is hard to interface with, it's not because of C++, it's because of Qt. Also, Gtk+ is quite hard to interface with to begin with, given how much it relies on GLib/GObject and how weird it is to use it. That's I think the #1 reason GTK has so many bindings, RAD would be simply not feasible otherwise.




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

Search: