I came across your channel by accident through your debugging video, was impressed and watched the rest. Congratulations on a refreshing approach, and I hope it goes well.
You shouldn't need to use a c to denote a class, just use an upper case first letter of each word and you know its a class. If its camelCase, you know its a variable and if its in all caps with underscore spaces, its a constant. Easy peezy
Minor code suggestion, start using const in functions that don't modify the class, also for little jobs, I just put it the header. for example: int getX() const { return x; } I really like your style of video. I came to it trying to debug using platformio (On Linux so not quite the same but at least you pointed me in the right direction hopefully). Looking forward to more videos in the future.
dude, you needed to make this vid like 3 years ago!! thanks. i have watched it 4 times now. this is like a crash coarse on oop in arduino for someone that had a bit of arduino experience. definitely subbed!!
Nice video. This is something that I was looking for some time ago. Also this video is good to follow. If you ever run out of topics then Unit testing tutorial using Platform io would be absolutely the thing that is missing in this world. Keep it going 😊
This is cool. I just started playing esp32 development and my brief search on OOP in C basically just resulted in multiple people saying don't. As a result, I instead opted to create a few psuedo-classes using structs with internal functions. I was still using the Arduino IDE, forcing me to have everything in a single file. Now I'm just curious about the pros/cons of structs vs classes.
Great video idea, since most Arduino code examples are pretty much spaghetti code. However I have a small suggestion for improvement in your code. In the video you say that dynamic memory allocation should be avoided if possible on microcontrollers like Arduino (which is correct). But in the constructor of e.g. the 'c_canvas' class you use the new keyword, which in fact does dynamic memory allocation. Also you don't have a destructor where you delete the object. It's worth noting that in this case dynamic memory allocation is abolutely unecessary. So it would be better to just stack allocate these objects.
Very true. I don't recall why exactly i did it this way. Although i would argue that since only one of the display objects exists and it never has to be deleted (that wouldn't make much sense in a program about a display) it doesn't matter too much if i use dynamic memory or not. But sure, the project would work as well without dynamic memory if the display becomes a variable inside of canvas and coordinates gets a reInit() function like the snowflake class.
As I watch this I wondered why not use inheritance for different snowflakes, and snowstorm would have a list of the base class snowflake. To create random snowflakes use the concrete factory pattern to instantiate the snowflake and add them to the list. It is fun coding on the Arduino platform, I use the UNO or MEGA to do my controller projects for Halloween... BTW which C++ version are they using? And if memory serves, didn't C++11 add treads, and if so, could you make your Arduino sketch multithreaded? C++14 you can have functors and lambda functions... Anyway, YT recommended your video and it was fun to drop by and see what you were coding.
Would you elaborate on your ideas of "use inheritance for different snowflakes" , "concrete factory pattern" and etc? And it's advantages. Thanks Bro! in advance.
@@nova0302 Sure, I appreciate the question... the point I'd like before jumping in is that there are many ways to solve a problem... this is another example of a solution: The thing that stood out in my mind was a switch statement in the code, this means that there are variations or "specializations" of snowflakes. Just like the classic OO example of base class "fruit" and specializations of "apple", "banana", "pear", etc. you could model snowflakes with a base class and inherit "big flake", "little flake", etc. The Base class is the common snowflake that has the function "Update()" but now it is virtual and you let the subclasses or child classes implement that... the child classes have the pixel images and other properties that make it special... and that's the idea of inheritance, is that the child classes are the specializations of the base class. The next part was design patterns of Concrete Factory... factories are creational patterns, we want to "make" something and we have all these child classes, how to organize them into a single "make me an 'X' type snowflake"... And with this, there is a caveat... There are two patterns named factory, "abstract" and "concrete" and a lot of people find the naming confusing and it happens to me too. I refer you to Stack overflow has an excellent discussion on this topic... but if memory serves me correctly, the one to use in this case is "concrete factory" because you are making a "concrete" class like "Big snowflake" which is an actual class, but here's the idea: That in the main loop you ask the factory to make you a particular snowflake and it constructs it for you, this snowflake object can be put in a list, array, etc. and as before you are calling Update() but now it's virtual member function in the base class that is actually calling the specific Update() for that snowflake and snowflake type. The advantages: Largely in the coding of snowflake code, each child class (inherited from base class snowflake), is now singularly devoted to it's specialization without the complexity of switch cases and logic to do the specialization. I need to add that the snowflake code example is small and simple, so the advantages are not as apparent; however, it is a standard in OO design and in pays off in more complex problems where all that specialization would just tangle up the code and make the logic more difficult to write and maintain. Furthermore, if you want separation of objects that draw images from the hardware that the objects are being drawn on... e.g. the LCD or OLED screen, checkout the "Bridge Pattern" it's a way to decouple these two things... each can specialize independently and the "bridge" is the communications between them through an API. A bridge pattern is an example of structural patterns, where clearly this manages independent class hierarchies, and it solves (prevents) a problem called class explosion which is known as an Anti-pattern... (a whole other rabbit hole to chase down). I hope that helped answer your question... I realize that this is more information than you asked for, but I had to start somewhere and someone took the time to explain it to me... so I'm paying it forward. Cheers!
@@kayakMike1000 Got a point there... C++ wants to be the "Swiss Army Knife" of languages. However, I have found them to be interesting as a mix-in these notions of capture and such are new to me. But if you've ever wanted to do a pointer to a member function of a particular instance, it makes it easy-peasy lemon squeezy as you just capture that instance. I know that's a rare situation, I had one case where I needed rather wanted to do it, this was before C++14 (or whenever they added those) but I tried it and it's cool the language supported me doing it! But, granted, You have a point. I wonder what they did to C++ in C++23... Dare I look?
I believe that inheritance in this case is an excess of form over substance. I am from a school where the qualities were not worthy of an offspring of their own. Classic example: Cat and dog, but not dachshund and Alsatian. (Of course, we are talking about a certain level of generalization.) REMEMBER: excessive fragmentation is not good.
Hi, I have an idea for another video: How to create a code where it will be easy to replace parts, and it itself has loose connections between itself (simple enable or disable module). Take, for example, a thermometer. The sensor can be NTC, sensor, thermocouple... Showing can be on the display, Serial, several LEDs, or fan control. Or maybe several of them at once. I couldn't find anywhere how to operate (such pseudo-)events in arduino. (many events)
That is an interesting topic. OO is nice and clean but i got some unexpected error stuff i ran into. Like constructors with parameters that don't give an error but the thing won't run either, created a public init function for that. Anyways I wreck my way trough it. Thanks for the upload .
Is your object global? If so, be careful that its constructor doesn't involve anything that first has to be initialized in setup(). Remember that this object gets created BEFORE you hit setup(). This is why so many Arduino library objects require an init(). You define a global instance, it allocates static memory but only runs actual functions when called by init().
@@jse-shack825 Thanks. I have to take a look at the sequence of things and how the memory allocation of objects is working in a test setup. I did not call any obj.functions in setup() just assigned constructor params to private member vars in the constructor when declaring the object before setup().
WHY? OOP doesn't guarantee readability. You might break things into objects differently than someone else, depending on what you think is important at the time.
It makes it readable to yourself. If somebody else has to read your code then chances are high that you do coding for a living, which then means that you had a specific education or company guideline on how to structure OOP. This video is only there to educate hobbyists and beginners on how to move away from spaghetti code in main.cpp.
@@jse-shack825 lol. In my case, it helps to divide the code into independent modules, so that they cover the smallest possible scope and keep connections between them very weak. There is nothing to hide - the more lines of code, the worse.
Maybe a bit controversial: 1. rules of code formatting. 2. read up on design patterns 3. rewrite your code / refactor it - especially when something in it annoys you
OOP is cringe
I came across your channel by accident through your debugging video, was impressed and watched the rest. Congratulations on a refreshing approach, and I hope it goes well.
You shouldn't need to use a c to denote a class, just use an upper case first letter of each word and you know its a class. If its camelCase, you know its a variable and if its in all caps with underscore spaces, its a constant. Easy peezy
Minor code suggestion, start using const in functions that don't modify the class, also for little jobs, I just put it the header. for example: int getX() const { return x; } I really like your style of video. I came to it trying to debug using platformio (On Linux so not quite the same but at least you pointed me in the right direction hopefully). Looking forward to more videos in the future.
What's the benefit of using "const" in the function signature?
dude, you needed to make this vid like 3 years ago!! thanks. i have watched it 4 times now. this is like a crash coarse on oop in arduino for someone that had a bit of arduino experience. definitely subbed!!
Nice video. This is something that I was looking for some time ago. Also this video is good to follow. If you ever run out of topics then Unit testing tutorial using Platform io would be absolutely the thing that is missing in this world. Keep it going 😊
this is already my favorite channel
I'll be playing with this tomorrow. Thanks, and good luck with the channel 👍
This is cool. I just started playing esp32 development and my brief search on OOP in C basically just resulted in multiple people saying don't. As a result, I instead opted to create a few psuedo-classes using structs with internal functions. I was still using the Arduino IDE, forcing me to have everything in a single file. Now I'm just curious about the pros/cons of structs vs classes.
Great video idea, since most Arduino code examples are pretty much spaghetti code. However I have a small suggestion for improvement in your code. In the video you say that dynamic memory allocation should be avoided if possible on microcontrollers like Arduino (which is correct). But in the constructor of e.g. the 'c_canvas' class you use the new keyword, which in fact does dynamic memory allocation. Also you don't have a destructor where you delete the object. It's worth noting that in this case dynamic memory allocation is abolutely unecessary. So it would be better to just stack allocate these objects.
Very true. I don't recall why exactly i did it this way. Although i would argue that since only one of the display objects exists and it never has to be deleted (that wouldn't make much sense in a program about a display) it doesn't matter too much if i use dynamic memory or not. But sure, the project would work as well without dynamic memory if the display becomes a variable inside of canvas and coordinates gets a reInit() function like the snowflake class.
Damn thats a nice introduction. Thanks!
@0:41 in which app you drawing the object diagram
This Channel is amazing, 195th sub for u
As I watch this I wondered why not use inheritance for different snowflakes, and snowstorm would have a list of the base class snowflake. To create random snowflakes use the concrete factory pattern to instantiate the snowflake and add them to the list. It is fun coding on the Arduino platform, I use the UNO or MEGA to do my controller projects for Halloween... BTW which C++ version are they using? And if memory serves, didn't C++11 add treads, and if so, could you make your Arduino sketch multithreaded? C++14 you can have functors and lambda functions... Anyway, YT recommended your video and it was fun to drop by and see what you were coding.
Would you elaborate on your ideas of "use inheritance for different snowflakes" , "concrete factory pattern" and etc?
And it's advantages. Thanks Bro! in advance.
@@nova0302 Sure, I appreciate the question... the point I'd like before jumping in is that there are many ways to solve a problem... this is another example of a solution:
The thing that stood out in my mind was a switch statement in the code, this means that there are variations or "specializations" of snowflakes. Just like the classic OO example of base class "fruit" and specializations of "apple", "banana", "pear", etc. you could model snowflakes with a base class and inherit "big flake", "little flake", etc. The Base class is the common snowflake that has the function "Update()" but now it is virtual and you let the subclasses or child classes implement that... the child classes have the pixel images and other properties that make it special... and that's the idea of inheritance, is that the child classes are the specializations of the base class.
The next part was design patterns of Concrete Factory... factories are creational patterns, we want to "make" something and we have all these child classes, how to organize them into a single "make me an 'X' type snowflake"... And with this, there is a caveat... There are two patterns named factory, "abstract" and "concrete" and a lot of people find the naming confusing and it happens to me too. I refer you to Stack overflow has an excellent discussion on this topic... but if memory serves me correctly, the one to use in this case is "concrete factory" because you are making a "concrete" class like "Big snowflake" which is an actual class, but here's the idea: That in the main loop you ask the factory to make you a particular snowflake and it constructs it for you, this snowflake object can be put in a list, array, etc. and as before you are calling Update() but now it's virtual member function in the base class that is actually calling the specific Update() for that snowflake and snowflake type.
The advantages: Largely in the coding of snowflake code, each child class (inherited from base class snowflake), is now singularly devoted to it's specialization without the complexity of switch cases and logic to do the specialization. I need to add that the snowflake code example is small and simple, so the advantages are not as apparent; however, it is a standard in OO design and in pays off in more complex problems where all that specialization would just tangle up the code and make the logic more difficult to write and maintain.
Furthermore, if you want separation of objects that draw images from the hardware that the objects are being drawn on... e.g. the LCD or OLED screen, checkout the "Bridge Pattern" it's a way to decouple these two things... each can specialize independently and the "bridge" is the communications between them through an API. A bridge pattern is an example of structural patterns, where clearly this manages independent class hierarchies, and it solves (prevents) a problem called class explosion which is known as an Anti-pattern... (a whole other rabbit hole to chase down).
I hope that helped answer your question... I realize that this is more information than you asked for, but I had to start somewhere and someone took the time to explain it to me... so I'm paying it forward. Cheers!
Functors and lambdas are from functional programming, not OOP. C++ tries to be everything can't do anything very well, except drive people nuts.
@@kayakMike1000 Got a point there... C++ wants to be the "Swiss Army Knife" of languages. However, I have found them to be interesting as a mix-in these notions of capture and such are new to me. But if you've ever wanted to do a pointer to a member function of a particular instance, it makes it easy-peasy lemon squeezy as you just capture that instance. I know that's a rare situation, I had one case where I needed rather wanted to do it, this was before C++14 (or whenever they added those) but I tried it and it's cool the language supported me doing it! But, granted, You have a point. I wonder what they did to C++ in C++23... Dare I look?
I believe that inheritance in this case is an excess of form over substance. I am from a school where the qualities were not worthy of an offspring of their own. Classic example: Cat and dog, but not dachshund and Alsatian. (Of course, we are talking about a certain level of generalization.) REMEMBER: excessive fragmentation is not good.
Hi, I have an idea for another video: How to create a code where it will be easy to replace parts, and it itself has loose connections between itself (simple enable or disable module).
Take, for example, a thermometer. The sensor can be NTC, sensor, thermocouple... Showing can be on the display, Serial, several LEDs, or fan control. Or maybe several of them at once.
I couldn't find anywhere how to operate (such pseudo-)events in arduino. (many events)
That is an interesting topic. OO is nice and clean but i got some unexpected error stuff i ran into. Like constructors with parameters that don't give an error but the thing won't run either, created a public init function for that. Anyways I wreck my way trough it. Thanks for the upload .
Is your object global? If so, be careful that its constructor doesn't involve anything that first has to be initialized in setup(). Remember that this object gets created BEFORE you hit setup(). This is why so many Arduino library objects require an init(). You define a global instance, it allocates static memory but only runs actual functions when called by init().
@@jse-shack825 Thanks. I have to take a look at the sequence of things and how the memory allocation of objects is working in a test setup. I did not call any obj.functions in setup() just assigned constructor params to private member vars in the constructor when declaring the object before setup().
Oh the infamous common and utils folders.
Nice job
Instant sub.
you should consider teaching
WHY? OOP doesn't guarantee readability. You might break things into objects differently than someone else, depending on what you think is important at the time.
It makes it readable to yourself. If somebody else has to read your code then chances are high that you do coding for a living, which then means that you had a specific education or company guideline on how to structure OOP. This video is only there to educate hobbyists and beginners on how to move away from spaghetti code in main.cpp.
@@jse-shack825 lol. In my case, it helps to divide the code into independent modules, so that they cover the smallest possible scope and keep connections between them very weak. There is nothing to hide - the more lines of code, the worse.
What resources would you recommend for learning how to do this?
Maybe a bit controversial:
1. rules of code formatting.
2. read up on design patterns
3. rewrite your code / refactor it - especially when something in it annoys you