On 27/08/2013 02:48, Lex Trotman wrote:
Hi Nick,
Thanks for the well thought out sensible proposal. In general I agree with the idea of progressively moving appropriate parts of the code to using appropriate C++ features. Of course, as usual, I disagree on some of the details and I'm sure we would disagree on some of the selections of files, but thats healthy.
Thanks for considering it ;-)
I now think gradually using a (quite heavily restricted) subset of C++98 for *some* source files might be better than moving to C99 (more on that below). I still support C99 over just C90 though. (I don't do a lot of Geany coding now, but I plan to do some from time to time, so obviously this is just my opinion and less important than more active devs).
I'll address the "restricted subset" idea here and refer back from relevant places below. My experience of a couple of projects migrating to C++ is that applying strict "limits" on the C++ features just leads to implementing those features poorly in the code itself. One common one is to not allow virtual functions, but in my experience that just leads to the use of function pointers and the associated ugly syntax and increase in errors.
If we want to write OOP code, allow "virtual" and "class". Personally I tend to think for Geany OOP is mostly useful for GTK. If we want that, we would need gtkmm bindings.
Another reason why I suggest we don't use OOP is that getting overloading right with inheritance is difficult for C++ beginners, and can cause bugs with experienced devs. Avoiding OOP simplifies the unintuitive corner cases of C++, and is easier for C programmers.
The approach that seems to have worked better is the same as that required for C++ in general, *appropriate* use of features. Whilst that is easier to control in a corporate situation where programmers can be trained properly, review by senior devs should catch the more egregious misuses within Geany, its not like we have a huge developer base to train anyway :).
We have a fairly large potential contributor base.
Reasons:
- No build problems for Long Term Support distros that don't support C99
(but clearly do compile scintilla's C++98).
Neil is attempting to move Scintilla forward, it might soon be using C++03.5
Another project was against Neil moving to C++11, not just us. Nice though C++11 is. As things stand though, we know using more C++98 won't cause problems for users. C99 possibly might. C++11 may do.
- RAII - this is a pretty essential feature for safe resource management.
It's a stupid acronym though, it's basically automatic scoped destruction:
http://en.wikipedia.org/wiki/**Resource_Acquisition_is_**Initializationhttp://en.wikipedia.org/wiki/Resource_Acquisition_is_Initialization
Agree on both the usefulness and the silly name :)
:)
- References can be useful in making code more readable vs pointers.
Unfortunately, since references are immutable, you can never get rid of pointers in imperative style programs, that leads to code with a mix, luckily getting it wrong (using . not ->) will be picked up by the compiler.
I don't want to use references instead of pointers, only where references make the code easier to maintain. E.g. 'Output' arguments and sometimes in local scope to simplify repeated expressions.
- Developer enjoyment, productivity & gaining more experience with C++.
(This might be an incentive for e.g. me to spend more time working on Geany). This has to be offset against those that might need to learn e.g. RAII.
Indeed there is a learning curve, but hopefully many will find that useful and attractive.
+1
- Possibly we could use a GObject wrapper for safe reference counting. I
don't want to add any dependencies on C++ libraries though.
Or what almost all my C++03 programs have, a template headed: /* The ubiquitous intrusive reference counted smart pointer */ :)
Of course use gobject, for things that are gobjects, but don't bring it into things that are not, the idea is to move to C++, not to tie more closely to a C library.
I don't know how well it would work in practice. But it might be better to stick to one type for reference counting than two.
- Possibly we could have some template function utilities instead of
unsafe macros. Although I think most header files should be kept C90 compatible.
Yes existing headers that are used by the remaining C code will have to remain C compatible, and will probably need:
#ifdef __cplusplus extern "C" { #endif
around them.
There's a GLib macro for that.
But (for C++ files) that doesn't prevent having extra headers with templates in them.
Yes.
matt:
+1. While I'm also not sure it's a good idea in Geany and certainly won't be pressing for it anytime soon, 90% of C++'s crumminess is due to backwards compatibility with C, so I think it should be (theoretically, not socially) possible to gradually transition from one to the other in a project like Geany without too much pain.
colomban:
I doubt it, C++ is sufficiently different from a C program not to be compilable by a C++ compiler in many cases -- even if it is just for some implicit casts C++ requires to be explicit (IIRC), and there are plenty. Or maybe it depends what "too much pain" means:)
So long as the gain is larger than the pain.
I don't think it would be significant pain. I'm happy to be the guinea pig though.
Also, I doubt it's any kind of sensible either, because good C++ use is sufficiently different from C to require large rewrite. In this case, better rewrite everything and don't keep the clumsy code
I disagree it *requires* a large rewrite. You can make *good* use of some C++ features without having *idiomatic* use of C++. (E.g. dmd, the reference D compiler is not written in idiomatic C++ - it doesn't use the STL really, but it's still good code).
I agree with Nick, my experience is transitioning to C++ does not need a big re-write just to get a compile. You then *choose* what to re-write, to take advantage of features to make life easier and safer. Even I don't advocate wholesale re-writing of random code to C++ idioms without a reason :)
Yes, I don't want to move to C++ idioms unless there's a significant benefit, considered on a case by case basis.
I think it would be quite manageable. If we did this, I suggest just converting one source file at a time, and only files that can benefit from RAII and/or the more powerful type system.
Yes, so long as C compatible interfaces are available for as yet unconverted files its fine, for example if GeanyDocument became a C++ class appropriate headers would be needed.
I'd keep the API types (and all fundamental types) in C.
We would also need to disable some C++ features. I hoped we could do that with g++ flags, but I've only found this so far:
I addressed the problems with this general idea above. It will also lead to ongoing bikesheds on what features are "allowed" and why can't Geany progress to use more useful features and ... This is both wasteful of community time, and destructive of community spirit, better to encourage use of appropriate features, and where an "advanced" feature is appropriate, then it can be used without having to remove such constraints.
IMO features should be judged by weighing usefulness vs maintainability & community skillset.
-fno-rtti (disable runtime info for object inheritance)
Hardly worth it :)
How will we keep the ABI compatible with C then? ;-)
There ought to be a way to disable mixed code and declarations, but maybe not.
NEVER DECLARE ANYTHING UNTIL YOU HAVE A VALUE FOR IT. :)
Agree, where practical. But Colomban doesn't like it.
I don't know if this can be enforced by g++, but I suggest we disallow these keywords:
class
Why, its just a struct with default hiding, this just forces the use of struct xxx { private: .... }. The use of private members is an important part of the ability of the compiler to protect the programmers from themselves, and a big plus of C++, since it makes it easier to ensure that required invariants are maintained because only listed entities can change the members.
OK, you may be right. I thought it would make it clearer that inheritance and virtual functions are not allowed (even though they work with structs).
dynamic_cast
Agree it should *almost* never be used, and so just search for it is easy enough.
I think inheritance + virtuals can be hard to maintain. And harder for some contributors to grok.
friend
Unless you are implying never to use private members, sometimes you *have * to use friends.
I like private. I don't plan on using friend, but it's not so bad.
mutable
Its mostly useful for optimisations that we probably don't need ... yet :)
operator
Thats required to define the ubiquitous reference counting smart pointer, but agree that operators should *only* be used where their semantics mirror the standard ones. (and of course only in interfaces which are not used by C code)
I would prefer to keep our code simple by not defining our own containers. In Geany's src, I think we should avoid it for simplicity.
throw
Agree, when we have mixed C/C++ code, throwing an exception from C++ through C can cause all the resources in the C code to leak since it doesn't have any destructors to clean up for it.
OK.
virtual
Geany has a couple of places where a bastardised version of virtual functions have been implemented in C, its ugly, hard to understand, and error prone. These cases *should* be replaced by C++ virtuals (if anyone understands the C code enough to do it :)
I assume you refer to tagmanager. I'm not sure if TM needs to be OOP. I don't fancy rewriting it either ;-)