Class inside Class

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):

stringList.item(int Index).append(char* SubItem)

The sub items class could be named class item

My files (without sub item management):

.h

# 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

#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)

#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?

You can define a class inside another class.

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

Thanks for your reply!

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

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:

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

I get these errors:

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?

#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.

Thanks for your reply!

Here is my .h file with aproppriate functions:

# 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)?

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().

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.

Trying to explain (the function actually works for me):

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:

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:

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

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.

Is there a way to call functions like this:

Lets take a look at that last statement, first:

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?

Thanks for your reply!

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

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

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!

Regarding this:

PaulS:
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:

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:

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?

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.

You said do get rid of this:

#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:

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.

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.

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?

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.