Arduino the Object Oriented way

MattS-UK: Personally I would not call simple inheritance polymorphism, even with virtual functions - It's simply, inheritance. Multiple inheritance and overriding methods, would be examples of polymorphism, to my mind.

Using virtual functions is by definition polymorphic behavior. The derived class is overriding the virtual functions declared in the base.

If you were simply extending another class by using it as a base and adding further functionality in the derived class, then its just simple inheritance.

The write up is not bad, but I think the examples could be far simpler. The functionality inside the classes is going to draw attention away from the actual points the article is trying to explain.

MattS-UK: As an OOP tutorial held up to novices, the article is highly opinionated, dubious even, in places.

Agreed. It's my opinion, it's the way I write things, it's a specific way I use OO with arduino (specifically: giving everything a setup and a loop). Maybe I should put a caveat to that effect towards the beginning.

MattS-UK: Especially.

  • Neglecting to mention the cost of OOP.
  • Overuse of private properties
  • brightnessClicker 'should' [not] be a property of headlamp - Breaks the MVC pattern.
  • Calling millis() repeatedly
  • Blocking a refresh method for 800,000 clock cycles.

I don't see what's wrong with making things private.

And it doesn't make sense to complain that OOP is expensive, and to be peevish that I don't use MVC everywhere. Yes, you can do make event queues and whatnot that malloc space for event objects. Not worth it. And in any case, I state repeatedly that this sort of stuff is not suitable for anything where cycles are important.

As to not making brightness clicker part of headlamp by direct composition - I just disagree. It's a fine way to do (some) things. Direct composition doesn't in itself break MVC. If this sort of composition does break MVC and composition by reference doesn't, isn't it a little odd that the syntax and method calls you need to make are otherwise identical?

Not sure that calling millis() repeatedly is a problem. How much work is it, compared to passing it around on the stack or leaving it in a sketch-scoped variable? I would have thought that millis() talks pretty directly to the chip somewhere, straight to a hardware port.

I used to take millis() once and pass it into the loop() for everyone, but eventually decided that the whole point is that these things shouldn't need to synchronize.

"The problem is that there are then two places [where a click is started]" The redundancy can be reduced logically, by sticking to a read/modify/write pattern.

Yes, my code doesn't strictly adhere to a pattern. I like using an enum for states and having a loop that is strictly that switch statement and nothing else.

Maybe I should pull out and make explicit that some methods are smalltalk-style 'messages' intended to be called by other things, that they comprise an interface to the object.

I see no tangible benefit in the Runnable class. As such, the class adds unnecessary complexity, consuming RAM and clock cycles needlessly.

Well, there are intangible benefits to it. Helps clarify what you are doing, I think. Actually, the real reason is that Runnable and its automatically-populated queue in effect provide a higher-level framework than the sturdy old setup() and loop() methods on their own.

Personally I would not call simple inheritance polymorphism, even with virtual functions - It's simply, inheritance. Multiple inheritance and overriding methods, would be examples of polymorphism, to my mind.

Perhaps I need to point out that the benefit of the polymorphic button class is that you could write a class that does something different on a long click and short click, and that you wouldn't need to rewrite the "debounce/short/long click" logic again.

"It's called 'time-slicing'" No, not really. Time-slicing is an established term, which implies pre-empting, by a scheduler.

Wikipedia agrees. What do you call multitasking by way of yielding control, then? Actually, I'll ask wikipedia. … Hmm, "co-operative multitasking" seems to be the thing to call it.

"co-operative multitasking" seems to be the thing to call it.

Also "Run to completion scheduler."

I have been a fan of OOP ever since I came across Smalltalk many many years ago - at a time when I could not afford the compiler.

Alas IMHO this discussion has evolved to neatly illustrate a big problem. The people who know OOP can't resist talking about it in technical terms that make it completely inaccessible to beginners.

I do realize that (for example) the engineers who design a motor car need to know a great deal of technical stuff that the average car owner does not even know exists. But the beauty of cars is that their user-interface is easily understood and is the ONLY thing that 99% of the users are aware of.

I also realize that this Forum is used by programmers (the equivalent of the car design engineers) and it is natural that they should converse among themselves in technical jargon.

What is missing here, however, is the layer provided by the marketing system in the motor industry which effectively isolates the user from the engineering jargon.

If @PaulMurrayCbr's contribution is to be useful for newcomers (as I think was the original intention - at least, for newcomers to OOP) then it seems to me there needs to be scope to present it separately from the technical discussion that has been taking place in this Thread.

...R

@pYro_65

pYro_65: Using virtual functions is by definition polymorphic behavior.

Fair comment.

The write up is not bad,

It's not bad but it's not good either.

I think the examples could be far simpler.

Agreed. I started skimming when I got to the tail light. I think few would read so far, unless they were building exactly the same project.

@Robin2

Robin2: The people you know OOP can't resist talking about it in technical terms that make it completely inaccessible to beginners.

When someone says, this is how it should be done, when in truth, this is how it could be done, you are going to end up with a technical discussion.

What is missing here, however, is the layer provided by the marketing system in the motor industry which effectively isolates the user from the engineering jargon.

Disagree. What is missing here is technical critique and peer review.

PaulMurrayCbr: Agreed. It's my opinion, it's the way I write things, it's a specific way I use OO with arduino (specifically: giving everything a setup and a loop). Maybe I should put a caveat to that effect towards the beginning.

What you do is not a million miles from what I do. A caveat is an excellent idea.

I don't see what's wrong with making things private.

As you say in your comments, // make these slow for testing If you can think of a need to change the values, the properties should not be private. Further, private properties block inheritance and code reuse, which can be very frustrating.

And it doesn't make sense to complain that OOP is expensive,

I'm not complaining. Abstraction costs RAM and clock cycles, it's a fact.

As to not making brightness clicker part of headlamp by direct composition - I just disagree. It's a fine way to do (some) things. Direct composition doesn't in itself break MVC. If this sort of composition does break MVC and composition by reference doesn't, isn't it a little odd that the syntax and method calls you need to make are otherwise identical?

I think it odd that a second instance of headlamp comes with a second instance of brightnessClicker - That would be the proof of MVC being broken.

Not sure that calling millis() repeatedly is a problem.

Concurrency is, or could be, the problem.

Yes, my code doesn't strictly adhere to a pattern.

I don't have a problem with your code. I am contending your use of 'should' and 'of course.' MVC is about the most common hardware control rationale out there. OOP can make MVC implementation much easier. The onus would be on you to explain why MVC 'should' be ignored. Alternatively, you could mention it exists and point out your example is too trivial to warrant strict adherence.

Well, there are intangible benefits to it [the runnable class].

On an 8 bit micro? No, I don't think there are any benefits, intangible or otherwise. Art for art's sake. Complication for complication sake, in my opinion.

Hmm, "co-operative multitasking" seems to be the thing to call it.

I almost called it co-operative tasking. I very deliberately did not call it multitasking, of any kind.

MattS-UK:

What is missing here, however, is the layer provided by the marketing system in the motor industry which effectively isolates the user from the engineering jargon.

Disagree. What is missing here is technical critique and peer review.

Disagree. What you have said is correct. But it is quite different from the needed separation between users and developers - for the benefit and convenience of users.

In my experience it can be very difficult to get developers (in any field) to appreciate the very different perspective of users.

...R

Robin2: Disagree. What you have said is correct. But it is quite different from the needed separation between users and developers - for the benefit and convenience of users.

Reading the article infers one is, or wishes to be, a developer. Hence, I believe the issue is one of education and not separation. My point would be, materials offered up as being educational, need to be technically correct. Matters of opinion maybe unavoidable but they should be clearly delineated, from matters of fact and established, expert consensus.

I completely agree the conceptual language, which sometimes obscures OOP, should be kept to a minimum within introductory tutorials. However, I see no reason to avoid the terminology, during review of such articles. I am aware of the, not in front the children, school of thought but I do not agree with it.

In my experience it can be very difficult to get developers (in any field) to appreciate the very different perspective of users.

Careful with that stereotype, Eugene ;)

westfw:

Camel:
Modern C++ features such as ranged loops, lambda functions, auto type deduction and so on really make C++ a high-level language…

I’d like to see an Arduino example using one or more of those. An awful lot of the time, for C++ programmers, “no performance sacrifice” means “if your actual data was 1MB, encapsulating it all in a useful and elegant C++ container only adds another 100k, and obviously 10% is not very significant” rather than “the object overhead fits in 2k with plenty of room for actual data.”

The items mentioned by Camel have little to do with container classes. They are just features in C++11, that containers can of course use.

Here is an Arduino based example that contains a ranged loop and auto type deduction. It also uses one of my ‘container’ classes called BitBool (in library manager).

This example provides a more efficient method printing binary than provided by the core. And it can be used to print 64bit binary which is not even implemented in the core (max 32bits).

The code to use it is very simple, and its quite efficient.

#include <BitBool.h>

void setup() {

  uint64_t data = 0xAABBCCDD00112233;

  Serial.begin(9600);
  Serial.print( "Printed as binary: " );

  auto &bits = toBitBool<REVERSE_BOTH>(data);

  for( auto bit : bits ){
    Serial.write( '0' + (bit ? 1 : 0) );
  }
}

void loop() {}

As for lambdas, they can be viewed as simply functions that you can declare inline, or inside another function.

MattS-UK: Careful with that stereotype, Eugene ;)

Genuinely don't know what you had in mind when you wrote that.

...R

Some changes made. I have a couple of annoying writing ticks: overuse of ‘of course’, saying “note that X” rather than simply saying “X”. And I included a link to a relevant wikipedia page.

Robin2:
Genuinely don’t know what you had in mind when you wrote that.

“Careful with that axe, Eugene(!)” Is a lengthy instrumental number by Pink Floyd. The track was later reworked and re-named, “Come in number 51. Your time is up!” Both titles refer to self indulgence.

What I had in mind was the irony, of attributing prejudices to stereotypes.

Apologies for my musical ignorance. I may recognize it if I heard it - but I am very poor at titles.

...R

(deleted)

lennarthennigs: I have a question though on constructor vs setup() use. Could you please give me some more details/examples on what can be put inside the constructor of a class and what should be part of a setup() method?

setup() for anything that calls methods from the arduino liobrary - anything to do with hardware initalisation. pinMode() is the most common thing.

constuctor for stuff that is "pure" C++. Especially for setting const variables.

The point to remember is that the Arduino environment does quite a bit of work before setup() gets called. I don't know what, exactly, but you can't rely on the hardware state being reasonable until then.

Constructors get called ... actually, I don't know when constructors get called. It may even be the case that thinks like assignments to const variables are pulled out by the compiler and baked into the binary image - they never really get executed at all.

I mean … maybe I'm wrong. Maybe it would all be perfectly ok to put things in constructors. But then again, maybe not.

For example, it's common for things like the LCD object provided by a library to have a constructor to assign the pins, but a separate initialize() method to be called in setup().

PaulMurrayCbr: Constructors get called ... actually, I don't know when constructors get called.

Early. Well before anything Arduino has run.

PaulMurrayCbr: For example, it's common for things like the LCD object provided by a library to have a constructor to assign the pins, but a separate initialize() method to be called in setup().

Correct design.

Constructors get called ... actually, I don't know when constructors get called.

Early. Well before anything Arduino has run.

In the compiler-added startup code, after "initialized data" is copied from flash into RAM and uninitialized data is cleared, and before main() is called.

westfw: Early. Well before anything Arduino has run. In the compiler-added startup code, after "initialized data" is copied from flash into RAM and uninitialized data is cleared, and before main() is called.

IIRC, C++ destructor semantics are difficult to navigate when you have multiple virtual inheritance and things like that. The best practice seems to be not to write things that rely on clever destructor behaviour.

Liked your take on Arduino OO design with setup and loop as standard methods. I'll bear it in mind for future coding

In my day when defining classes, I seem to recall coding let and set functions for object properties. How would you implement read only object properties or integrated validation for setting an object property?