丑陋的三重间接:可扩展缓冲区管理结构

问题描述 投票:0回答:2

我目前正在尝试为基于 AVR 的步进电机控制器构建一个字符串解析器。这个想法是通过 UART 观察一些输入字符串,然后将该字符串分解为几个子缓冲区,这些子缓冲区最终将进入一组电机控制行为。考虑以下分隔符:

'<' - START
'%' - 更改缓冲区
'>' - 停止
“A”- 顺时针
'B' - 逆时针

我会采用一个看起来像 <1234A%7743B> 的任意宽的字符串,并将其转换为“电机 1 上顺时针旋转 1234 步,电机 B 上逆时针旋转 7743 步”。

到目前为止,我已经想出了以下解决方案

#include <stdio.h>
#include <stdint.h>

#define TARGET 12

typedef struct 
{
    uint8_t STRING_SEL;

    char  X    [32]; // buffer X
    char  Y    [32]; // buffer Y
    uint8_t  iX; // buffer X iterator
    uint8_t  iY; // buffer Y iterator
    uint8_t  PTR_CTR;

    char* Xptr; // pointer to buffer X
    char* Yptr; // pointer to buffer Y

    uint8_t * iXptr; // pointer to buffer X iterator
    uint8_t * iYptr; // pointer to buffer Y iterator

    char ** PTR_ARR [2];   //pointer to array of buffer initial value pointers
    uint8_t ** CTR_ARR[2]; //pointer to array of buffer iterator pointers

} parser_manager;

static parser_manager mgr =  {
                              .STRING_SEL = 0,
                              .X          = {[0 ... 31] = 0},
                              .Y          = {[0 ... 31] = 0},
                              .iX         = 0,
                              .iY         = 0,
                              .PTR_CTR    = 0,
                              .Xptr       = &mgr.X[0],
                              .Yptr       = &mgr.Y[0],
                              .iXptr      = &mgr.iX,
                              .iYptr      = &mgr.iY,
                              .PTR_ARR    = {&mgr.Xptr, &mgr.Yptr},
                              .CTR_ARR    = {&mgr.iXptr, &mgr.iYptr}
                             };

这让我陷入了三星级编程地狱,并导致令人讨厌的会员访问,例如:

char EXPECT_CHAR = *(*(*(mgr.PTR_ARR + mgr.PTR_CTR)) + (***(mgr.CTR_ARR + mgr.PTR_CTR)));
(mgr.Y[TARGET] == EXPECT_CHAR) ? printf("1\n") : printf("0\n");

那么为什么要做这么丑陋的事情呢?我这样做的理由是,在解析传入字符时,我会为每个“%”字符增加 PTR_CTR,从而干净地切换迭代器和缓冲区。然后,我可以通过添加缓冲区和指向结构的指针,然后扩大 PTR 和 CTR 数组,将其扩展到 N 个电机。

我可以看到我可以使用计数器数组为自己节省一定的间接级别,因为它不需要是双指针数组。

这感觉是解决这个问题的一种极其不优雅的方式。如何使这个过程更干净?到目前为止,我唯一的想法是转向固定宽度的数据包,消除对这种虚假动态的需求。

c parsing avr readability
2个回答
0
投票

无需深入了解实现细节,当您将属于同一黄油的东西收集到一个对象中时,实现可能会变得更容易理解,例如:

typedef struct
{
    char buf[32]; // Buffer
    uint8_t  it;  // Buffer iterator
    char ** PTR_ARR;    //pointer to array of buffer initial value pointers
    uint8_t ** CTR_ARR; //pointer to array of buffer iterator pointers
    ...
} motor_t

#define N_MOTORS 2

motor_t motors[N_MOTORS];

然后有一个函数,它需要一个

motor_t*
指针来完成特定任务,例如:填充缓冲区、将值映射到运动行为等。

PTR_ARR
CTR_ARR
仍然看起来像黑客,并且有一个更直接的数据结构。


0
投票

您有

X
Y
作为结构成员
parser_manager
:

    char  X    [32]; // buffer X
    char  Y    [32]; // buffer Y

你也提到过

然后,我可以通过向结构添加缓冲区和指针,然后扩大 PTR 和 CTR 数组,将其扩展到 N 个电机。

你打算怎么做? (通过在

X
结构中添加更多成员,例如
Y
parser_manager
!)

最好创建一个具有成员

X
Y
的不同结构。在
parser_manager
结构中获取该结构的指针,以便可以通过重新分配内存来扩展它。这样,您就可以在访问
X
Y
缓冲区时消除多层间接寻址。

用于演示目的的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char X[32];
    char Y[32];
} buffers;

typedef struct {
    buffers * ptr_x_y;
    int numele;
} parser_manager;

int main()
{
    parser_manager mgr = {NULL, 0};

    mgr.numele = 1;
    mgr.ptr_x_y = malloc (sizeof (buffers) * mgr.numele);
    if (!mgr.ptr_x_y) {
        exit (EXIT_FAILURE);
    }

    strcpy (mgr.ptr_x_y[0].X, "DUMMY_X1");
    strcpy (mgr.ptr_x_y[0].Y, "DUMMY_Y1");
    printf ("%s, %s\n", mgr.ptr_x_y[0].X, mgr.ptr_x_y[0].Y);
    
    mgr.numele = 2;
    buffers * temp;
    mgr.ptr_x_y = realloc (temp = mgr.ptr_x_y, mgr.numele * sizeof (buffers));
    if (!mgr.ptr_x_y) {
        free (temp);
        exit (EXIT_FAILURE);
    }

    strcpy (mgr.ptr_x_y[1].X, "DUMMY_X2");
    strcpy (mgr.ptr_x_y[1].Y, "DUMMY_Y2");
    printf ("%s, %s\n", mgr.ptr_x_y[0].X, mgr.ptr_x_y[0].Y);
    printf ("%s, %s\n", mgr.ptr_x_y[1].X, mgr.ptr_x_y[1].Y);
    
    // free dynamically allocted memory
    
    return 0;
}

iX
iY
是缓冲区
X
Y
的迭代器,因此您也可以使它们成为
buffers
结构的成员。

© www.soinside.com 2019 - 2024. All rights reserved.