The current coding style of the Tk library is archaic, some examples/suggestions:
The use of register variables is obsolete, modern compilers are ignoring this keyword, a compiler knows better which variable should be registered.
Nowadays inline functions are state of the art, and a big help to reduce speed and size. But this requires that the superfluous and harassing MODULE_SCOPE will be removed (the implicit extern declaration is causing linkage problems).
A line limit of 78 characters is style of the 90's. I'm developing software since more than 30 years and I suggest a line limit of 105 (result of experience).
The excessive use of curly brackets is not state of the art and disturbing. Short code does not need curly brackets, example:
instead of
This should not be mixed, this means:
and not
When using compiler conditions the curly braces can be annoying; example without curly braces:
With the use of curly braces it is ugly:
The C++ comment style (also belongs to C99 standard) is shorter and more clear:
instead of
Take into account that editors are highlighting the syntax.
One more example, combined with too many curly braces:
instead of
A more clear distinction between booleans and non-booleans, some examples:
is ok, because here the pointer will be used as a boolean – it is null or it is not null –, but the next example is wrong:
The function strcmp does not return a boolean, the return value is either 0, +1, or -1, and none of these values is true or false, all the values are equitable, and the correct expression is:
And interpreting the result as "equal or not equal" is not possible, in this case strcmp has to return a non-zero value for equality. The inaccurate expression "if (!strcmp(...))" is confusing – although syntactically correct.
More worse is the archaic use of boolean values for variables. The C compiler does not provide predefined boolean values, but they can be defined, e.g.
I used this kind of definition because the functions Tcl_GetBooleanFromObj, etc. are working with type int.
Now it will be easier to see whether a variable is dedicated for a boolean value, and whether a boolean or an integer value will be assigned. I did not use a style like
In this case it's obviously better to be conform to the well known C++ standard.
Moreover the vaiable name should express whether it contains a boolean, example:
is much better than
One more observation: a function call like
id more expressive than this function call:
Integer type int should be used only if the variable may have negative values, otherwise unsigned (and not unsigned int – although it's the same for the compiler) is appropriate:
It is more clear whether a negative value is allowed, or not.
Some processors (e.g. AMD) are processing this code
faster than this code
(see the handbook of the AMD processor).
The current C norm provides a more elaborated choice of integers, see this example:
Currently the GNU compiler is using 32 bit integers for int even on 64 bit architectures, but this choice is arbitrary, the C standard also allows the choice of 64 bit integers. Probably the GNU compiler will change this choice in the future, so it is recommended to use a more exact definition for this structure:
This definition is forcing the use of 32 bit integers, because:
Furthermore it is more precise to use unsigned integers, both variables will never have negative values.
Note: for the exact integer types the header file stdint.h is required. This header belongs to the C99 standard. Some are saying that stdint.h isn't available at all platforms, but we are living in year 2015, and C99 is from year 1999. C compilers not fulfilling the C99 standard are unserious, nobody is using such compilers. Even MSVC is providing stdint.h since 12.0 (2013), and this is the worst compiler of all.
The style of variable declaration is confusing, although many so called gurus are recommending this style – never trust a guru! Example:
This is any type of an array declaration, this is clear, but which type? At the first glance it seems to be a pointer to an array of 4 integers (because the int is separated), but it is an array of 4 pointers to integers, and this will be expressed clearly in this way:
Here the pointer to int is separated, because it's an array of pointers. It is said that the "drawback" of the latter style is that in general only a single variable can be declared, but this is not a drawback – I prefer not to declare more than one variable in one line –, and a clear expression is more important.
The declaration of static functions is in seldom cases unavoidable, but in most cases superfluous, and archaic style. Ordering the static functions inside the module, so that the definition of the function will be seen from any caller, is the preferred way, because this helps the compiler to optimize the function calls. Moreover the maintenance of the declaration for static functions will be avoided. The advantage of the pre-declaration of static functions is to have an overview, but this can be compensated:
The use of an IDE.
In general a static functions should be defined before any non-static function will be defined.
I don't know whether the target compiler for the Tk library is always the GNU compiler (or clang), or if any other compiler should also compile this code. But if only GNU (or clang) is targeted than the use of nested functions (also called lexical scoping) has big advantages:
Algorithms that shuffle around lots of variables will become cleaner and more readable, and the compiler optimization is working better.
If a function has to perform many tasks, the code will become quite more readable when calling sub-functions.
The repetition of code at many places inside a function – especially in the Tcl/Tk library this occurs often – can be reduced/shifted to a nested function.
Too many parenthesis are disturbing the readability and code maintenance, example:
The precedence of operator || is very low, and lower than the precedence of comparison operators, any C programmer, and any mathematician, is knowing this. Better (more readable) is:
Another examples why too many parenthesis are disturbing:
Obviously much better is:
Avoid mixed styles of line breaking. Example:
More readable is:
Next example:
Better is:
Line breaks should be done before the logical operators && and ||, not after; example:
The readability increases when breaking before the operator:
Another example:
This should be written in the following way:
In C++ there is a big difference between the prefix increment/decrement and the postfix increment/decrement. So in C++ always the prefix operator should be used, except the postfix operator is explicitly necessary. It's very comfortable for C++ developers if the C code is also using this convention. Example:
instead of
Note that these suggestions do not advice to rewrite the library, but I like to recommend to consider this for future code, or for reworked code. The disadvantage is that the library will contain two styles, the old and the new one. But I think this is better than to live with archaic code. And there exists powerful source code formatters which can be used to apply some of the rules to the whole library code.