array of an array of chars compare

Hi,

I'm trying to make a menuHandler which handles actions based on user input. The below code is only a partial extract of my code but it should describe the problem i am facing.

I have:

Main
|
--MenuHandler.h
|
--Menu.h

The menu.h defines the structure of the menu using char * constants and assign them to arrays. The resulting array of 'strings' is a single menu page. The first item in the array defines the menu name. So for example:

Root, itm1, itm2, itm3, itmBck
submenu1, itm4, itm5, itm6, itmBck

The problem I'm having is with; why is checkCurrentMenu() always returning false.I would expect that the first call to the method returns 1 and the second call would return 0

Note that i need to be able to pass the complete array for comparison as it's also used elsewhere. Also note that this is just a simplification of the logic i'm working with.

//
// Created by Raoul on 13/02/2020.
//

#include <Arduino.h>
#include "menuHandler.h"

void setup() {

    Serial.begin(9600);
}

void loop() {
    menuHandler handler;
    Serial.print("Current menu is root: ");
    Serial.println(handler.checkCurrentMenu(mnuRoot), DEC);
    Serial.print ("Current menu is menu2: ");
    Serial.println(handler.checkCurrentMenu(mnu2), DEC);
    delay(1000);
}
//
// Created by Raoul on 13/02/2020.
//

#ifndef TEST_MENUHANDLER_H
#define TEST_MENUHANDLER_H

#include "menuItems.h"

class menuHandler {

public:
    menuHandler();
    bool checkCurrentMenu(const char * const * page);
};


#endif //TEST_MENUHANDLER_H
//
// Created by Raoul on 13/02/2020.
//

#include "menuHandler.h"

menuHandler::menuHandler() {}

bool menuHandler::checkCurrentMenu(const char* const * page) {
    return (page == mnuRoot);
}
//
// Created by Raoul on 13/02/2020.
//

#include "menuHandler.h"

menuHandler::menuHandler() {}

bool menuHandler::checkCurrentMenu(const char* const * page) {
    return (page == mnuRoot);
}
//
// Created by Raoul on 13/02/2020.
//

#ifndef TEST_MENUITEMS_H
#define TEST_MENUITEMS_H

#include <avr/pgmspace.h>

const char * const PROGMEM  itmRoot= "Reflow Oven Menu";
const char * const PROGMEM itmMenu2 = "Menu2";
const char * const PROGMEM itmSave = "Save";
const char * const PROGMEM itmStart = "Start";
const char * const PROGMEM itmBack = "< Back";

const char * const mnuRoot[] PROGMEM = {itmRoot, itmMenu2, itmStart, itmBack};
const char *const mnu2[] PROGMEM = {itmMenu2, itmSave, itmStart, itmBack};

#endif //TEST_MENUITEMS_H

Output

Current menu is menu2: 0
Current menu is root: 0
Current menu is menu2: 0
Current menu is root: 0
Current menu is menu2: 0
Current menu is root: 0
Current menu is menu2: 0

If you move these lines:

menuHandler::menuHandler() {}

bool menuHandler::checkCurrentMenu(const char* const *page)
{
  return (page == mnuRoot);
}

from the fie that I assume you named menuHandler.cpp into the main file above the setup, it works as you desire.

Don't ask me why because I don't know. I just moved those to simplify what I was looking at and it started working.

Hope that helps.

Thanks but the idea of the menu handler is that I am able to define the actions linked to specific menu items outside the main class for readability and maintainability.

I currently have all this logic in the main class, as you proposed, and that works. But it's seriously affect readability if you have like 20 different menus.

I think it has to do with the memory addresses of the arrays and how they are passed to the method, but I can't figure out how to do it properly with the structure I described.

You have a design flaw in your object oriented way of thinking : your Menu class is not an independent thing, it does share variable names with your main code and can't be compiled independently. --> imagine it being a pre-compiled library, then how would you know all the menu and submenu items that all the programers on earth are going to do.

What happens is that in menuHandler.h when you do

#include "menuItems.h"

so you create the PROGMEM entries once (include is like copying the file in place)

Then in the main .ino file, when you do

#include "menuHandler.h"

you copy in place what's in menuHandler.h which thus adds the menuItems.h code into that file and thus (even with your #ifdef - that's to avoid recursion inside one file) you actually create the entries again.

because the way you use the const keyword, the compiler optimizes things away and does not catch the redefinition and sees them as locally scoped.

So you are redefining twice the PROGMEM entries and so the pointer you use in the main ino file for mnuRoot is not the same pointer used in menuHandler.cpp

One way for you to see this is to add

int a = 1;

at the end of your menuItems.h file, before #endif. That will create a global variable and when you try to compile then the compiler will bark at you as you defined a twice.

[color=orange].../sketch_feb14a.ino.cpp.o (symbol from plugin): In function `setup':
(.text+0x0): multiple definition of `a'
.../sketch/menuHandler.cpp.o (symbol from plugin) : (.text+0x0 ): first defined here[/color]

There is a need to understand the difference between declaration and definition.

A declaration is like you telling the compiler "I hereby declare to you that there shall exist a variable of such type that come into existence somewhere in memory, don't worry about it, things will become clear later at link time"

A definition is like you telling the compiler "I want a variable of this type to be created now. Please allocate its memory (and possibly assign this value to it)."

In your code you need to have only ONE definition and in any other file where you want to reference that variable you need to tell the compiler about its existence with the keyword [b]extern[/b]

--> but in practice here you don't want to play with externs. Your menuHandler class should know nothing at compile time about the menus the user will decide to use. You should have a method declareMainMenu() or pass the main menu at instantiation time and use an instance variable to remember this

Here is what it could look like:

file demo.ino

#include "menuItems.h"
#include "menuHandler.h"
menuHandler handler(mnuRoot);

void setup() {
  Serial.begin(115200);
  Serial.print("Current menu is root: ");
  Serial.println(handler.checkCurrentMenu(mnuRoot), DEC);
  Serial.print ("Current menu is menu2: ");
  Serial.println(handler.checkCurrentMenu(mnu2), DEC);
}

void loop() {}

file menuItems.h

const char itmRoot[] PROGMEM  = "Reflow Oven Menu";
const char itmMenu2[] PROGMEM = "Menu2";
const char itmSave[] PROGMEM  = "Save";
const char itmStart[] PROGMEM = "Start";
const char itmBack[] PROGMEM  = "< Back";

const char* const mnuRoot[] PROGMEM = {itmRoot, itmMenu2, itmStart, itmBack};
const char* const mnu2[] PROGMEM = {itmMenu2, itmSave, itmStart, itmBack};

then your class knows nothing about menuItems.

file menuHandler.h

#ifndef TEST_MENUHANDLER_H
#define TEST_MENUHANDLER_H

class menuHandler {

  public:
    menuHandler(const char* const* m) : _rootMenu(m) {};
    bool checkCurrentMenu(const char* const* page);

  private:
    const char* const* _rootMenu;
};


#endif //TEST_MENUHANDLER_H

file menuHandler.cpp

#include "menuHandler.h"

bool menuHandler::checkCurrentMenu(const char* const* page) {
  return (page == _rootMenu);
}

PS: if you read the documentation for PROGMEM you'll see they state

Syntax

const dataType variableName[] PROGMEM = {data0, data1, data3…};

dataType - any variable type
variableName - the name for your array of data

Note that because PROGMEM is a variable modifier, there is no hard and fast rule about where it should go, so the Arduino compiler accepts all of the definitions below, which are also synonymous. However experiments have indicated that, in various versions of Arduino (having to do with GCC version), PROGMEM may work in one location and not in another. The "string table" example below has been tested to work with Arduino 13. Earlier versions of the IDE may work better if PROGMEM is included after the variable name.

const dataType variableName[] PROGMEM = {}; // use this form
const PROGMEM dataType variableName[] = {}; // or this one
const dataType PROGMEM variableName[] = {}; // not this one <<====

so your menu items should be defined this way

const char itmRoot[] PROGMEM  = "Reflow Oven Menu";
const char itmMenu2[] PROGMEM = "Menu2";
const char itmSave[] PROGMEM  = "Save";
const char itmStart[] PROGMEM = "Start";
const char itmBack[] PROGMEM  = "< Back";

const char* const mnuRoot[] PROGMEM = {itmRoot, itmMenu2, itmStart, itmBack};
const char* const mnu2[] PROGMEM = {itmMenu2, itmSave, itmStart, itmBack};