将表示不同类型的两个结构加在一起
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;
}
我有一个这样的结构:
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;
}