0

I have built a string to enum converter, which converts a known set of strings that it receives into enum, in order to satisfy a function pointer. Some of you may recognize some elements of this question elsewhere. This is written in Arduino, so C++.

enum input {FIRMV, VALCN, RQSPC, LIDVM, LIDVC, PREVC, STPWM, CHPWM, STPER, CHPER, SHIGH, PREST, PREND, RQPRE, CLPRE,
            RQDAT, CUPTC, LIDTC, CURPR, CUPTT, CUFAC, LIFAC, STFAC, MTFAC, PPFAC, PCOFF, CUPFM, CUPFC, LIDFM,
            LIDFC, PREFC, FCPWM, FCPER, FHIGH, INVAL
           };

input convert(String str) { //A string to enum converter. Strings are sequences of characters used in Arduinos. if (str == "FIRMV") return FIRMV; else if (str == "VALCN") return VALCN; else if (str == "RQSPC") return RQSPC; else if (str == "LIDVM") return LIDVM; else if (str == "LIDVC") return LIDVC; else if (str == "PREVC") return PREVC; else if (str == "STPWM") return STPWM; else if (str == "CHPWM") return CHPWM; else if (str == "STPER") return STPER; else if (str == "CHPER") return CHPER; else if (str == "SHIGH") return SHIGH; else if (str == "PREST") return PREST; else if (str == "PREND") return PREND; else if (str == "RQPRE") return RQPRE; else if (str == "CLPRE") return CLPRE; else if (str == "RQDAT") return RQDAT; else if (str == "CUPTC") return CUPTC; else if (str == "LIDTC") return LIDTC; else if (str == "CURPR") return CURPR; else if (str == "CUPTT") return CUPTT; else if (str == "CUFAC") return CUFAC; else if (str == "LIFAC") return LIFAC; else if (str == "STFAC") return STFAC; else if (str == "MTFAC") return MTFAC; else if (str == "PPFAC") return PPFAC; else if (str == "PCOFF") return PCOFF; else if (str == "CUPFM") return CUPFM; else if (str == "CUPFC") return CUPFC; else if (str == "LIDFM") return LIDFM; else if (str == "LIDFC") return LIDFC; else if (str == "PREFC") return PREFC; else if (str == "FCPWM") return FCPWM; else if (str == "FCPER") return FCPER; else if (str == "FHIGH") return FHIGH; else return INVAL; }

Is there a better way of converting the string to enum that doesn't involve slowly running through every layer of an if-else stack? Please let me know if more information is needed.

3 Answers3

6

As already hinted at in the answer to the first question, a table might be a good idea, even if only mapping from string to enum. The code would be a bit more readable, even though not necessarily shorter nor faster.

If you need speed, you might sort the entries and use a binary search which is simple enough to implement, but that's probably overkill for 33 entries - my rough estimate is about 4-5 comparisons on average compared to about 16 comparisons on average for the simple linear search, and you might improve on that if you put often-used commands at the start of the table. Likely not worth the effort of the binary search.

6

First thought that comes to mind is skepticism that this is a bottleneck in your program's execution. Most arduino programs spend most of their time asleep, so you should have plenty of leftover CPU available to run through the list as-is.

If for some reason, you still want to speed it up a little bit, I think for a good trade-off between execution speed and code complexity, I think I would switch on the first character then test the full string after that.

Of your 34 commands, they start with 8 unique characters. More evenly distribute the names of the commands if possible, and you can further speed up things.

You'd end up with something that looks like this:

input convert(String str) { //A string to enum converter. Strings are sequences of characters used in Arduinos.
  switch(str[0]) {
    case 'C':
        if (str == "CHPER") return CHPER;
        else if (str == "CHPWM") return CHPWM;
        ...etc...
        else return INVAL;    
    case 'F':
        ...etc...    
    case 'M':
        if (str == "MTFAC") return MTFAC;        
        else return INVAL;
    case 'S':
        if (str == "SHIGH") return SHIGH;
        else if (str == "STFAC") return STFAC;
        else if (str == "STPER") return STPER;
        else if (str == "STPWM") return STPWM;        
        else return INVAL;             
    return INVAL;
  }
}

If you have an abundance of leftover flash, you could use a code generation tool to create an tree of nested switches to narrow down the branches each character at a time.

Additionally, you can skip a comparison by also skipping the first character once you've switched on the first: if (str[1] == "HPER") return CHPER; at the expense of a little bit of readability, and possibly flash usage.

Arduinos, and embedded devices in general play by different rules than desktop apps and webserver applications, so some of the approaches that make sense on an arduino would be crazy-talk for a server application, and vise-versa. There are many situations where just iterating through a loop can be many times faster than more sophisticated algorithms, particularly when dealing with small datasets as is common on embedded devices like these.

Using the C++ STL on arduinos can be pretty treacherous on arduinos because if you aren't very familiar with it you can suddenly explode your code size.

whatsisname
  • 27,703
3

Sounds like a good candidate for a std::map, or perhaps a std::unordered_map.

If you already have a function (probably using switch) that converts the enum to string, then you could use that to populate the map, ensuring the two are consistent.

Also, I've no idea what String is (not being an Arduino developer), but consider accepting a std::string_view rather than passing by value.