This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Thoughts on minimizing global variables.

I remember reading a question a user had about handling interrupts, he didn't like the fact that he would need to create global state variables in order to implement handling them as state machines (a purest after my own heart).  I couldn't re-locate the discussion to add my thoughts there.

My way around that is to create a class for each set of variables and functions needed, created at compile time (i.e., not using "new").  Now the "global" variables are encapsulated in the private section of the class.  For example:

class BiasClass {

private:

int StateVariable;

...

public:

void SetVoltage(int Voltage);

void RunPidLoop(void);

...

};

// Declare non-inline functions here ...

// Declare 1 instance of class

BiasClass Bias;

// Now call functions

Bias.SetVoltage(Voltage);

// The timer interrupt calls:

Bias.RunPidLoop();

I haven't looked at the resulting assembly code but I bet that other than very slightly more startup time from the call to the constructor it's about the same efficiency as doing it in C.

Doug

  • The state variables don't necessarily have to be global. You can have a state processing function that keeps it's state as a static local variable. In the ISR, you call a static function with an event code that gets stored. In your background process, when exiting from ISR, you check if there is processing to do, then call the state function. When done, go back to sleep.

    I am not sure the what all the penalties are of using C++ on a small uController. I can guess, but I've never tried it personally.
  • Doug Broadwell said:
    My way around that is to create a class for each set of variables and functions needed, created at compile time (i.e., not using "new").  Now the "global" variables are encapsulated in the private section of the class.

    That's pretty similar to moving the functions that access the state variable into a separate file, and then making the state a file scope static.

    Also, if you're accessing the state variable from mainline code as well as from an ISR it needs to be volatile.

  • Right, but in my case I have several variables that need to be shared among several functions so in that case would need to be global (module) variables.

    I don't think there is much, if any penalties for using classes in this way in a small microcontroller - but you need to know what things in C++ will cost you more; certainly creating classes dynamically and polymorphism have costs to them.  It probably depends on the quality of the compiler being used.  One of Bjarne Stroustupt's guiding principals is that there should be no penalty for functionality not used.  In my example I'm really just using a class to make a set of variables only global to the set of functions contained in the class.

    Doug

  • A class is also a global construct. And it takes even more space than a simple global variable. And adds additional entry and exit code before/after main as well as constructor/destructor code. And makes accessing its content more complex (= slower and larger code).
    Exactly the type of things you should do when you want to write beautiful code for an exam and which you should avoid when writing efficient code for a microcontroller. Not to mention that a globally instantiated class is the same scope as a globally defined variable.

    If you don't want the variable to be globally visible, you can declare it a local static (!) variable inside the ISR. Then only the ISR can access it.
  • Thanks Jens-Michael, your response inspired me to do some testing to see what the real cost of using classes was.  I used CCS 6.0.1 and set up two projects, one implementing some functions in C style with module local (static) "global" variables and the other implementing them in a class.  With no optimizations the C version resulted in 50 assembly statements and the C++ resulted in 60.  However, when I turned on optimization, the C version was 32 statements and the C++ version was 33 statements.

    I think it is a common misconception that using classes has to incur a huge size or performance penalty.  Yes, in the C++ startup code there is a little performance hit calling the constructor to initialize class variables while in the C code they are initialized in a bulk copy, and the (null) destructor call on shutdown should be optimized away.  Remember, I'm creating the class at compile time, not on the heap with "new" which requires all call and variable accesses to be made via pointers.  Also, luckily the CCS compiler defaults to not adding the extra stack accesses to handle "try" "catch" handling.  There's no reason I can think of that a good compiler couldn't generate identical code for both.

    My overriding guiding principal when writing (non-trivial) software is "Managing Complexity";  anything that I can do that helps in that regard that doesn't have an unreasonable cost I try to do.

    The reason I can't just declare a static local variable in my ISR is that the variable must be accessed by other functions.  Also, a class can be declared static so that it is not global.

  • You're right, using classes does not HAVE to have a large impact. It depends on the class and its usage. Yes, with a simple, statically created class, the overhead is small. However, there is an overhead and (in this simple case) no gain at all. I guess in cases where using a class has a real benefit, the overhead will also increase noticeably. Not to mention that designing a proper class is much more effort than adding a global variable. And after all, bot end up in the same, unprotected ram and are equally prone to index-out-of-bounds or stack overflow problems. :)
  • Hi Jens-Michael,

    I've been thinking about my use of C++ in embedded projects, and I think the point you were making earlier is very valid: unless you have the memory and CPU-Cycles to spare you don't really want to do Object-Oriented Programming in embedded systems.  I only use a subset of C++ as a "Better C".  I like the finer granularity of encapsulating functions and variables in static classes (makes it explicit which functions and variables are publicly available and which are not).  I have one design that has 15 state machines in it run by one state engine and using Namespaces make the implementation Much cleaner.  Even though C++ has a greatly expanded Standard Library, there are many library functions that are huge pigs in space and time.  I just did a search and there's a nice discussion about embedded C++ at  http://electronics.stackexchange.com/questions/3027/is-c-suitable-for-embedded-systems

    Regards,

    Doug

  • Nice discussion indeed.
    I liked teh statement about a code spending more than 20% of the time for coding will produce bad code or be a bad (or unexperienced) coder. This means, someone who spends 100hours an a problem, 20 of them for coding, is a good coder, and someone spending only 30 hours on the problem, but 25 of them for coding, is a bad coder. I'd say, people making this kind of statements are the ones who wonder why their 1MHz system won't do more than 10 calculations per second with their nice object-oriented floating-point math :)

    My approach is to use the tool that fits best from the tools you have. Where 'best fit' is not necessarily the most advanced or 'dogmatically correct' one. There's nothing to say against C++, as long as one doesn't forget that some of the nice features of C++ are not so nice to use if you have a resource-limited and/or time-critical system. The namespaces you mentioned have more or less no impact on the code and are mainly organizational. Static objects aren't a big problem too, as you figured out by your tests. Other things are (especially exceptions).

    Our own Java-based metering device server application (when originally created by external coders) used XML descriptions for passing floating point values from one part of the application to another. Where passing a simple integer would have been sufficient. Needless to say that it was utterly failing when it had to handle than the two or three test devices used during development. Our current implementation (where all this nonsense was removed) handles >500 devices simultaneously and there's still some room. And much more functionality. On the same hardware. Using OOP just for its own sake is simply stupid. If you have a reason, it's fine. Same for using C++ vs. C vs. assembly. In both directions.

**Attention** This is a public forum