2

When dealing with microcontrollers there are things that are inherently global - I'm thinking about peripherals like serial ports or other interfaces. There are also peripherals that are not only global but there is only one (and there will never be more) - like peripheral controlling core clocks or interrupt controller. These peripherals do have some kind of global state (for example - core clock is set to something) and it's inefficient to reverse-calculate these values.

If I'd like my program to be nicely object-oriented, I'm having hard time deciding how to deal with such objects... Global variables are not nice and this is obvious, but I just don't know (not enough experience) whether I should try to "hide" the fact that these things ARE global... For example "cin" or "stdout" are globals too (let's ignore the fact that in multithreaded apps these are usually thread-specific) and noone is hiding that... Let's stick with the clock generator peripheral - there is only one, so I could use the singleton anti-pattern (; or make the class static or just have the single object global (that's what I have usually done), as this object has the current clock setting stored and this value is needed for LOTS of other things - it's needed to set system timer used by RTOS, it's needed to set clocks for other peripherals (UART baudrate, SPI bitrate, ...), it's needed to set correct clock for external memories or configure memory wait states. That's why I think that creating one object in main() and passing it around everywhere would be a bit cumbersome...

I could write the methods so that all "global" information would come from the peripheral registers (for example the core frequency could be reverse-calculated from current PLL settings), but this also seems like a wrong idea, not to mention that creating object for clock generator peripheral everywhere would look funny...

Current clock setting could be stored in static member of the class, but from here there's only one small step towards a fully static class (as "this" pointer will be useless for a class that has no state)...

The solution usually found in not-object-oriented programs is closest to fully static class - there are only functions that operate on global variables.

Anyone has some nice idea how to deal with such scenario nicely or whether this problem is worth the time? Maybe I should just use one global object and be done with it? (;

Freddie Chopin
  • 8,440
  • 2
  • 28
  • 58
  • Read about [Singleton][1] pattern, that's what you should use. [1]: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern – piokuc Apr 07 '13 at 07:44
  • 1
    I'm not sure what your actual issue is. What you're describing is exactly the point of the singleton pattern, which I assume you're aware of since you mentioned it yourself. – Parker Kemp Apr 07 '13 at 07:46
  • I'm also aware that nowadays Singleton doesn't have good "publicity" (; and is frowned-upon... Moreover - efectively Singleton is just a single global variable or a static class - it's still global. What I'm trying to learn is whether in this scenario the "globality" can be avoided somehow. – Freddie Chopin Apr 07 '13 at 07:49
  • @Freddie - Like you say, the singleton pattern is frowned upon because some people use it every time they need one instance of a class. We don't have any patterns for having two or three objects of the same class, so why complicate the case with one object? If you need one clock driver, just create an object of that class and be done with it! You can pass a reference to it when needed. You'll soon see that it will *not* be used everywhere. – Bo Persson Apr 07 '13 at 10:28
  • If you have common *functionality*, then even though they are static in number, they can still be a class. For instance some boards have multiple serial ports. Some drivers will have composition, like a *touch screen* driver may use an *spi* driver. Linux is [structured](https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/Documentation/driver-model/overview.txt) like this, but is written in `C` for more control. The parts you think are `static` may not be; especially if you start looking at families of chips. – artless noise Apr 07 '13 at 14:43
  • I never said that USART is "single" - this was an example of sth that is inherently global. A thing that is single and global is interrupt controller or clock controller. – Freddie Chopin Apr 07 '13 at 15:23
  • @FreddieChopin Sorry, what I said is not meant to be taken literally for your design. Substitute *interrupt controller* and *clock controller* for *UART*. Some boards do have multiple *interrupt controllers*. I upvoted [Parker Kemp](http://stackoverflow.com/users/1239706/parker-kemp) because this is the flavor of his answer. The resources are static for the board, and there is no changing that. However, it doesn't mean it is static for all uses of the code. This is a perfect fit for a **singleton** or **multipleton**.; A physical limitation on objects. – artless noise Apr 07 '13 at 17:11

4 Answers4

5

If I'd like my program to be nicely object-oriented, I'm having hard time deciding how to deal with such objects... Global variables are not nice and this is obvious, but I just don't know (not enough experience) whether I should try to "hide" the fact that these things ARE global...

When I read that, I wonder if you know why you are using OOP and why you don't use globals.

Firstly, OOP is a tool, not a goal. In your case, the interrupt controller doesn't need things like derivation and virtual functions. All you will need is an interface to program it, wrapped in a single class. You could even use a set of plain functions that do that (C=style modular programming) without giving up on maintainability. In your case, making the single instance global is even clearer. Imagine the alternative, where different parts of the program could instantiate a class that is used to access the same UART underneath. If you're using globals, the code (or rather the author) is aware of this and will think about how to coordinate access.

Now, concerning the globals, here's an example why not to use them:

int a, b, c;

void f1()
{
    c = a;
    f3();
}

void f2()
{
    c = b;
    f3();
}

void f3()
{
    // use c
}

int main()
{
    a = 4;
    f1();
    b = 5;
    f2();
}

The point here is that parameters are stored in globals instead of passing them around as actual parameters and making it difficult to see where and when they are used. Also, the use above totally rules out any recursive calls. Concerning your environment, there are things that are inherently global though, because they are unique parts of the environment, like the interrupt controller (similar to cin/cout/cerr/clog). Don't worry about those. There have to be really many of them used all over the place until you need to think about restricting access.

There are two guidelines to make this easier:

  • The larger the scope of an object, the more it needs a speaking name (compare to a, b, c above). In your case, I'd store all hardware-specific objects in a common namespace, so it is obvious that some code is accessing globals. This also allows separate testing and reuse of this namespace. Many hardware vendors even provide something like this in the form of a library, in order to help application development.
  • In the code wrapping/modelling the hardware, perform requests but don't make decisions that are specific to the application on top of it. Compare to e.g. the standard streams, that are provided but never used by the standard library itself, except maybe for dumping fatal error information before aborting after a failed assertion.
artless noise
  • 21,212
  • 6
  • 68
  • 105
Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
2

You have pretty much outlined your options:

  • globals
  • static data members of classes / singletons

It's ultimately up to you to decide between the two, and choose which you like more from the aesthetic or other prospective.

At the end of the day, just as you have said, you'll still have one clock generator, one UART, etc.

One thing to keep in mind is that too much abstraction solely for the purpose of abstraction isn't going to buy you much if anything. What it may do, however, is make it harder for someone unfamiliar to your code figure out how things really work behind the layers of classes. So, take into account your team, if any.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
2

The Singleton pattern is a source of debate, for sure. Some people simply say it's "bad", but I would disagree; it is just widely misused and misunderstood. It is definitely useful in certain situations. In fact, it applies to exactly the situation you've described.

If you have a class that needs to be available globally and by its very nature cannot have multiple instances, then a singleton is the best option.

Parker Kemp
  • 719
  • 1
  • 10
  • 23
1

Singletons, global objects, static classes, they are all the same. Dress the evil global state in whatever sauce you want, it's still global state. The problem is in the salad, not in the dressing, so to speak.

A little-explored path is monadic code, Haskell-style (yes in C++). I have never tried it myself, but from the looks of it, this option should be fun. See e.g. here for an example implementation of Monad interface in C++.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • 1
    -1 Generally in *OOP* concepts this is true. However, in this case there are physical limitations on the objects. If there is only a single interrupt controller, why is it helpful to be able to create more of them? Certainly it may be better to concentrate your efforts on other portions of the design where OOP concepts will pay off. OOP is great if your objects match the problem domain. Here there is a physical limitation. – artless noise Apr 07 '13 at 17:20
  • @artlessnoise: no one is advocating a multitude of interrupt controllers. The problem is that the one you've got is globally visible, not that somebody could create another one. – n. m. could be an AI Apr 07 '13 at 17:31
  • If you are worried about visibility, then a [factory](http://en.wikipedia.org/wiki/Factory_method_pattern) can be used. This borders on *paranoia* and I would suggest that design efforts are better spent elsewhere. Simply using directories for *header* placement and `-I` directives can achieve the same results and/or partial linking with symbol removal. Unless you expect people to manually `extern` the class; I think you have personnel problems in this case; comments can also help. I understand the downsides to this, but I think they are warranted in this case. – artless noise Apr 07 '13 at 17:41
  • @artlessnoise Global state, when only exposed indirectly via a façade, a decorator, an adapter, or whatever is in vogue today, is still global state. If you can hide your piece of state from the rest of the program *completely*, without a trace, more power to you. In most cases there will be some strings to pull, with visible consequences. And nobody is going to do nasty illegal things, just call perfectly legal, documented, globally available functions. – n. m. could be an AI Apr 07 '13 at 18:15