gpt4 book ai didi

c++ - 以更健壮和类型安全的方式处理 ASCII 命令

转载 作者:太空宇宙 更新时间:2023-11-04 12:40:17 26 4
gpt4 key购买 nike

我有一个模块可以接收 ASCII 命令,然后对它们做出相应的 react 。我想知道是否有可能以更健壮和类型安全的方式调用处理程序函数。

过去,我有类似下面的代码,这也与这个答案非常相似: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 风格可变参数可以在中央函数中进行解析,但这不会带来类型安全。

编辑:同时我提出了以下解决方案,我认为它在一定程度上平衡了骇客性和模块化:

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);

最佳答案

围绕这个构建一个抽象层可能会使事情变得更复杂,从而容易出错。我不会这样做,除非您要处理的命令数量巨大,需要维护,而这是您应用程序的主要任务之一。

有了保持类型安全并保持解析与算法分离的先决条件,您可以构建类似于以下类似 C 的伪代码的东西:

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;

然后您可以制作不关心解析的回调处理函数,而只关心它们的专用任务:

void cmd_branch (const char* str);
void cmd_kill (int n);

命令数组可能看起来像这样:

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,
}
};

然后解析函数会做:

  • 通过搜索上面的列表查找接收到的命令(如果列表很大则搜索)。
  • 检查接收到的命令支持什么类型的参数
  • 相应地解析参数
  • 使用适当类型的参数调用相关函数

由于此示例仅使用了一些虚拟类型函数指针 (void(*)(void)),因此您必须转换为正确的类型。可以通过例如 C11 _Generic 来完成:

call(commands[i], int_val);

扩展为:

#define call(command, arg) _Generic((arg), \
int: (void(*)(int)) command.callback, \
const char*: (void(*)(const char*)) command.callback )(arg)

关于c++ - 以更健壮和类型安全的方式处理 ASCII 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54543946/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com