This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

C++: Global classes: prefer static data or not?

Hi,
very sorry if this question should be too picky ... .

I have quite a bit of large "global classes" in my multi-module C++ software. (I mean a class without constructor/destructor, which is used exactly one time in the software - it is defined "globally").

As it is used only one time, I could specify the variables / functions as static or not. I was wondering, which way would create tighter code. According to my investigations I have the following rules now:
- Functions should be static, except inline functions - these must NOT be static (otherwise they are not inlined any more).
- Public data variables, which are heavily used outside class functions, should preferably be defined static.
- Private data variables, which are mainly used inside class functions, should preferably be defined non-static.

Could some C++ expert have a look at these rules and comment them? (ok, quite ok or complete nonsense?)

PS: In some C++ languages, it seems to be possible to define a complete class as static:

static class TestClass{
...
};

As I see, this is NOT possible in Keil C++, or am I wrong?

  • ... I first ended up using all classes non-static now.

    Just then I finally recognized that in such a case I cannot use bitband member variables (see my other post from today at the end of "--bitband compiler switch fails (very strange)".

    So for "one-time" classes with bitband member variables, I now would recommend to define them completely static (all members + functions static).

  • Sorry: One correction necessary: It is NOT required to define the class functions static. Only the struct with the bit definitions needs to be static, then bitband will work fine. This is quite convenient and safe then.

  • I was wondering, which way would create tighter code.

    Use straight C.

    - Functions should be static, except inline functions - these must NOT be static (otherwise they are not inlined any more).
    - Public data variables, which are heavily used outside class functions, should preferably be defined static.
    - Private data variables, which are mainly used inside class functions, should preferably be defined non-static.

    Declaring your code/data as static doesn't make it any shorter, so none of these will make your code any "tighter". Besides, if you declare all your data members as static, you will also have to declare as static all your methods that access them. Given that static methods cannot be virtual, why would you need classes at all? So I can only repeat: use straight C and don't cudgel you brains :)

  • I used C long time ... (and before this Assembler long time...).

    But C++ is really very smart. When I switched from C to C++ with Keil some months ago, I checked carefully, but the code does NOT get larger - it even got partly shorter to my surprise (but only some bytes for a 14kB code size, opt level 1). It goes about 100 bytes up / down, depending whether the "one-time" classes are non-static or static, but there was no destinctive trend that you could say static is better or not.

    With C++ you have all the advantage of very good inline function support, private data with inline access functions etc. etc. So in a large project coding becomes really MUCH more save.

    Concerning Bitband I first had a mixup - sorry - I thought it is necessary to use static functions, but this is NOT correct - it is completely sufficient if the Bitband data structure is defined static, then all works nicely. (You only get "problems" if you use bitband in classes which are used in a multiple way, e. g. a motor class used for several motors - in such a case you need this PBBADR scheme which I described in the other post).

    (If you look at the quite complicated construction of the Bitband address, it is quite clear, that the compiler can use the bitband access by himself only for static data).

    (If you use C, you will run into the same problem, if you try to organize the data in structures, e. g. you have a motor data struct and then 4 motors, then if this motor data struct includes some bit structures, you also will have to work with such a PBBADR construct as I discribed in my "bitband post").

  • With C++ you have all the advantage of very good inline function support, private data with inline access functions etc. etc. So in a large project coding becomes really MUCH more save.

    Inline sounds like micro-optimization to me (that's a bad thing most of the time). In my opinion, this is definetely not a good enough reason to switch to C++. Straight C has private data as well (remember static variables?), and access functions can be inlined by the compiler if you use multi-file compilation (a recent feature in a number of compilers). C++ is not harmless: it provides many more ways to shoot yourself in the foot. Besides, straight C is much easier to learn which is important if you want to add coders to your team.
    That's my opinion, anyway. Do as you like :-)

  • C++ inline functions are very good to use as accessor functions when interacting with the hardware. Compared to a #define, you still get full type checking etc. But you can hide if a pin is active high or low, or if a pin needs to switch between tristate input for "high" state and actively drive low for "low" state - might be needed for a 3V3 chip using an external pull-up to reach 5V high levels but needing strong sink capability.

    When turning on/off a car alarm, there are zero speed needs, so it doesn't matter if the activate_alarm() is a standard function call or an inlined function. If controlling the displayed data of a large LED panel, speed would matter.

    Talking about C++ classes, it's possible to have virtual methods that makes use of static data structures too. So instead of having one C++ class with multiple instances for multiple controlled hardware objects, there can be one base class and then several classes that contains virtual methods to access the specific hardware I/O.

    One way is to have one mapping layer that defines all I/O - possibly using bit-banding. And then C++ objects that contains the business logic and have action methods that makes use of the bit-banding of the mapping layer.

  • Hi Mike Kleshov,
    If you program in C, then there will come a point where you organize your data in structs. Usually then you would arrive at a point, where you would like to create Read-Only Access for some of your struct variable for external modules.

    This is not possible in C, but in C++ this is no problem, using such inline-functions (In C this will not work even if you use inline functions, as the multi-module-public inline functions (defined in some h file) of course have no access to static variables defined in a module).

    This sounds a simple point, but in fact it is very powerful to enable fail-safe programming. Alone due to this I would strongly recommend C++.

    As I told you, to my experience with Keil-ARM C++, C++ gives NO larger code size.

    I would clearly say, that C++ is easier in programming than C, and C easier than assembler, but a good microcontroller programmer should be able to use all 3 languages. (If you program C++ together with several people in a team, you should specify, whether it is allowed to use constructors-destructors, new-delete-operators and virtual base classes - for the beginning I would not allow them. Especially no new-delete-operator or virtual base classes, as those things will add quite a bit of internal "ghost code" to your program).

    I used to program some other microcontroller family for some years now in C, and I am really VERY impressed by the Keil-ARM compiler, just I miss currently heavily the fast implementation of saturated addition for int64/uint64 variables. This would be a snap to implement if embedded assembler would work, but unfortunately for a Thumb-only processor - as STM32F4 - embedded assembler does not work in the Keil-ARM compiler (in the previous compiler I used, I had to used embedded assembler at many points for interrupt programming - but the code generated by Keil ARM C++ is so impressively nice, that currently my only issue crying for embedded assembly is the addition for int64/uint64 with high-speed saturation checking - just this unfortunately currently is quite urgent for me).

    [If some Keil support team member is reading this, I would be VERY grateful for an estimate, whether in near future Keil ARM C++ will support inline assembly for Thumb mode, or whether at least in some near future I could expect an intrinsic compiler function for the saturated addition/subtraction of int64/uint64 (alternatively, I would be even more happy for an intrinsic for the branch commands B{cond} (best for any available condition EQ, NE, ...LE) - but I think unfortunately a branch command is not suitable to define by function mnemonics in C - further for saturation control it would be necessary to somehow force the compiler to use the signify versions of ADD and SUB previously to this intrinsic branch command) (if possible, please also insert an intrinsic for __vcvt int16/uint16, or support VCVT.S16/U16 for conversion from float to int16/uint16).]