Cpp - parent-children classes - best practice

Working on a project with a uLCD-display from 4DSystems.

I'm wondering about the use of classes with the parent-children behavior.

Let assume that:

  • My project has 10 different Windows, so called Forms
  • Every Form can hold a number of Objects
  • The objects could be: buttons, images, labels etc

Each object have properties like:

  • position (x,y)
  • Size (dx, dY)
  • Color etc
  • events (what shall happen if this obj is pressed etc)
    ....

Any suggestions in how to get started with the Parent-child class implementation?

I kind of know Cpp by theory, but this is the first time i use it in practice, so i will need some guidelines for start, so i know if i'm on the right track.

Is classes the right way to go in Arudino Mega? Or is it way too RAM-consuming? What other options do i have?

All feedback is appreciated!

Its not unreasonable - don't go overboard and make everything a class though,
most properties you have are ints or strings or code (event handling can all
be in methods for that particular class - make the event handling generic and
data-driven if you can).

I'd suggest having a base class for forms that has a set of subforms, position/size
and basic virtual event/display methods.

Then subclass it for particular widgets (you could have a widget subclass which
lacks the list/array of subforms I suppose - these are the leaves of the UI tree).

Look at how other micro UI libraries work, there will be some inspiration to
be had I suspect.

I'm thinking...

class myForm{
public:
...
void makeChild(&child);
private:

int _formNr;
String _formID="";
myObject _arrayOfChildren;
//Dynamic Array containing unknown nr of childrens
//... i:e {buttonObject3, buttonObject2, buttonObject6, label5 etc I'm unsure about this declaration.

};

class myObject //children
{
public:
void init();
void onButtonPressed();
void onButtonRelease();
private:
int _coord_X;
int _coord_Y;
int _width;
int _height;
?? _event; //a bit unsure of how to handle this one atm..

};
My idea is that every parent will hold an array with pointers to different children.
Is that a good solution?

I'm unsure of the implementation of the childrenArray. Please advice me...

I'm thinking...

class myForm{

Don't. You use concrete names for classes, like Form, not instance-type, wishy washy names like myForm.

You probably want the array to be a collection, and I suggest it would be worth firming up the abstract concepts of something that can be drawn and something that it can be drawn within. Can it be drawn in multiple places in a form, or in multiple forms? Is the relationship between the form and the objects it displays managed by the form, or the objects, or something else? For the event handling it would IMO be sensible to define the base Drawable class (or whatever other name you come up with) so that it can be notified of an event and define a hierarchy of event classes that they can be notified about. I suspect that to avoid circular class dependencies you would end up wanting to separate out the event notification interface from the interface used to make something be rendered.

PaulS:

I'm thinking...

class myForm{

Don't. You use concrete names for classes, like Form, not instance-type, wishy washy names like myForm.

Ok, sorry about that!:slight_smile:

PeterH:
You probably want the array to be a collection, and I suggest it would be worth firming up the abstract concepts of something that can be drawn and something that it can be drawn within. Can it be drawn in multiple places in a form, or in multiple forms? Is the relationship between the form and the objects it displays managed by the form, or the objects, or something else? For the event handling it would IMO be sensible to define the base Drawable class (or whatever other name you come up with) so that it can be notified of an event and define a hierarchy of event classes that they can be notified about. I suspect that to avoid circular class dependencies you would end up wanting to separate out the event notification interface from the interface used to make something be rendered.

Thanx. for the answer...

So you mean i should go with the arrays instead of classes (association classes?)??

I'm not sure i understand your thought exactly though. Could you provide a simple code-example? It's easier to follow

The relationship is that one object can be om multiple forms and multiple times on each form. The Form is the top level and is filled with objects.
(again, not sure i understand the question: "is the relationship between the form and the objects it displays managed by the form, or the objects, or something else? ")

Do you mean somehing like this?
Form[1] = {*button0, *button1, *label1, *label5, *image4}???

A base class is used to hold all of the properties and methods that are common to all classes. For example, suppose you have real estate investments in residential, commercial, and vacation properties. All of those properties share certain things in common: a mortgage, property taxes, insurance expenses, address, repairs, etc. They also have methods in common: PayMortgage(), PayPropertyTaxes(), PayInsurance(), etc. However, some things are unique to each property type. Commercial property has to have so many parking places per 100 square feet, so many restrooms per square foot, handicap access, etc. Resident homes are concerned with bedrooms and bathrooms, while vacation properties may track lockable storage space, feet of shoreline, boat bock, etc. Therefore, clsBuilding becomes the common denominator to hold all those things the three property types share in common, but each subclass, clsResidential, clsCommerical, and clsVacation, are defined with those properties and methods that make them unique.

You should try to organize your problem with these ideas in mind. Also, not every OOP problem needs inheritance.

A base class is used to hold all of the properties and methods that are common to all classes. For example, suppose you have real estate investments in residential, commercial, and vacation properties. All of those properties share certain things in common: a mortgage, property taxes, insurance expenses, address, repairs, etc. They also have methods in common: PayMortgage(), PayPropertyTaxes(), PayInsurance, etc. However, some things are unique to each property type. Commercial property has to have so many parking places per 100 square feet, so many restrooms per square foot, handicap access, etc. Resident homes are concerned with bedrooms and bathrooms, which vacation properties may track lockable storage space, feet of shoreline, boat bock, etc. Therefore, clsBuilding becomes the common denominator to hold all those things the three property types share in common, but each subclass, clsResidential, clsCommerical, and clsVacation, are defined with those properties and methods that make them unique.

You should try to organize your problem with these ideas in mind. Also, not every OOP problem needs inheritance.

Why start a new thread? I merged them.

class myForm{
...
class myObject //children

Children isn't the word here, is it? If you have children they are humans, not pigeons. So if myForm has children they are myForm, aren't they?

Maybe container is the word you want. A form contains objects.

You may want to look into the STL. That allows you to have collections of dynamically sized things.

hbx2013:
I'm thinking...

class myForm{
public:
...
void makeChild(&child);
private:

EWWW!

You should avoid using 'private'. Use 'protected' instead. 'private' should never have been invented, In My Bombastic Opinion. Why you ask? Because if you derive another class from that one, you won't have access to any of the 'private' members. However, you WILL have access to the PROTECTED members. Trust me, protected works SO much better and gives you the kinds of flexibility you want to have when abstracting or deriving classes. And I've already suggested to "the powers that be" that ALL! ARDUINO! LIBRARIES! use 'protected' instead of 'private'. It makes 'class MyHardwareSerial : public HardwareSerial' much more reasonable than cloning the files and creating an entirely new library [yes I had to do this to fix stuff, make it more efficient].

And anyway, it's YOUR code, so what's the whole 'private' thing good for anyway? If you want to protect data members from EVER being accessed outside of the class, abstraction is the way to go, but Arduino is a bit too small to put that kind of thing into place anyway so basically, 'protected' will do what you want. It's like a reminder NOT to mess with it unless you're "the class" (or a derived class). And that's good enough, right?

Thanx Mr Gammon...
I'm not sure what is the best approach here.
Just to be sure, and before i start to explore new territories;
with STL, do you happen to mean this library?

It sounds good in theory, containers that can hold dynamically sized objects is what i need.

econjack: Thanx.. But that's pretty much all i know about classes already, since i know it in theory (a bit), but have never worked with in in real life.

bombasticbob: You may be right. I'm new to practical object oriented programming , so i can't argue/disargue with that. It sounds good though and i'll try to keep that i mind :slight_smile:

In this thread, i'm trying to figure out what is the best approach, before digging into details and syntax! :slight_smile:

with STL, do you happen to mean this library?

That's right. That's an implementation of the Standard Template Library for the Arduino. With that you can have containers of any type (within reason). Containers can be vectors, lists, maps, queues etc.

The container type depends on the frequency and speed that you wish to add, remove or access individual items.

The library has a bit of an overhead, but then, so does a lot of stuff. The sprintf function springs to mind as does the String class.

Great, i also found a YT tutorial on this: C++ Tutorial #13, STL Vectors (1) - YouTube

But i may have forgotten to mention that i (in this case), only have to deal with static objects.
In other words, there're no forms or objects added or removed during run-time. Everything could be hard-coded.

Does that little fact changes your suggestion? Or is STL the way to go anyway?

One simple and ugly solution would be if/switch statements, something like this:

if(activeForm == 2 && formChangeRequest){
showButton(100, 100, ....); //Parameters = X, Y, Width, height, etc
showLabel(....);
etc etc
}

But i wanna learn new stuff in the process, so that's why i started this thread to figure our my alternatives.

hbx2013:
void makeChild(&child);

What do you consider a "child"?
Also, if you're creating a "child", return it... "MyObject & createChild()".

String _formID="";

Despite popular believe, object orientation itself does not add much memory usage/code size to a project.
However, using the "String" class, especially what seems to me like for no particular reason, does inflate code artificially. You don't need a formID at all as far as I can tell. And if you do, use a "const char*" instead.

myObject _arrayOfChildren;

Use a simple linked list instead.

void onButtonPressed();
void onButtonRelease();

Assuming not all "children" are buttons, you might want to consider a more generic base class like "FormElement" and then a more specialized class like "class Button : public FormElement".

Then use something like this "myForm.addElement(new Button());"

int _coord_X;
int _coord_Y;
int _width;
int _height;

Don't use a leading "_" for member variables.
Also, assuming that X/Y/W/H can't be negative and don't need 2 bytes each, just use "byte" or "uint8_t" instead.

My idea is that every parent will hold an array with pointers to different children.
Is that a good solution?

Asides from not using an array, sure, why not.

I'm unsure of the implementation of the childrenArray. Please advice me...

If you want to see some seriously clean object oriented code for inspiration, check out the Cosa source code:
http://forum.arduino.cc//index.php?topic=150299.0

hbx2013:
But i may have forgotten to mention that i (in this case), only have to deal with static objects.
In other words, there're no forms or objects added or removed during run-time. Everything could be hard-coded.

...

It sounds good in theory, containers that can hold dynamically sized objects is what i need.

If nothing changes at runtime, then a simple static array could be all you need. I did one a few hours ago, which I still had open:

typedef struct
  {
  const char * name;
  unsigned long value;
  }  tConfiguration;
  
tConfiguration configuration [] = 
  {
  { "TANK_FULL", 42 },
  { "TANK_EMPTY", 21 },
  
  // more here
  
  };
  
void setup () { }
void loop () { }

In this case the compiler will allocate enough memory for this array at compile-time. You may need to put the whole thing into program memory to save RAM.

On a small processor, best practice probably embraces design decisions that take into account the processor's limitations (like, limited memory) so you need to take that into consideration, and it may count against a really elegant solution.

Plus, what int2str said, which I noticed after I started typing. You don't necessarily need a linked list, an array may work, but then a linked list lets you add and remove things more easily.

Try to lean towards code that is easy to write, easy to understand, and easy to use.