On 24/07/2015 23:58, T.J. Duchene wrote:


On 7/24/2015 3:57 AM, Roger Leigh wrote:

First, thank you for the reply, Roger. I supremely appreciate it.

The C++ compiler objectively does a better job here.  It's simpler,
quicker to write, safer, easier to refactor.  And when I've done a
full conversion of GObject-based C code to C++, the C++ compiler has
picked up on real bugs which the C compiler hadn't picked up on
(because the type information had been deliberately cast away).  GTK+
programs can be riddled with bugs, without the developer being any the
wiser in the absence of compiler diagnostics.

That is true to some degree. I don't agree entirely.  It really depends
on the compiler and what settings are being used.  GCC is not the
world's greatest compiler by any stretch of the imagination, and there
are a lot of extraordinarily lazy FOSS programmers who ignore warnings,
and do not initialize variables before using them.

It's worse than this, and is nothing to do with the compiler and everything to do with unsafe practices in the libraries. With GTK/GObject, suppose I use one of the macro casts for polymorphism such as GTK_WIDGET(). This cast casts a pointer to a (GtkWidget*) with a little checking. But it's essentially just the same as (GtkWidget*). This "works" if you use the correct cast macro with a variable of the correct type. BUT. What if you get it wrong? You have explicitly cast away the real type and told the compiler to use a completely different type. There is *no way* the compiler can issue a diagnostic if you got it wrong. In C++ this simply doesn't happen; upcasting is completely transparent, downcasting with dynamic_cast is completely safe. This can lead to long standing latent bugs in the codebase that are well hidden.

That's the start. Now consider stuff like signals and closures, g_signal_connect, g_cclosure_invoke etc. What happens to type-safety here? Again, it's almost always completely lost. What happens if you screw up? Nothing. You would get no diagnostic from the compiler, and this would result in bad things at runtime. In C++ with libsigc++, Boost.Signals etc. the signals and handlers are templated and specific for the signal and handler types, even going so far as to be able to rebind and elide arguments. If you get it wrong it will fail to compile, not crash and burn at runtime. And with the gpointer (void) data pointer in the signal, used to pass arbitrary data or object to the handler, that's completely unsafe--the handler has no guarantee it's going to get what it expected. Again, the C++ handlers don't do that--it's completely safe and checked at compile time.

This is not about "programmer laziness"--they were not in a position to be notified of any mistakes they made, at all.

No one can blame you
for disliking the fact that GTK tends to leak references  (at least from
warnings I seen on the console) when using Linux.  That's entirely out
of your hands.

No. It means the programmer screwed up the reference counting. *Every* instance is programmer error. Since different object types handle reference counting differently and inconsistently, this is a very difficult thing to get right. Do you get a newly-created object with a refcounf of 1? With a floating reference? Do you need to sink it? It's a nightmare. This is one reason the GTKmm bindings are nicer--they handle it correctly for you and give you a consistent and transparent way to handle it (GLib::RefPtr). No more manual refcounting, and no inconsistency makes it impossible to screw up.

If I had to critique GTK based on what little you have
told me, I'd say it was probably a bad design, trying to force OOP in C.

It's definitely both of these things. C is entirely unsuited to OOP if you care about the result being correct and robust.

That is not to say that OOP cannot be done in C, but as a programming
paradigm, I personally feel that OOP is a very flawed idea.  I've been
programming for a long time, and to be perfectly honest, I have seen
very few cases where OOP methodology actually makes things "better".
There are some nice features, but I don't buy the whole idea as actually
making software development easier. I'd sacrifice most of those features
in a heartbeat not to have to deal with exceptions.

I think this is at odds with general consensus to be honest. OOP can certainly be overused where other approaches would be more appropriate, but in the general case it provides many concrete benefits, so long as you don't try to do polymorphism in C.

This too also has its limits, but put it this way: I haven't had a
memory leak in C++ code in a decade due to the use of shared_ptr, and
this isn't something I can say for my GTK+ C code, or GTK+ C code in
general.

I would  qualify that statement by saying you have not had a leak as
long as an exception was not generated.

Absolutely not. All the code is completely exception safe and will not leak if exceptions are thrown. That's what RAII and smartpointers take care of. Running under valgrind shows zero leaks with exceptions being thrown all over the place. Exception handling can be *completely robust* when you use modern C++ techniques.


Regards,
Roger
_______________________________________________
Dng mailing list
Dng@lists.dyne.org
https://mailinglists.dyne.org/cgi-bin/mailman/listinfo/dng

Reply via email to