Go Down

Topic: Class inside Class (Read 284 times) previous topic - next topic

giova014

Aug 09, 2015, 09:03 pm Last Edit: Aug 09, 2015, 09:04 pm by giova014
Hi everyone!

I'm making a string List library, to use in other libraries.
My list have the items and then, sub items. The sub items are divided by the DEMILITER_CHAR.
What I'm trying to do is append a sub item to a specific item index. So I decided to create a class inside my stringList class to handle the sub items functions, but I don't know how to do so, to acess it like this (in the .ino file):

Code: [Select]
stringList.item(int Index).append(char* SubItem)

The sub items class could be named class item

My files (without sub item management):

.h
Code: [Select]
# ifndef stringList_h
# define stringList_h

#include <Arduino.h>
#include <string.h>
//------------------------------------------------------------------------

#define makeList(Array) ((char*)Array)
#define element(Index) (&array[(Index)*itemsMaxLength])

#define DELIMITER_CHAR ';'

class stringList
{
public:
stringList() {}
void setup(char* Items, int MaxItems, int ItemsMaxLength);
void assign(char* Items);
void assign(char* Items, int MaxItems, int ItemsMaxLength);

int    size();
void clear();
void sort();
void sortReverse();
void reverse();
void shuffle();
void lower();
void upper();

int append(char* NewItem);
bool modify(int Index, char* NewItem);
inline bool modify(char* Item, char* NewItem, int From=0) { return modify(getIndex(Item,From),NewItem); }
bool remove(int Index);
inline bool remove(char* Item, int From=0) { return remove(getIndex(Item,From)); }

inline char* getItem(int Index) { return (Index>=0 && Index<nItems ? element(Index) : NULL); }
bool getItem(int Index, char* Buffer, int Length);
int getIndex(char* Item, int From=0);


private:
char* array;
int nItems, itemsMaxLength, maxItems;

};

#endif


.cpp
Code: [Select]
#include "stringList.h"

//--------------------------------------------------------------------------------

void stringList::setup(char *Items, int MaxItems, int ItemsMaxLength){
assign(Items,MaxItems,ItemsMaxLength);
}

//--------------------------------------------------------------------------------

void stringList::assign(char* Items){

if(Items!=NULL){
array = Items;

nItems = 0;
for(int n=maxItems-1; n>=0; n--){
if(strlen(element(n))>0){
if(n+1>nItems)
nItems = n+1;
break;
}
}
}
}

//--------------------------------------------------------------------------------

void stringList::assign(char* Items, int MaxItems, int ItemsMaxLength){

itemsMaxLength = ItemsMaxLength;
maxItems = MaxItems;

assign(Items);
}

//--------------------------------------------------------------------------------

void stringList::clear(){

memset(array,0,maxItems*itemsMaxLength);
nItems = 0;
}

//--------------------------------------------------------------------------------

void stringList::sort(){
char buffer[itemsMaxLength];
memset(buffer,0,sizeof(buffer));

for(int n=0;n<nItems-1;n++){
for(int m=n+1;m<nItems;m++){
if(strcmp(element(m),element(n))<0){
strcpy(buffer,element(n));
strcpy(element(n),element(m));
strcpy(element(m),buffer);
}
}
}
}

//--------------------------------------------------------------------------------

void stringList::sortReverse(){
char buffer[itemsMaxLength];
memset(buffer,0,sizeof(buffer));

for(int n=0;n<nItems-1;n++){
for(int m=n+1;m<nItems;m++){
if(strcmp(element(m),element(n))>0){
strcpy(buffer,element(n));
strcpy(element(n),element(m));
strcpy(element(m),buffer);
}
}
}
}

//--------------------------------------------------------------------------------

void stringList::reverse(){
char buffer[itemsMaxLength];
memset(buffer,0,sizeof(buffer));

for(int n=0;n<nItems;n++){
int m = (nItems-1-n);
if(n>=m)
break;
strcpy(buffer,element(n));
strcpy(element(n),element(m));
strcpy(element(m),buffer);
}
}

//--------------------------------------------------------------------------------

void stringList::shuffle(){
char buffer[itemsMaxLength];
memset(buffer,0,sizeof(buffer));

int m;
randomSeed(analogRead(0));
for(int n=0;n<nItems;n++){
m = random(n,nItems);
strcpy(buffer,element(n));
strcpy(element(n),element(m));
strcpy(element(m),buffer);
}
}

//--------------------------------------------------------------------------------

void stringList::lower(){
for(int n=0;n<nItems;n++)
strlwr(element(n));
}

//--------------------------------------------------------------------------------

void stringList::upper(){
for(int n=0;n<nItems;n++)
strupr(element(n));
}

//--------------------------------------------------------------------------------

int stringList::size(){
return nItems;
}

//--------------------------------------------------------------------------------

int stringList::append(char* NewItem){

if(nItems >= maxItems || strlen(NewItem)>=itemsMaxLength)
return -1;

strcpy(element(nItems-1),NewItem);
return nItems-1;
}

//--------------------------------------------------------------------------------

bool stringList::modify(int Index, char* NewItem){

if(Index<0 || Index>=maxItems || strlen(NewItem)>=itemsMaxLength)
return false;

strcpy(element(Index),NewItem);

if(Index+1>nItems)
nItems = Index+1;

return true;
}

//--------------------------------------------------------------------------------

bool stringList::remove(int Index){

if(Index<0 || Index>=nItems || nItems<=0)
return false;

for(int n=Index;n<nItems;n++){
strcpy(element(n),element(n+1));
}
memset(element(nItems-1),0,itemsMaxLength);

if(Index<nItems)
nItems--;

return true;

}

//--------------------------------------------------------------------------------

bool stringList::getItem(int Index, char* Buffer, int Length){
if(Index<0 || Index>=maxItems)
return false;

strncpy(Buffer,element(Index),Length);
return true;
}

//--------------------------------------------------------------------------------

int stringList::getIndex(char* Item, int From){

if(From<0)
From = 0;

if(strlen(Item)>=itemsMaxLength || From<0 || From>=maxItems)
return -1;

for(int n=From;n<nItems;n++)
if(!strcmp(element(n),Item))
return n;

return -1;
}


.ino (simple test)
Code: [Select]
#include <stringList.h>

stringList myList;

char myListArray[10][15] = {{"FirstItem"},{"SecondItem"},{"ThirdItem"}};

void setup() {
  Serial.begin(115200);
  Serial.println(" -- BEGIN -- ");

  myList.setup(makeList(myListArray),10,15);
  myList.append("FourthItem");
  myList.modify(3,"AnotherItem");
  myList.remove(2);
  Serial.println(myList.getItem(2));
}

void loop() {
  /* NOTHING */
}


Is there a way to do this?
Arduino!!

michinyon

You can define a class inside another class.

There is plenty of explanations of the rules for doing this, online.

giova014

Thanks for your reply!

So, I starting with the declaration, I declared a class inside the main class like this:

Code: [Select]


class stringList
{
public:
class item {
public:
item(int Index) { _item = Index; }
void append(char* NewItem) { strcat(element(_item),NewItem); }
int _item;
};

};



Using it in the .ino file like this:

Code: [Select]

myList.item(2).append("Number");


I get these errors:

Code: [Select]


In file included from stringList.ino:1:0:
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h: In member function 'void stringList::item::append(char*)':
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h:50:10: error: invalid use of non-static data member 'stringList::array'
   char*  array;
          ^
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h:9:26: error: from this location
 #define element(Index) (&array[(Index)*itemsMaxLength])
                          ^
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h:45:41: note: in expansion of macro 'element'
     void append(char* NewItem) { strcat(element(_item),NewItem); }
                                         ^
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h:51:17: error: invalid use of non-static data member 'stringList::itemsMaxLength'
   int   nItems, itemsMaxLength, maxItems;
                 ^
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h:9:40: error: from this location
 #define element(Index) (&array[(Index)*itemsMaxLength])
                                        ^
C:\Users\Giovanni\Documents\Arduino\libraries\stringList/stringList.h:45:41: note: in expansion of macro 'element'
     void append(char* NewItem) { strcat(element(_item),NewItem); }
                                         ^
stringList.ino: In function 'void setup()':
stringList:15: error: invalid use of 'class stringList::item'
invalid use of 'class stringList::item'



I searched a lot, but still can't find a solution. What's the type of this: Nested, Inner or Child class?
Arduino!!

PaulS

#3
Aug 10, 2015, 07:53 am Last Edit: Aug 10, 2015, 07:53 am by PaulS
Code: [Select]
#define makeList(Array) ((char*)Array)
#define element(Index) (&array[(Index)*itemsMaxLength])

Get rid of this CRAP!

Define functions that have types and belong to the appropriate classes.

giova014

Thanks for your reply!

Here is my .h file with aproppriate functions:

Code: [Select]
# ifndef stringList_h
# define stringList_h

#include <Arduino.h>
#include <string.h>
//------------------------------------------------------------------------

#define DELIMITER_CHAR ';'

template < int X, int Y >
inline char* makeList(char (&Array)[X][Y]) { return (char*)Array; }

class stringList
{
public:
inline char* element(int Index) { return &array[(Index)*itemsMaxLength]; }

stringList() {}
void setup(char* Items, int MaxItems, int ItemsMaxLength);
void assign(char* Items);
void assign(char* Items, int MaxItems, int ItemsMaxLength);

int    size();
void clear();
void sort();
void sortReverse();
void reverse();
void shuffle();
void lower();
void upper();

int append(char* NewItem);
bool modify(int Index, char* NewItem);
inline bool modify(char* Item, char* NewItem, int From=0) { return modify(getIndex(Item,From),NewItem); }
bool remove(int Index);
inline bool remove(char* Item, int From=0) { return remove(getIndex(Item,From)); }

inline char* getItem(int Index) { return (Index>=0 && Index<nItems ? element(Index) : NULL); }
bool getItem(int Index, char* Buffer, int Length);
int getIndex(char* Item, int From=0);

private:
char* array;
int nItems, itemsMaxLength, maxItems;

};

#endif


How can I declare a class inside the stringList and use this new class a as member (just like a function)?
Arduino!!

PaulS

Code: [Select]
template < int X, int Y >
inline char* makeList(char (&Array)[X][Y]) { return (char*)Array; }

Wrong. When this function ends, the array that you have returned a pointer to goes away. To make a persistent array, you need to use new() or malloc().

Quote
How can I declare a class inside the stringList and use this new class a as member (just like a function)?
Given that a class is a collection of data and functions, please explain this statement. You could use an instance of the class as a member, and that instance will have data and methods, but using a class as a member doesn't make sense.

giova014

#6
Aug 10, 2015, 02:49 pm Last Edit: Aug 10, 2015, 03:10 pm by giova014
Trying to explain (the function actually works for me):
Code: [Select]
template < int X, int Y >
inline char* makeList(char (&Array)[X][Y]) { return (char*)Array; }


The (&Array) gets the address of Array and (char*)Array return a pointer to the Address of Array, so even after the function call, the pointer will still return the correct pointer.

For a class as a member example, let's say I have something like this:

Code: [Select]

MainClass{
  MainClass(){}
  int append(char* NewItem);
 
  SubClass{
    SubClass(){}
    int append(char* NewItem);
  };
};


The SubClass is a class that process the individual members in the MainClass (in the stringList example).

Is there a way to call functions like this:
Code: [Select]
MainClass.add("Item"); //Append a item to the list
MainClass.SubClass(0).add("SuItem"); //Append a SubItem to the Item of Index 0 of the list
Arduino!!

PaulS

Quote
The (&Array) gets the address of Array and (char*)Array return a pointer to the Address of Array, so even after the function call, the pointer will still return the correct pointer.
But, that pointer points to memory that no longer is assigned to Array. That memory is available to be reused at any time.

Quote
Is there a way to call functions like this:
Lets take a look at that last statement, first:
Code: [Select]
MainClass.SubClass(0).add("SuItem");
SubClass() calls the constructor, with one argument. You don't have a one argument constructor.

SubClass doesn't have an add() method.

MainClass does not have a method called SubClass().

So, no, you can't do that.

Now, some code that actually compiles?

giova014

#8
Aug 10, 2015, 03:48 pm Last Edit: Aug 10, 2015, 03:48 pm by giova014
Thanks for your reply!

Quote
SubClass doesn't have an add() method.
It's not add(), it's append(), sorry for the mistake!

With your explanation I made this code:

.ino
Code: [Select]
class MainClass{
  public:
    MainClass(){}
    int append(char* NewItem) {Serial.println("New Item append");}
   
    class SubClass{
      public:
        SubClass(int Index){_item = Index;}
        int append(char* NewItem) {Serial.println("New Sub Item append");}
      private:
        int _item;
    };
 
    SubClass item(int Index) {}
};

MainClass class1;

void setup(){
  Serial.begin(115200);
  class1.item(0).append("Item");
 
}

void loop(){}


In the Serial Monitor I get
Code: [Select]
New Sub Item append

It's working!
I guess I can continue from now on, implementing this model into the stringList library.

Thanks for your help!


Arduino!!

giova014

#9
Aug 10, 2015, 03:58 pm Last Edit: Aug 10, 2015, 03:59 pm by giova014
Regarding this:

But, that pointer points to memory that no longer is assigned to Array. That memory is available to be reused at any time.
With this test code:

Code: [Select]
template < int X, int Y >
inline char* makeList(char (&Array)[X][Y]) { return (char*)Array; }

char myArray[3][6] = {{"Test1"},{"Test2"},{"Test3"}};

void printArray(char* ArrayToPrint, int Rows, int Length){
  for(int j = 0; j<Rows; j++){
    Serial.println(&ArrayToPrint[(j)*Length]);
  }
}

void setup(){
  Serial.begin(115200);
  printArray(makeList(myArray),3,6);
 
}

void loop(){}


The ArrayToPrint becames a pointer to myArray.

This code gives me the output:

Code: [Select]

Test1
Test2
Test3


I didn't use template directly into printArray() because it's actually in a class (see the setup() in stringList library -  first post).

Is there anything wrong?
Arduino!!

PaulS

Quote
Is there anything wrong?
Your template function is doing what, exactly? It isn't allocating any memory.

There is an array of size 3 * 6. You pass the address of the array to the function, but for what purpose?

If the purpose WAS to allocate memory, that memory would be available for reuse as soon as the function ended. That the memory is NOT immediately overwritten doesn't mean anything.


giova014

You said do get rid of this:

Code: [Select]
#define makeList(Array) ((char*)Array)
Where I get a pointer to an array of any size.

The only way I could do it with a proper function was:
Code: [Select]
template < int X, int Y >
inline char* makeList(char (&Array)[X][Y]) { return (char*)Array; }

In this, the template is possibiliting to get a pointer of an array of any size.
Arduino!!

PaulS

Quote
You said do get rid of this:
#define makeList(Array) ((char*)Array)
Where I get a pointer to an array of any size.
That is not doing anything. The preprocessor will simply replace the makeList string with a cast.

Quote
The only way I could do it with a proper function was:
But, what is "it" that you think you are doing? What, exactly, do you think that the function is doing?

giova014

For my library handle a size array, which can be of any size, I pass to the setup() function the pointer to the array and then the sizes(Rows,Columns,Length).

To get this pointer of a bidimensional array, I created the makeList() that is no more than a "good looking" cast to (char* = pointer?) as you saw in the macro definition.

But I can use a function, which is type safe.

The "it" is to get a pointer of char array.

Arduino!!

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy