struct within struct and pointers

Hi,

I have defined a few struct's like this -

struct M_option {
  int8_t POS;
  int8_t MNU;
  int8_t OPT;
  int8_t OPTOPT;
  boolean SCRu;
  boolean SCRd;
  int8_t SCRMNU;
  int8_t SCROPT;
  int8_t FNC;
  int8_t FNCOPT;
};

struct M_static {
  int8_t POS;
  int8_t OPT;
};

struct M_dynamic {
  int8_t POS;
  int8_t OPT;
};

struct menu {
  char * Static_Text;
  int8_t MaxOPT;
  int8_t MaxSVars;
  int8_t MaxDVars;
  M_option * OPT;
  M_static * SVar;
  M_dynamic * DVar;
};

and I want to be able to dynamically define how many M_option's as OPT, how many M_static's as SVar and how many M_dynamic's in DVar when the user selects the menu that they want to navigate to.

I've tried this without error -

menu C_MNU;
//options
C_MNU.OPT[0] = (M_option){ 16,2,1,0,0,0,0,0,0,0 };
C_MNU.OPT[1] = (M_option){ 16,3,1,0,0,0,0,0,0,0 };
//static vars
C_MNU.SVar[0] = (M_static){ 1,1 };
C_MNU.SVar[1] = (M_static){ 2,2 };
//dynamic vars
C_MNU.DVar[0] = (M_dynamic){ 25,2 };
C_MNU.DVar[1] = (M_dynamic){ 35,5 };
C_MNU.DVar[2] = (M_dynamic){ 45,3 };

However I would like to be able to define all the pointers in one line for each struct type, i.e. -

C_MNU.OPT = { (M_option){ 16,2,1,0,0,0,0,0,0,0 },(M_option){ 16,3,1,0,0,0,0,0,0,0 } };

Which is clearly the wrong syntax as this will not compile. I tried using the new command to pre-set the size of the pointer using - C_MNU.OPT = new M_option[2]; but this does not compile either.

Any suggestions as to how I can condense the allocation of the settings with the most minimal amount of code and memory usage?

Also, when the user navigates to a new menu page I want the existing menu values and array sizes to clear and be redefined each time.

I have had to use a 2560 Mega board to cope with the 20+ menus I have defined. Would be nice to get the code to work with the 2K of memory that the 328 boards support.

Thanks,

John

struct M_option
{
    int8_t  POS;
    int8_t  MNU;
    int8_t  OPT;
    int8_t  OPTOPT;
    boolean SCRu;
    boolean SCRd;
    int8_t  SCRMNU;
    int8_t  SCROPT;
    int8_t  FNC;
    int8_t  FNCOPT;
};

struct M_static
{
    int8_t POS;
    int8_t OPT;
};

struct M_dynamic
{
    int8_t POS;
    int8_t OPT;
};

struct menu
{
    const char*     Static_Text;
    int8_t          MaxOPT;
    int8_t          MaxSVars;
    int8_t          MaxDVars;
    M_option*       OPT;
    M_static*       SVar;
    M_dynamic*      DVar;
};

M_option    m_opttion1  = { 16, 2, 1, 0, 0, 0, 0, 0, 0, 0 };
M_option    m_opttion2  = { 16, 3, 1, 0, 0, 0, 0, 0, 0, 0 };

M_static    m_static1   = { 1, 1 };
M_static    m_static2   = { 2, 2 };

M_dynamic   m_dynamic1  = { 25, 2 };
M_dynamic   m_dynamic2  = { 35, 5 };


menu        menus[]     =
{
      { "static text line 1", 0, 0, 0, &m_opttion1, &m_static1, &m_dynamic1 }
    , { "static text line 2", 0, 0, 0, &m_opttion2, &m_static2, &m_dynamic2 }
};


void loop()
{}

void setup()
{}

That would be fine if I wanted an array of menu's, however how would I put m_opttion1 and m_opttion2 into the one menu.OPT array using one line in code? Your code takes m_opttion1 and puts it in the first menu and then puts m_opttion2 in the second menu. At any one time I just want the currently active menu (no array of menus as this will chew up even more memory). In that one menu I want to be able to dynamically change how many OPT's, SVar's and DVar's are in the active menu.

Ultimately I would like to be able to just have something like this -

menu active_menu = {all values for this menu};

Probably not possible as OPT, DVar and SVar are pointer arrays.

"... using one line in code ..."

Perhaps you could be a bit more vague in your requirements?

struct M_option
{
    int8_t  POS;
    int8_t  MNU;
    int8_t  OPT;
    int8_t  OPTOPT;
    boolean SCRu;
    boolean SCRd;
    int8_t  SCRMNU;
    int8_t  SCROPT;
    int8_t  FNC;
    int8_t  FNCOPT;
};

struct M_static
{
    int8_t POS;
    int8_t OPT;
};

struct M_dynamic
{
    int8_t POS;
    int8_t OPT;
};

struct menu
{
    const char*     Static_Text;
    int8_t          MaxOPT;
    int8_t          MaxSVars;
    int8_t          MaxDVars;
    M_option*       OPT;
    M_static*       SVar;
    M_dynamic*      DVar;
};

M_option    m_options[2] =
{
      { 16, 2, 1, 0, 0, 0, 0, 0, 0, 0 }
    , { 16, 3, 1, 0, 0, 0, 0, 0, 0, 0 }
};

M_static    m_statics[2] =
{
      { 1, 1 }
    , { 2, 2 }
};

M_dynamic   m_dynamics[2]  =
{
      { 25, 2 }
    , { 35, 5 }
};



menu        menu        =
{
    "static text line", 2, 2, 2, m_options, m_statics, m_dynamics
};


void loop()
{}

void setup()
{}

I must be completely misunderstanding the difference between a memory pointer and an array.

What I am after is a dynamic array of various structs within the menu.

Coming from my VB.NET I can work with arrays like this -

        Dim array1() As String
        ReDim array1(5)
        array1= {"1", "2", "3", "4", "5"}

Can I do the same in this context -

...
struct menu
{
    const char    Static_Text[];
    int8_t          MaxOPT;
    int8_t          MaxSVars;
    int8_t          MaxDVars;
    M_option       OPT[];
    M_static       SVar[];
    M_dynamic      DVar[];
} C_MENU;

How would I then "ReDim" the upper bound of C_MENU.OPT in C++?

lloyddean: "... using one line in code ..."

Perhaps you could be a bit more vague in your requirements?

struct M_option
{
    int8_t  POS;
    int8_t  MNU;
    int8_t  OPT;
    int8_t  OPTOPT;
    boolean SCRu;
    boolean SCRd;
    int8_t  SCRMNU;
    int8_t  SCROPT;
    int8_t  FNC;
    int8_t  FNCOPT;
};

struct M_static {    int8_t POS;    int8_t OPT; };

struct M_dynamic {    int8_t POS;    int8_t OPT; };

struct menu {    const char*     Static_Text;    int8_t          MaxOPT;    int8_t          MaxSVars;    int8_t          MaxDVars;    M_option*       OPT;    M_static*       SVar;    M_dynamic*      DVar; };

M_option    m_options[2] = {      { 16, 2, 1, 0, 0, 0, 0, 0, 0, 0 }    , { 16, 3, 1, 0, 0, 0, 0, 0, 0, 0 } };

M_static    m_statics[2] = {      { 1, 1 }    , { 2, 2 } };

M_dynamic   m_dynamics[2]  = {      { 25, 2 }    , { 35, 5 } };

menu        menu        = { "static text line", 2, 2, 2, m_options, m_statics, m_dynamics };

void loop() {}

void setup() {}

Perhaps you could be a bit more sarcastic in your responses? Seriously? I'm really new to c++ having spent the last 25 years programming in vb. Please cut me some slack?

My requirement is to be able to define a struct within a struct and the embeded structs need to be an array of that struct type that can vary in size. I have shown the example of the stuct's I have defined as a starting point. I need to achieve this with a minimal memory footprint and a minimal number of lines. I do not want ALL the menus that need to be defined as an array, just a single menu object defined by the struct type that can be modified on the fly so as to reduce the amount of memory required. If I had enough space left in EEPROM on the 328 chip after storing all the other settings I have there I would even suggest that I keep all the menu settings there and retrieve them each time the menu changes.

First your symbol names leave a lot to be desired as they only vaguely suggest there use to me. THis leave me making assumptions. Probably wrong assumptions so ...

I would recommend that 'DVar' contains the address of an array of 'M_dynamic' elements declared at some MAXIMUM size. 'MaxDVars' then contains the current number of entries in the array. And YOU modify the array contents yourself adjusting the position of entries in the array and the count of current items in the array yourself.

Thanks lloyddean. That is practically what I ended up doing anyhow before I signed up to this forum. The working code is this -

//Menus
struct M_option {
  int8_t   POS;  //LCD cursor position of marker for option selection
  int8_t   MNU; //Menu number go to when selected
  int8_t   OPT;  //Option number on the target menu to go to when selected
  int8_t   OPTOPT;  //parameter for option
  boolean  SCRu; //scroll up to other menu enabled
  boolean  SCRd; //scroll down to other menu enabled
  int8_t   SCRMNU;  //menu to scroll to
  int8_t   SCROPT;  //menu option on target menu to scroll to
  int8_t   FNC; //function to action when MNU = 0
  int8_t   FNCOPT;  //function parameter value
};

struct M_static {
  int8_t  POS;  //LCD cursor position for static variable
  int8_t  OPT;  //variable to use (switch case to get desired variable)
};

struct M_dynamic {
  int8_t  POS; //LCD cursor position for dynamic variable
  int8_t  OPT; //variable to use (switch case to get desired variable)
};

struct menu {
  char       Static_Text[81];  //LCD03 4x20 static text for menu
  int8_t     MaxOPT;  //Max selectable options
  int8_t     MaxSVars;  //Max static vars
  int8_t     MaxDVars;  //Max dynamic vars
  M_option   OPT[8];  //Fixed array of option definitions
  M_static   SVar[8];  //Fixed array of static variable definitions
  M_dynamic  DVar[8];  //Fixed array of dynamic variable definitions
} C_MNU;
...
void Set_Menu_Content() {
  //menu defs

  //Initial Menu  
  if (A_MNU==1) {  //Root Menu
    
    strcpy(C_MNU.Static_Text,"                MenuTHR       RUD       AIL       ELE       CH5       CH6");

    C_MNU.MaxOPT=1;
    C_MNU.MaxSVars=1;
    C_MNU.MaxDVars=6;

    //selectable options
    C_MNU.OPT[0] = (M_option){16,2,1,0,0,0,0,0,0,0};

    //static vars
    C_MNU.SVar[0] = (M_static){ 1,1 };

    //dynamic vars
    C_MNU.DVar[0] = (M_dynamic){ 25,2 };
    C_MNU.DVar[1] = (M_dynamic){ 35,5 };
    C_MNU.DVar[2] = (M_dynamic){ 45,3 };
    C_MNU.DVar[3] = (M_dynamic){ 55,4 };
    C_MNU.DVar[4] = (M_dynamic){ 65,6 };
    C_MNU.DVar[5] = (M_dynamic){ 75,7 };

  } else if ...

So back to the main question, is there any way to not have to set fixed array sizes when C_MNU is defined? Rather than using -

  M_option   OPT[8];  //Fixed array of option definitions
  M_static   SVar[8];  //Fixed array of static variable definitions
  M_dynamic  DVar[8];  //Fixed array of dynamic variable definitions

can I define -

  M_option   OPT[];  //Fixed array of option definitions
  M_static   SVar[];  //Fixed array of static variable definitions
  M_dynamic  DVar[];  //Fixed array of dynamic variable definitions

and define the size of the C_MNU.OPT/SVar/DVar arrays when I assign all the options?

Make them pointers and not arrays. Like you have it, they are arrays of zero size.

  M_option *   OPT;  //Fixed array of option definitions
  M_static  * SVar;  //Fixed array of static variable definitions
  M_dynamic * DVar;  //Fixed array of dynamic variable definitions

Personally I would recommend the STL, but that has a bit of an overhead. Maybe you can afford it.

http://www.gammon.com.au/forum/?id=11119

Example of using the STL. I downloaded and installed it from Andy Brown’s site as he describes.

Then:

#include <iterator>
#include <vector>
#include <pnew.cpp>

//Menus
struct M_option {
  int8_t   POS;  //LCD cursor position of marker for option selection
  int8_t   MNU; //Menu number go to when selected
  int8_t   OPT;  //Option number on the target menu to go to when selected
  int8_t   OPTOPT;  //parameter for option
  boolean  SCRu; //scroll up to other menu enabled
  boolean  SCRd; //scroll down to other menu enabled
  int8_t   SCRMNU;  //menu to scroll to
  int8_t   SCROPT;  //menu option on target menu to scroll to
  int8_t   FNC; //function to action when MNU = 0
  int8_t   FNCOPT;  //function parameter value
} ;

struct M_static {
  int8_t  POS;  //LCD cursor position for static variable
  int8_t  OPT;  //variable to use (switch case to get desired variable)
};

struct M_dynamic {
  int8_t  POS; //LCD cursor position for dynamic variable
  int8_t  OPT; //variable to use (switch case to get desired variable)
};

struct menu {
  char       Static_Text[81];  //LCD03 4x20 static text for menu
  int8_t     MaxOPT;  //Max selectable options
  int8_t     MaxSVars;  //Max static vars
  int8_t     MaxDVars;  //Max dynamic vars
  std::vector <M_option> OPT;   // vector of option definitions
  std::vector <M_static> SVar;  // vector of static variable definitions
  std::vector <M_dynamic> DVar; // vector of dynamic variable definitions
} C_MNU;

void setup ()
  {
  Serial.begin (115200);

  M_option foo;
  M_option bar;
  
  C_MNU.OPT.push_back (foo);
  C_MNU.OPT.push_back (bar);
  
  M_static a;
  M_static b;
  
  C_MNU.SVar.push_back (a);
  C_MNU.SVar.push_back (b);
  
  }  // end of setup

void loop ()
  {
    
  }  // end of loop

Note how we are adding multiple menu and variable items with push_back? You can do that indefinitely (until you run out of memory that is).

You might be better off using pointers, that might reduce the amount of copying around of stuff. Example:

#include <iterator>
#include <vector>
#include <pnew.cpp>

//Menus
struct M_option {
  int8_t   POS;  //LCD cursor position of marker for option selection
  int8_t   MNU; //Menu number go to when selected
  int8_t   OPT;  //Option number on the target menu to go to when selected
  int8_t   OPTOPT;  //parameter for option
  boolean  SCRu; //scroll up to other menu enabled
  boolean  SCRd; //scroll down to other menu enabled
  int8_t   SCRMNU;  //menu to scroll to
  int8_t   SCROPT;  //menu option on target menu to scroll to
  int8_t   FNC; //function to action when MNU = 0
  int8_t   FNCOPT;  //function parameter value
} ;

struct M_static {
  int8_t  POS;  //LCD cursor position for static variable
  int8_t  OPT;  //variable to use (switch case to get desired variable)
};

struct M_dynamic {
  int8_t  POS; //LCD cursor position for dynamic variable
  int8_t  OPT; //variable to use (switch case to get desired variable)
};

struct menu {
  char       Static_Text[81];  //LCD03 4x20 static text for menu
  int8_t     MaxOPT;  //Max selectable options
  int8_t     MaxSVars;  //Max static vars
  int8_t     MaxDVars;  //Max dynamic vars
  std::vector <M_option *> OPT;   // vector of option definitions
  std::vector <M_static *> SVar;  // vector of static variable definitions
  std::vector <M_dynamic *> DVar; // vector of dynamic variable definitions
} C_MNU;

void setup ()
  {
  Serial.begin (115200);

  M_option * foo = new M_option;
  M_option * bar = new M_option;
  
  C_MNU.OPT.push_back (foo);
  C_MNU.OPT.push_back (bar);
  
  M_static * a = new M_static;
  M_static * b = new M_static;
  
  C_MNU.SVar.push_back (a);
  C_MNU.SVar.push_back (b);
  
  }  // end of setup

void loop ()
  {
    
  }  // end of loop

Thanks Nick,

the main concern with the three extra includes is if they will occupy more memory than the pre-allocated arrays. I looked at vectors yesterday and came to the conclusion that it was one step forward and three steps back with memory usage.

If I went with the pointers, i.e. -

M_option *   OPT;  
  M_static  * SVar;  
  M_dynamic * DVar;

I would I assign each M_option, M_static and M_dynamic to C_MNU.OPT/SVar/DVar pointers? Is this still valid in this case -

    //selectable options
    C_MNU.OPT[0] = (M_option){16,2,1,0,0,0,0,0,0,0};

    //static vars
    C_MNU.SVar[0] = (M_static){ 1,1 };

    //dynamic vars
    C_MNU.DVar[0] = (M_dynamic){ 25,2 };
    C_MNU.DVar[1] = (M_dynamic){ 35,5 };
    C_MNU.DVar[2] = (M_dynamic){ 45,3 };
    C_MNU.DVar[3] = (M_dynamic){ 55,4 };
    C_MNU.DVar[4] = (M_dynamic){ 65,6 };
    C_MNU.DVar[5] = (M_dynamic){ 75,7 };

Another thing that has gone unanswered is if I can do something like this? -

C_MNU.DVar = { {25,2},{35,5},{45,3},{55,4},{65,6},{75,7} };

I've tried this without any luck compiling it. If this is possible what syntax would I use to allocate all required pointers in one assignment?

You can't assign like that. That syntax is for declarations.

I looked at vectors yesterday and came to the conclusion that it was one step forward and three steps back with memory usage.

Are you talking RAM or program memory? It should be pretty neutral for RAM.

Was talking RAM, however I think I was getting close to using up program memory for the 328 chip.

Might revisit vectors again tomorrow.

So, for pointers would I still use this syntax or is there a way to shorten the assignments?-

    C_MNU.DVar[0] = (M_dynamic){ 25,2 };
    C_MNU.DVar[1] = (M_dynamic){ 35,5 };
    C_MNU.DVar[2] = (M_dynamic){ 45,3 };

and if that is the case, then do I still read the values back via ? -

int8_t LCDpos = C_MNU.DVar[0].POS
  int8_t Dvar = C_MNU.DVar[0].OPT

jgrouse: So, for pointers would I still use this syntax or is there a way to shorten the assignments?-

No. Better read up on C++.

I don't want to sound flippant, but this is just guessing.

So this is not the best place to gain information on c++ programming from more experienced c++ programmers such as yourself?

Last 25 years of vb programming has been through a combination of reading and looking at samples of code from other programmers. The most helpful aspect of learning a different code is through looking at other people's example code. You can call it guessing, but really it is just digging for information/examples from whoever has the time to help.

Thanks for the time that you have made available to me.

Hopefully someone else will come along with some examples of declaring, assigning and retrieving a pointer to a struct in the context of the example code I have placed here.

In the mean time I might look at using classes rather than structs.

My "feeling" is that using the STL and doing dynamic memory allocations will lead to memory fragmentation rather quickly which is really undesirable over the lifetime of your program.

I'm really not feeling well and can't think straight at the moment (perhaps you noticed). Maybe I'll be more agreeable and provide better suggestions tomorrow, or not.

Still not feeling well but I can’t sleep so here’s something that might be useful.

You implied that code fragments may be enough for you. If so this should give you an idea of how you might proceed with what you wish to accomplish.

This does compile …

const uint8_t   MAX_DYNAMIC     = 10;

// the following is the asme as a class except all instance members are by default 'public'
struct position_t
{
    uint8_t  _x, _y;

    // class instance 'constructor'
    position_t(uint8_t x = 0, uint8_t y = 0)    // <-- parameters with which to construct an instance of this class
        : _x(x), _y(y)                          // <-- class initialization list
    {}
};

struct menu_t
{
    char*       _psz;                           // pointer to zero terminated string
    uint8_t     _count;                         // count of 'current' entries in '_pos' array
    position_t  _pos[MAX_DYNAMIC];              // will call position_t constructor using default arguments
};

menu_t  menu;                                   // global compiler will zero struct, calling constructor on each entry of '_pos' in 'position_t'

void loop()
{}

void setup()
{
    menu._psz = "menu label";
    menu._pos[menu._count++] = { 0, 0 };        // will call position_t constructor passing 0, 0 as arguments
    menu._pos[menu._count++] = { 1, 0 };        // will call position_t constructor passing 1, 0 as arguments
}

jgrouse: So this is not the best place to gain information on c++ programming from more experienced c++ programmers such as yourself?

IMO not really, no. It's a forum dedicated to a pretty small niche ([u]Arduino[/u] programming questions). While people who answer questions here will typically know a lot outside this niche too, there are many other resources available for you to get answers to those more general questions. You should IMO not expect your Arduino specialists to teach you the fundamentals of C++ design, or website design, or whatever else you may need to complete your project that is not really about Arduino.

If the goal is the save programming space... I don't think what you're trying to do is going to make that much difference.

If the goal is to save ram... I also don't think this is going to make that much difference.

but to answer some of you're questions...

You can not do this because struct size cannot change. If you were allowed to do this, the struct size would change per assignment.

C_MNU.DVar = { {25,2},{35,5},{45,3},{55,4},{65,6},{75,7} };