将表示不同类型的两个结构加在一起

adding two structs representing different types together

我有一个这样的结构:

typedef enum any_type{
    ANY_TYPE_CHAR,
    ANY_TYPE_UCHAR,
    ANY_TYPE_SHORT,
    ANY_TYPE_USHORT,
    ANY_TYPE_INT,
    ANY_TYPE_UINT,
    ANY_TYPE_LONG,
    ANY_TYPE_ULONG,
    ANY_TYPE_FLOAT,
    ANY_TYPE_DOUBLE,
} any_type;

typedef struct any{
    any_type type;
    union{
        char as_char;
        unsigned char as_uchar;
        short as_short;
        unsigned short as_ushort;
        int as_int;
        unsigned int as_uint;
        long as_long;
        unsigned long as_ulong;
        float as_float;
        double as_double;
    };
} any;

代表指定类型之一的变量。这些结构体有四种可能的运算,分别是加减乘除。

我的问题是,有没有一种有效的方法可以对它们执行这些操作,而无需为每种情况做大量的 if 语句?这将导致仅针对这四个操作的 4 * 10 * 10 = 400 if/else if 语句,这肯定是低效的!

我的代码在C

提前致谢。

你应该制作expr结构。这意味着 AST(抽象语法树)的一个节点。它有操作。这意味着 +、-、*、/ 对于 lhs、rhs。 do_expr 计算表达式。最后,dump_any 打印值。

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

typedef enum any_type{
  ANY_TYPE_CHAR,
  ANY_TYPE_UCHAR,
  ANY_TYPE_SHORT,
  ANY_TYPE_USHORT,
  ANY_TYPE_INT,
  ANY_TYPE_UINT,
  ANY_TYPE_LONG,
  ANY_TYPE_ULONG,
  ANY_TYPE_FLOAT,
  ANY_TYPE_DOUBLE,
} any_type;

typedef struct any{
  any_type type;
  union{
    char as_char;
    unsigned char as_uchar;
    short as_short;
    unsigned short as_ushort;
    int as_int;
    unsigned int as_uint;
    long as_long;
    unsigned long as_ulong;
    float as_float;
    double as_double;
  };
} any;

typedef enum op_type{
  OP_PLUS,
  OP_MINUS,
  OP_MULT,
  OP_DIVID,
} op_type;

typedef struct {
  int op;
  any lhs;
  any rhs;
} expr;

int
do_expr(expr* e, any *r) {
  switch (e->op) {
    case OP_PLUS:
      r->type = e->lhs.type;
      r->as_int = e->lhs.as_int + e->rhs.as_int;
      return 0;
      break;
    default:
      fprintf(stderr, "unknown operation\n");
      break;
  }
  return 1;
}

void
dump_any(any* a) {
  switch (a->type) {
    case ANY_TYPE_INT:
      printf("%d\n", a->as_int);
      break;
    default:
      assert(!"unknown type");
      break;
  }
}

int
main(int argc, char* argv[]) {
  expr e1 = {
    .op = OP_PLUS,
    .lhs = { .type = ANY_TYPE_INT, as_int: 1, },
    .rhs = { .type = ANY_TYPE_INT, as_int: 2, },
  };

  any ret;
  if (do_expr(&e1, &ret) == 0) {
    dump_any(&ret);
  }

  return 0;
}

抱歉,您必须进行大小写切换,因为编译器必须为不同的类型生成不同的 CPU 指令。操作的种类由变量的选择来定义。

这将需要大量代码,因为 combinatorial explosion 是问题的一部分。

使用 table 驱动的方法:定义两个 table,一个用于类型之间的转换,一个用于对它们执行操作。

转换为函数指针类型

typedef any (*convert_any)(any val, any_type target);

并在 any_type 上进行 table 次转化:

convert_any 转换[] = { convert_char, convert_uchar, convert_short, ... };

每种类型都有这样的实现(你需要其中的十个):

static any convert_char(any val, any_type target) {
    any res = { .type = target };
    switch (target) {
        case ANY_TYPE_CHAR: res.as_char = val.as_char; break;
        case ANY_TYPE_INT: res.as_int = (int)val.as_char; break;
        ...
    }
    return res;
}

对于操作创建另一个函数指针类型:

typedef any (*operation_any)(any left, any right);

您需要制作一个此类指针的 3D 数组 - 每个三元组运算符、左操作数的类型和右操作数的类型。

实现看起来像这样:

static any add_int(any left, any right) {
    any lhs = conversion[left.type](left, ANY_TYPE_INT);
    any rhs = conversion[right.type](right, ANY_TYPE_INT);
    any res {.type = ANY_TYPE_INT, .as_int = lhs.as_int + rhs.as_int};
    return res;
}
static any add_double(any left, any right) {
    any lhs = conversion[left.type](left, ANY_TYPE_DOUBLE);
    any rhs = conversion[right.type](right, ANY_TYPE_DOUBLE);
    any res {.type = ANY_TYPE_DOUBLE, .as_double = lhs.as_double + rhs.as_double };
    return res;
}

将有 40 个这样的实现 - 每个结果类型和操作对一个。 3D table 将包含 400 个条目,具体取决于操作需要生成的结果类型。该调用将查找 3D 数组,找到 operation_any 指针,传递两个参数,并得到如下结果:

any res = operations[PLUS_OP][left.type][right.type](left, right);

为了避免组合噩梦,考虑有 3 组类型,无符号整数有符号整数浮点数

目标是确定操作要使用 3 个组中的哪一个。然后根据其类型和目标组获取操作数的值。对每个小组进行数学计算。然后按组和最高排名类型保存。


对于 10 种类型中的每一种,创建 10 个函数来获取数据和 return 作为最宽的 无符号整数 有符号整数 还有 10 个,最后 浮点数 .

有 10 个

对于这 10 种类型中的每一种,执行相同的操作来创建函数来设置数据。到目前为止60个小函数。

要访问正确的函数,请在函数中使用 table 查找。获取功能多3个,设置功能多3个

加法函数的例子在最后。它根据 3 个组之一查找排名最高的类型和 gets/adds/sets。

现在为 -,/,* 添加 3 个函数。总共约60+6+4个函数。

奖励:要添加像 % 这样的新函数,只需要再编写 1 个函数。

#include<assert.h>

typedef enum any_type{
    // Insure these are in rank order
    // ANY_TYPE_CHAR left out for now
    ANY_TYPE_SCHAR,
    ANY_TYPE_UCHAR,
    ANY_TYPE_SHORT,
    ANY_TYPE_USHORT,
    ANY_TYPE_INT,
    ANY_TYPE_UINT,
    ANY_TYPE_LONG,
    ANY_TYPE_ULONG,
    ANY_TYPE_FLOAT,
    ANY_TYPE_DOUBLE,
    ANY_TYPE_N,
} any_type;

typedef struct any{
    any_type type;
    union{
        signed char as_schar;
        unsigned char as_uchar;
        short as_short;
        unsigned short as_ushort;
        int as_int;
        unsigned int as_uint;
        long as_long;
        unsigned long as_ulong;
        float as_float;
        double as_double;
    };
} any;

/////////////////////////////////////
unsigned long any_uget_schar(const any *x) {
  return (unsigned long) x->as_schar;
}
unsigned long any_uget_uchar(const any *x) {
  return x->as_uchar;
}
/* 8 more */

unsigned long any_get_unsigned(const any *x) {
  static unsigned long (*uget[ANY_TYPE_N])(const any *x) = {
    any_uget_schar, any_uget_uchar, /* 8 others */ };
  assert(x->type <  ANY_TYPE_N);
  return (uget[x->type])(x);
}

/////////////////////////////////////
signed long any_sget_schar(const any *x) {
  return x->as_schar;
}
signed long any_sget_uchar(const any *x) {
  return x->as_uchar;
}
/* 8 more */

signed long any_get_signed(const any *x) {
  static signed long (*sget[ANY_TYPE_N])(const any *x) = {
      any_sget_schar, any_sget_uchar, /* 8 others */ };
  assert(x->type <  ANY_TYPE_N);
  return sget[x->type](x);
}
/////////////////////////////////////

double any_get_fp(const any *x); // similar for floating point

/////////////////////////////////////

void any_uset_schar(any *x, unsigned long y) {
  x->as_schar = (signed char) y;
}

void any_uset_uchar(any *x, unsigned long y) {
  x->as_uchar = (unsigned char) y;
}

/* 8 more */

void any_set_unsigned(any *x, unsigned long y) {
  static void (*uset[ANY_TYPE_N])(any *x, unsigned long y) = {
    any_uset_schar, any_uset_uchar, /* 8 others */ };
  assert(x->type <  ANY_TYPE_N);
  uset[x->type](x,y);
}

/* 10 more for any_sset_... */

/* 10 more for any_fset_... */

///////////////////////////////////////////////

static const char classify[] = "susususuff";


any any_get_add(any a, any b) {
  any sum;
  sum.type = max(a.type, b.type);
  assert(sum.type <  ANY_TYPE_N);
  switch (classify[sum.type]) {
    case 's':
      any_set_signed(&sum, any_get_signed(&a) + any_get_signed(&b));
      break;
    case 'u':
      any_set_unsigned(&sum, any_get_unsigned(&a) + any_get_unsigned(&b));
      break;
    case 'f':
      any_set_fp(&sum, any_get_signed(&a) + any_get_signed(&b));
      break;
    default:
      assert(0);
  }
  return sum;
}