Передача универсального аргумента в функцию C - PullRequest
0 голосов
/ 12 мая 2019

У меня есть структура

typedef struct
{  
    void *l_var;      
    void *r_var;    
}EXPR;

EXPR expr;

Я его инициализирую

expr.l_var = &motor_rt_params[0].position;
expr.r_var = &motor_rt_params[1].position;

Теперь я хочу работать со значениями

void SCRIPT_Process(void *l_var, void *r_var, uint32_t oper)
{
    int32_t res;

    switch (oper)
    {
        case OP_PLUS: 
          res = *((??? *) l_var) + *((??? *)r_var);
          break;
        case OP_MINUS: 
          res = *((??? *) l_var) - *((??? *)r_var);
          break; 
    }
}

SCRIPT_Process(expr.l_var , expr.r_var , OP_PLUS);

Переменные могут быть 32, 16, 8 бит. Вопрос - как я могу привести его к соответствующему типу (вместо (??? *)) во время выполнения?

Ответы [ 4 ]

2 голосов
/ 12 мая 2019

Как насчет сохранения размера при инициализации указателей?

typedef struct
{  
    void *l_var;      
    void *r_var;    
    size_t sz;
}EXPR;

expr.l_var = &motor_rt_params[0].position;
expr.r_var = &motor_rt_params[1].position;
expr.sz = sizeof(motor_rt_params[1].position);

позволяет, например,

void SCRIPT_Process(void *l_var, void *r_var, size_t sz, uint32_t oper)
{
    int32_t res;

    switch (oper)
    {
        case OP_PLUS: 
          if (sz == sizeof(int32_t))
            res = *((int32_t *) l_var) + *((int32_t *)r_var);
          else /* suppose a int16_t */
            res = *((int16_t *) l_var) + *((int16_t *)r_var);
          break;
        case OP_MINUS: 
          if (sz == sizeof(int))
            res = *((int32_t *) l_var) - *((int32_t *)r_var);
          else /* suppose a int16_t */
            res = *((int16_t *) l_var) - *((int16_t *)r_var);
    }
}

SCRIPT_Process(expr.l_var , expr.r_var , expr.sz, OP_PLUS);

Если предположить, что это int или short , я позволю вам добавить регистр int8_t

Преимущество также размещения размера в EXPR состоит в том, чтобы не потерять эту информацию / создать несоответствия из-за ошибки, потому что управление выполняется в другом фрагменте кода.

Или, может быть, дать EXPR вместо полей отдельно в аргументах SCRIPT_Process?

Может быть, вам также нужно знать, подписано или без знака, с дополнительным полем или использовать int для размера, оценивающего положительный размер для без знака (4, 2 или 1) и отрицательный размер для подписанный (-4, -2 -1).

Другой способ - сохранить указатели на нужные функции, а не на размер, что-то вроде виртуальной реализации C ++.

Конечно, все это предполагает, что вы не можете сохранить значения как int32_t в структуре, и вам действительно нужно сохранить указатели.

1 голос
/ 12 мая 2019

безопасный и такой же общий.Нет указателя наказания.Список типов в объединении может быть намного длиннее

typedef union
{
    int8_t u8;
    int16_t u16;
    int32_t u32;
}data_t;


int32_t get(data_t *o, int size)
{
    switch (size)
    {
        case 8: 
           return o -> u8;
          break;
        case 16: 
           return o -> u16;
          break; 
        default: 
          return o -> u32;
          break; 
    }
}

void SCRIPT_Process(data_t *l_var, data_t *r_var, uint32_t oper, int sizel, int sizer)
{
    int32_t res;
    int32_t l = get(l_var, sizel);
    int32_t r = get(r_var, sizer);


    switch (oper)
    {
        case OP_PLUS: 
          res = l + r;
          break;
        case OP_MINUS: 
          res = l -r;
          break; 
          /* ..... */
    }
}
0 голосов
/ 12 мая 2019

Переменные могут быть 32, 16, 8 бит.

Поэтому вам нужно знать:

  • адреса переменных
  • переменныхтипы и
  • операция

И вам нужно передать эту информацию.

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

enum type_e {
    TYPE_u8,
    TYPE_u16,
    TYPE_u32,
    // TODO: add more, ex. TYPE_INT, TYPE_DOUBLE, etc.
};

enum oper_e {
    OP_PLUS,
    OP_MINUS,
    // TODO: add mode, ex. OP_POW or OP_DIV etc. 
};

void SCRIPT_Process(void *res, const void *l, const void *r, enum type_e type, enum oper_e oper)
{
    switch (oper) {
    case OP_PLUS:
        switch (type) {
        case TYPE_u8:
            *(uint8_t*)res = *(uint8_t*)l + *(uint8_t*)r;
            break;
        case TYPE_u16:
            *(uint16_t*)res = *(uint16_t*)l + *(uint16_t*)r;
            break;
        case TYPE_u32:
            *(uint32_t*)res = *(uint32_t*)l + *(uint32_t*)r;
            break;
        default:
            assert(0);
        }
        break;
     case OP_MINUS:
        // TODO:
        assert(0);
    }
}

int main() {
    uint32_t l = 5, r = 2, res;
    SCRIPT_Process(&res, &r, &l, TYPE_u32, OP_PLUS);
    printf("%d + %d = %d\n", (int)l, (int)r, (int)res);
}

Было бы неплохо предоставить макрос, чтобы сделать код более подробным ис меньшим количеством набрав:

#define SCRIPT_PROCESS_MACRO(type, res, l, op, r) \
      *(type*)res = *(type*)l op *(type*)r;

void SCRIPT_Process(void *res, void *l, void *r, enum type_e type, enum oper_e oper)
{
    switch (oper) {
    case OP_PLUS:
        switch (type) {
        case TYPE_u8:
            SCRIPT_PROCESS_MACRO(uint8_t, res, l, +, r);
            break;
        case TYPE_u16:
            SCRIPT_PROCESS_MACRO(uint16_t, res, l, +, r);
            break;
        case TYPE_u32:
            SCRIPT_PROCESS_MACRO(uint32_t, res, l, +, r);
            break;
        default:
            assert(0);
        }
        break;
     case OP_MINUS:
        // TODO:
        assert(0);
    }
}

Или еще более простым с большим количеством макросов, что делает добавление новых операций и типов тривиальным:

#define SCRIPT_PROCESS_MACRO(type, res, l, op, r) \
      *(type*)res = *(type*)l op *(type*)r;

#define SCRIPT_PROCESS_TYPE_CASES(type, res, l, op, r) \
    switch (type) { \
    case TYPE_u8: SCRIPT_PROCESS_MACRO(uint8_t, res, l, op, r); break; \
    case TYPE_u16: SCRIPT_PROCESS_MACRO(uint16_t, res, l, op, r); break; \
    case TYPE_u32: SCRIPT_PROCESS_MACRO(uint32_t, res, l, op, r); break; \
    default: assert(0); break; \
    }

void SCRIPT_Process(void *res, void *l, void *r, enum type_e type, enum oper_e oper)
{
    switch (oper) {
    case OP_PLUS:
        SCRIPT_PROCESS_TYPE_CASES(type, res, l, +, r);
        break;
     case OP_MINUS:
        SCRIPT_PROCESS_TYPE_CASES(type, res, l, -, r);
        break;
    }
}

Или вы можете даже пойти с еще более универсальным решением,имеющие отдельные типы для результата, левый и правый операнды:

   void SCRIPT_Process(
         void *res, enum type_e restype,
         const void *l, enum type_e ltype,
         const void *r, enum type_e rtype,
         enum oper_e oper) {
     if (restype == TYPE_u8 && ltype == TYPE_u32 && rtype == TYPE_u16 && oper == OP_ADD) {
           *(uint8_t*)res = *(uint32_t*)ltype + *(uint16_t*)rtype;
     } if ( // and so on so on so on so on ....
  }
0 голосов
/ 12 мая 2019

Вы можете просто хранить значения в void*, а не в их адресе:

expr.l_var = (void*)motor_rt_params[0].position;
expr.r_var = (void*)motor_rt_params[1].position;

Затем, чтобы использовать их, приведите к intptr_t (значения, а не указатели). Эти типы гарантированно имеют одинаковую ширину с указателем, поэтому вам больше не нужно заботиться о ширине исходных значений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...