I would like to implement an interactive shell via UART serial port for Arduino, with pure C++ OOP style code. But I think if there are too many if-else judgements when judging the user input commands in the code, it will be a bit ugly,
So I would like to ask that, is there any way to avoid using if-else statement? For example,
BEFORE:
while(Serial.available())
{
serialReceive = Serial.readString();// read the incoming data as string
Serial.println(serialReceive);
}
if(serialReceive.equals("factory-reset"))
{
MyService::ResetSettings();
}
else if(serialReceive.equals("get-freeheap"))
{
MyService::PrintFreeHeap();
}
else if(serialReceive.equals("get-version"))
{
MyService::PrintVersion();
}
AFTER:
while(Serial.available())
{
serialReceive = Serial.readString();// read the incoming data as string
Serial.println(serialReceive);
}
MagicClass::AssignCommand("factory-reset", MyService::ResetSettings);
MagicClass::AssignCommand("get-freeheap", MyService::PrintFreeHeap);
MagicClass::AssignCommand("get-version", MyService::PrintVersion);
You can have an array that stores a function pointer along with the string that triggers the command (you can create a struct to store both).
Unfortunately Arduino does not support the std::vector class so for my example I will use c type arrays. However there is a library for Arduino that adds some STL support for Arduino https://github.com/maniacbug/StandardCplusplus (also with this library you can use the functional library to make passing functions as arguments easier)
//struct that stores function to call and trigger word (can actually have spaces and special characters
struct shellCommand_t
{
//function pointer that accepts functions that look like "void test(){...}"
void (*f)(void);
String cmd;
};
//array to store the commands
shellCommand_t* commands;
With this you can either initialize the command array to one size on start or resize it every time you add a command, it just depends on your use case.
A basic function that assumes you have already allocated enough space in the array for adding a command could look like this
int nCommands = 0;
void addCommand(String cmd, void (*f)(void))
{
shellCommand_t sc;
sc.cmd = cmd;
sc.f = f;
commands[nCommands++] = sc;
}
Then inside your setup function you can add your commands in a similar fashion as you have above
addCommand("test", test);
addCommand("hello world", helloWorld);
Lastly in your loop function you can use a for loop to look through all of the commands check the input string against all of the command strings.
You can call the function of the matched command like this
(*(commands[i].f))();
Related
I have a module which receives ASCII commands and then reacts to them accordingly. I am wondering if it is possible, to have a more robust and typesafe way of calling handler functions.
In the past, I had code like the following, which is also very similar to this answer: Processing ASCII commands via RS232 in embedded c
struct Command commands[] = {
{"command1", command1Handler}
{"command2", command2Handler}
...
};
//gets called when a new string has been received
void parseCmd(const char *input) {
//find the fitting command entry and call function pointer
}
bool command1Handler(const char *input) { }
bool command2Handler(const char *input) { }
I don't like that all handler functions have to do their own parsing. This seems needlessly repetitive and error prone.
It would be cool, if instead we could have it the following way, where all parsing is done in the the parseCmd function:
struct Command commands[] = {
{"command1", command1HandlerSafe}
{"command2", command2HandlerSafe}
...
};
void parseCmd(const char *input) {
//1. find fitting command entry
//2. check that parameter number fits the expected number for the target function
//3. parse parameters and validate the types
//4. call function with parameters in their correct types
}
bool command1HandlerSafe(bool param1, const char *param2) { }
bool command2HandlerSafe(int param1) {}
I think with old C-style varargs it would be possible to do the parsing in a central function, but that would not bring type safety.
Edit:
Meanwhile I came up with the following solution, which I thought somewhat balances the hackiness and modularization:
class ParameterSet{
struct Param{
const char *paramString;
bool isInt();
int toInt();
float toFloat();
..
}
ParameterSet(const char *input);
Param at(size_t index);
size_t length();
char m_buffer[100];
Param m_params[10];
}
bool command1HandlerMoreSafe(const ParameterSet *paramSet);
Building an abstraction layer around this might make things more complex and thereby bug prone. I wouldn't do that unless the amount of commands you are supposed to handle is vast, needs to be maintained, and this is one of the main tasks of your application.
With the pre-requisites to keep type safe and keep parsing separate from algorithms, you could build something similar to the following C-like pseudo code:
typedef enum
{
INT,
STR
} type_t; // for marking which type that is supported by the command
typedef struct
{
type_t type;
const char* text; // what kind of text that is expected in case of strings
} arg_t;
typedef struct
{
const char* name; // the name of the command
arg_t* args; // list of allowed arguments
size_t args_n; // size of that list
void (*callback)(void); // placeholder for callback functions of different types
} command_t;
You can then make callback handler functions that aren't concerned about parsing, but only about their dedicated task:
void cmd_branch (const char* str);
void cmd_kill (int n);
The array of commands might look something like this:
const command_t commands[] =
{
{ // BRANCH [FAST][SLOW]
.name="BRANCH",
.args=(entry_t[]){ {STR,"FAST"}, {STR,"SLOW"} },
.args_n=2,
.callback= (void(*)(void)) cmd_branch,
},
{ // KILL [7][9]
.name="KILL",
.args=(entry_t[]){ {INT, NULL} },
.args_n=1,
.callback= (void(*)(void)) cmd_kill,
}
};
The parse function will then do:
Find which command that was received by searching the above list (bsearch if large list).
Check what type of arguments the received command supports
Parse arguments accordingly
Call the relevant function with arguments of the appropriate type
Since this example just used some dummy type function pointer (void(*)(void)), you'll have to cast to the correct type. Can be done by for example C11 _Generic:
call(commands[i], int_val);
which expands to:
#define call(command, arg) _Generic((arg), \
int: (void(*)(int)) command.callback, \
const char*: (void(*)(const char*)) command.callback )(arg)
One way to keep the command handling interfaces the same is to fall back on the venerable argv / argc interface that main() receives. Assuming the received commands have some notion of words (perhaps whitespace separated), it could go like this:
Receive the input string.
Parse the input into words where the first word is the name of the command and the remaining words are its arguments.
As the parsing proceeds, place a pointer to the string that contains each word in an array and keep count of the number of elements in the array.
Using the first word, look up a command function pointer. You can use something like bsearch() if the commands are all known at compile time. Perhaps a hash table might also be appropriate. However you implement the mapping, the result is a pointer to a function that takes an array of pointers to the arguments and a count of the number of elements in the pointer array.
Invoke the command function via its pointer and pass the array of parsed words and the count, just like main() is invoked by startup code.
From there, each command function can deal with what its arguments specifically mean, converting strings representations to internal forms as necessary.
I have these large pcap files of market tick data. On average they are 20gb each. The files are divided into packets. Packets are divided into a header and messages. Messages are divided into a header and fields. Fields are divided into a field code and field value.
I am reading the file a character at a time. I have a file reader class that reads the characters and passes the characters by const ref to 4 call back functions, on_packet_delimiter, on_header_char, on_message_delimiter, on_message_char. The message object uses a similar function to construct its fields.
Up to here I've noticed little loss of efficiency as compared to just reading the chars and not doing anything with them.
The part of my code, where I'm processing the message header and extracting the instrument symbol of the message, slows down the process considerable.
void message::add_char(const char& c)
{
if (!message_header_complete) {
if (is_first_char) {
is_first_char = false;
if (is_lower_case(c)) {
first_prefix = c;
} else {
symbol_vector.push_back(c);
}
} else if (is_field_delimiter(c)) {
on_message_header_complete();
on_field_delimiter(c);
} else {
symbol_vector.push_back(c);
}
} else {
// header complete, collect field information
if (is_field_delimiter(c)) {
on_field_delimiter(c);
} else {
fp->add_char(c);
}
}
}
...
void message::on_message_header_complete()
{
message_header_complete = true;
symbol.assign(symbol_vector.begin(),symbol_vector.end());
}
...
In on_message_header_complete() I am feeding the chars to symbol_vector. Once header is complete I convert to string using vector iterator. Is this the most efficient way to do this?
In addition to The Quantum Physicist's answer: std::string should behave quite similar as vector does. Even the 'reserve' function is available in the string class, if you intend to use it for efficiency.
Adding the characters is just as easy as it can get:
std::string s;
char c = 's';
s += c;
You could add the characters directly to your member, and you are fine. But if you want to keep your member clean until the whole string is collected, you still should use a std::string object instead of the vector. You then add the characters to the temporary string and upon completion, you can swap the contents then. No copying, just pointer exchange (and some additional data such as capacity and size...).
How about:
std::string myStr(myVec.begin(), myVec.end());
Although this works, I don't understand why you need to use vectors in the first place. Just use std::string from the beginning, and use myStr.append() to add characters or strings.
Here's an example:
std::string myStr = "abcd";
myStr.append(1,'e');
myStr.append(std::string("fghi"));
//now myStr is "abcdefghi"
This question already has answers here:
Calling a Function From a String With the Function’s Name in C++
(8 answers)
Closed 7 years ago.
I'm working on a project and I need a way of receiving input from the console/user and use that to run a certain part in the code without the need of elaborate switch/if:else statements. Let me give you an example.
#include <iostream>
#include <string>
using namespace std;
void foo();
void foo2();
int main(){
string s;
cin >> s;
/*Take the input from cin and turn it into a function call, or some other
form of runnable code, like so*/
//Lets say the user inputs "run foo"
foo();
//Or if they input "run foo2"
foo2();
return 0;
}
void foo(){
cout << "Foo works, yay :D";
}
void foo2(){
cout << "Foo2 works, yay :D";
}
You might think I could do this with a switch or multiple if:else statements but this little code is just a small representation of what I need to do. The project requires this to be used on a large scale and I'd like it if I didn't need to use those, to save lines.
So is there any way I can do this in C++? Where the console user tells the program what to run and it runs said function?
Thanks!!
EDIT This is not a duplicate of the string to function call as this gets the input directly from the user versus from the program. As well as the answers show that you can use lua to do this since it is from user input.
Maybe the simplest thing would be to use a std::map of std::function objects:
std::map<std::string, std::function<void()>> funcs;
funcs[userInputString]();
Live example.
Depending on your requirements you may need something more sophisticated than this however and at some point of complexity you might want to consider embedding a scripting language like Lua.
Straight-forward way:
Make a map of string to std::function<void()>
Take the cin input string
Explode the string by spaces to a string array
If the first value is "run", Search the map for the second value and if you find it, execute the function pointer.
Arguments are hard to implement this way.
Better way:
Use LUA, Squirrel, Angel Code, JS, Python, or any other C/C++ embedded language available.
typedef void (*ScriptFunction)(void); // function pointer type
typedef std::map<std::string, ScriptFunction> script_map;
void some_function(void)
{
}
script_map m;
m.insert(std::make_pair("blah", &some_function));
void call_script(const std::string& pFunction)
{
script_map::const_iterator iter = m.find(pFunction);
if (iter == m.end())
{
// not found
}
(*iter->second)();
}
call call_script(user_input); in your code
I have a function that takes in user input via std::cin:
std::getline(std::cin, in);
and creates a corresponding data structure by matching it with a regular expression. The function then returns this data structure.
I'm using boost.test and I want to create a unit test to check that the output data type is correct given some inputs. However I don't know how to go about it since the input isn't passed as an argument to the function.
EDIT: Is there a simple way to create a boost test case that feeds the function a string via standard input?
If you have access to the source code of the function that calls std::getline, then the easiest solution is to rewrite it as a wrapper of another function having the same signature and implementation, but taking an additional std::istream& parameter that is used in place of std::cin. For example, if you currently have:
my_struct my_func()
{
//...
std::getline(std::cin, in);
//...
}
Then rewrite like this:
my_struct my_func(std::istream& is);
inline my_struct my_func()
{
return my_func(std::cin);
}
my_struct my_func(std::istream& is)
{
//...
std::getline(is, in);
//...
}
This way, you will be able to test the core functionality of my_func on constructed input sequences by passing std::istringstream objects into my_func(std::istream&).
If you do not have access to the source code of the function that calls std::getline, then one trick that you can use is to replace the standard in descriptor. See this answer for code that replaces the standard out descriptor and modify accordingly.
What I want is to add possibility to interact with application, and be able to extract information from application or event ask it to change some states.
For that purpose I though of building cli utility. The utility will connect to the application and send user commands (one line strings) to the application and wait for response from the application.
The command should contain:
- command name (e.g. display-session-table/set-log-level etc.)
- optionally command may have several arguments (e.g. log-level=10)
The question to choose syntax and to learn parse it fast and correctly.
I don't want to reinvent the wheel, so maybe there's already an answer out there.
Take a look at the interpreter example (example usage) from Boost.FunctionTypes. Note however that as it is it only supports free functions.
boost::program_options is worth a look.
The Readline library could be useful.
I would suggest using a JSON library.
I use an unholy mix of readline, boost::spirit, and the factory pattern to handle all that. It wouldn't be nearly as unholy if it weren't for readlines unapologetic C syntax :)
The outer loop looks like this
while(true)
{
char *line(NULL);
line = readline((cmd.leaf() + " > ").c_str());
if (line && *line)
{
add_history(line);
int error = ParseLine(line,*s_g, std::cout);
free(line);
if (error == ErrQuit)
break;
if (error == ErrSave)
....
Each command has a completion function and a parser/completion function
char **completeCreate(const std::vector<std::string> &, const char *text, int depth)
{
switch (depth)
{
case 1:
case 2:
{
return rl_completion_matches(text, rl_filename_completion_function);
break;
}
case 3:
{
return rl_completion_matches(text, rulesFill);
break;
}
}
return NULL;
}
Defines the completer for a command that takes two arguments, a filename and a string, which gets registered with the completion mechanism of readline through a factory + macro, that lets me register everything with something that looks like this
REG_COMP(Create, completeCreate);
On the parser side, I have a similar factory setup
int parseCreate(const std::vector<std::string> &names, Game &g, std::ostream &out)
{
if (names.size() != 4)
return parseHelpC(names, g, out);
if (!CreateGame(names[1],names[2],names[3],g))
return ErrGameCreation;
return ErrNone;
}
REG_PARSE(Create,"CardList PowerList RuleSet");
that provides the actual logic and help text
I've left out huge swaths of code that glues everything together, but would be happy to share the hideousness that is the code base (it is currently a private git repository) I look forward to see if someone has something that works better.