【发布时间】:2019-02-05 22:20:05
【问题描述】:
我有一个模块,它接收 ASCII 命令,然后对它们做出相应的反应。我想知道是否有可能采用更健壮和类型安全的方式来调用处理程序函数。
过去,我有类似下面的代码,这也与这个答案非常相似: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) { }
我不喜欢所有处理函数都必须进行自己的解析。这似乎是不必要的重复且容易出错。
如果我们可以通过以下方式来代替,那就太酷了,所有解析都在 parseCmd 函数中完成:
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) {}
我认为使用旧的 C 风格的可变参数可以在中心函数中进行解析,但这不会带来类型安全。
编辑: 同时我想出了以下解决方案,我认为这在一定程度上平衡了hackiness和模块化:
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);
【问题讨论】:
-
您可能会发现
std::map<std::string, HandlerFunctionType> commands = { {"command1", command1Handler}, { ...}, ... }很有用。您可以使用commands.at(command)(params)调用它。 Documentation forstd::map. -
如果命令处理程序具有不同的签名,我认为这是不可能的。我在这方面更新了帖子。不过,std::map 可能仍然比普通数组有用。
-
如果处理程序有不同的类型,
struct Command也会有问题。函数指针std::function或您在Command中使用的任何内容都将要求所有处理程序具有相同的原型。除非任何东西有一些时髦的魔法,否则我真的希望你让我参与。 -
这绝妙的魔法正是我所寻找的。可以存储参数的数量和参数类型,然后使用可变参数,但这不是类型安全的。 :(
-
切勿将可变参数函数用于任何目的。这是不应该添加到语言中的那些东西之一。
标签: c++ serial-port embedded ascii