Error 'typedef' and 'class' not declared.

Hello,

So here’s the problem. I did an Arduino program where I instantiate a class named Pair<F, S> (where of course F and S are templates declared above of the first and second member type).

To make the use easier I also did a “typedef Pair<String, String> Argument;”

This code is working on the codeblocks Arduino IDE, however I don’t know why but on the classic Arduino IDE it’s not compiling. I’ve got the ‘‘Argument’ has not been declared’ error.

If I try to replace ‘Argument’ by ‘Pair<String, String>’, I’ve got ‘‘Pair’ has not been declared’ instead which basically is the same.

Here is my code :

#define M_LOW 10
#define M_MED 50
#define M_HIGH 100

template<typename T>
void print(const T & s) {
    Serial.print(s);
}

template<typename T>
void println(const T & s) {
    Serial.println(s);
}

template<typename F, typename S>
class Pair {
    public:
        Pair() {
 
        }
        Pair(const F & a, const S & b):
            first(a),
            second(b) {

        }
        ~Pair() {}

        F first;
        S second;
};

typedef Pair<String, String> Argument;

/**
* Convert Hex color to rgb format [r, g, b].
* Return false if invalid format.
*/
bool colorHexToRGB(const String & hex, int * rgb) {
    String red, green, blue;
    delete rgb;
    rgb = new int[3];
    if (hex.length() == 7) {
        if (hex[0] == '#') {
            //number = (long) strtol( &hex.substring(1)[1], NULL, 16);
            red = hex.substring(1, 3);
            green = hex.substring(3, 5);
            blue = hex.substring(5, 7);
        } else {
            return false;
        }
    } else if (hex.length() == 6 && hex[0] != '#') {
        red = hex.substring(0, 2);
        green = hex.substring(2, 4);
        blue = hex.substring(4, 6);
    } else {
        return false;
    }

    rgb[0] = (int) strtol(&red[0], NULL, 16);
    rgb[1] = (int) strtol(&green[0], NULL, 16);
    rgb[2] = (int) strtol(&blue[0], NULL, 16);
    return true;
}

/**
* Return the first argument of the raw String.
*/
String getFirstArg(const String & args) {
    if (args.indexOf("/") != -1) {
        return args.substring(0, args.indexOf("/"));
    } else {
        return args;
    }
}

/**
* Return the first argument of the raw String and remove it from the original
* String.
*/
String extractFirstArg(String & args) {
    if (args.indexOf("/") != -1) {
        int index(args.indexOf("/"));
        String arg(args.substring(0, index));
        args = args.substring(index + 1);
        return arg;
    } else {
        String w(args);
        args = "";
        return w;
    }
}

/**
* Split raw String arguments into a pair of name and value.
* Return false if invalid format.
*/
bool splitArgAndValue(const String & raw, Argument & p) {
    int index(raw.indexOf("="));
    if (index != -1) {
        p.first = raw.substring(0, index);
        if (raw.length() >= index + 1) {
            p.second = raw.substring(index + 1);
        } else {
            p.second = "";
        }
        return true;
    }
    return false;
}

/**
* Get all arguments from a String request.
*/
void getArguments(String req, Argument * outputArray, int & argc) {
    argc = 0;
    while (req.length() > 0) {
        String arg(extractFirstArg(req));
        if (arg != "") {
            if (argc >= M_LOW) {
                println("Too much arguments in URL.");
            } else {
                if (splitArgAndValue(arg, outputArray[argc])) {
                    ++argc;
                }
            }
        }
    }
}

/**
* Execute the selected argument with its value.
* Return:
* OK - everything happened correctly.
* INVALID - This argument name is incorrect.
* ERR - something wrong happened during execution of this argument.
*/
String handleArg(const Argument & arg) {
    bool ok(true);
    if (arg.first.equals("action_1_name")) {
        // do something
        ok = false;
    } else if (arg.first.equals("action_2_name")) {
        // do something
    } else {
        return "INVALID";
    }
    return ok? "OK": "ERR";
}

/**
* Execute all arguments in the request.
* Return:
* OK - everything happened correctly.
* INVALID [arg1, arg2, ...] - The list of arguments with invalid name.
* ERR [arg1, arg2, ...] - something wrong happened during execution of these
* arguments.
* Can return multiple statement split by a space.
*/
String handleArgs(const Argument * args, int argc) {
    String code;
    int invalidCount(0), errorCount(0);
    String * invalid = new String[argc];
    String * error = new String[argc];
    for (int i(0); i < argc; ++i) {
        code = handleArg(args[i]);
        if (code == "INVALID") {
            invalid[invalidCount] = args[i].first;
            ++invalidCount;
        } else if (code == "ERR") {
            error[errorCount] = args[i].first;
            ++errorCount;
        }
    }
    String output("");
    if (invalidCount > 0) {
        output += "INVALID [";
        for (int i(0); i < invalidCount; ++i) {
            output += invalid[i];
            if (i < invalidCount - 1) {
                output += ", ";
            } else {
                output +="] ";
            }
        }
    }
    if (errorCount > 0) {
        output += "ERROR [";
        for (int i(0); i < errorCount; ++i) {
            output += error[i];
            if (i < errorCount - 1) {
                output += ", ";
            } else {
                output +="] ";
            }
        }
    }
    if (output == "") {
        output = "OK";
    }
    delete invalid;
    delete error;
    return output;
}

/**
* Handle a raw String request.
*/
String handleRequest(const String & reqFull) {
    String req(reqFull);
    if (req.length() == 0) return "ERROR_NULL_REQUEST";
    String returnCode("");
    if (req[0] == '?' && req.length() > 1) { // setter command
        req = req.substring(1);
        Argument * args = new Argument[M_LOW];
        int argc;
        getArguments(req, args, argc);
        returnCode = handleArgs(args, argc);
        delete args;
    }
    return returnCode;
}

/**
* Return all the informations available.
*/
String getAll() {
 // TODO
}

void setup() {
  Serial.begin(9600);
}

void loop() {
    println(handleRequest("?test=true/second_testfalse/thrid_test=true"));
    delay(1000);
}

I precise I don’t know why but it’s well working when I’m apparently not using functions (like just instanciate templated class Pair inside loop but with no extra code).

Thank you for help.

It would be more helpful if you posted an MRE. rather than a large code with so much unrelated stuff.

Sorry here it is :

#define M_LOW 10
#define M_MED 50
#define M_HIGH 100

template<typename T>
void print(const T & s) {
    Serial.print(s);
}

template<typename T>
void println(const T & s) {
    Serial.println(s);
}

template<typename F, typename S>
class Pair {
    public:
        Pair() {
 
        }
        Pair(const F & a, const S & b):
            first(a),
            second(b) {

        }
        ~Pair() {}

        F first;
        S second;
};

typedef Pair<String, String> Argument;


int foo(Argument & a) {
  print(a.first);
  return 0;
}

void setup() {
  Serial.begin(9600);
}

void loop() {
    delay(1000);
}

This is a well-known Arduino IDE issue, before compiling, it automatically adds function prototypes at the top of your code. These prototypes will most likely end up before the definition of Argument.

To get around this, add your own prototypes, or move use .hpp/.cpp files instead of .ino files for your code.

typedef Pair<String, String> Argument;

int foo(Argument & a); // <---- Add this prototype

int foo(Argument & a) {
  print(a.first);
  return 0;
}

Pieter

Thank you, it's working.