我有一个棘手但有趣的问题。挂在那里!
让我们假设我编写嵌入式代码,并且我在一个系统中必须将值存储为16位无符号才能与其他系统兼容。有些值有小数部分,但幸运的是我事先知道分隔符的位置(不固定点)。
现在让我们考虑以下结构:
typedef struct{
// pointer to a function that computes the "real" value
float (*getRealValue)(void);
// pointer to a function that computes a "stringified" value
bool (*getCustomString)(char *);
} var_info_t;
让我们声明一个变量var1,它是一个温度:
uint16_t var1 = 1530; // means 15.3°C
float var1_toFloat(void){ return (float)var1/100; } // should return 15.3
var_info_t var1_info = { .getRealValue = var1_toFloat, .getCustomString = NULL };
和另一个变量var2,恰好更像一个布尔值:
uint16_t var2 = 1; // means Enabled
bool var2_toString(char* buffer){ // should write "Enabled"
if(buffer) sprintf(buffer, "%s", var2 ? "Enabled" : "Disabled");
return true;
}
var_info_t var2_info = { .getRealValue = NULL, .getCustomString = var2_toString };
现在,我想将这些值一起显示在屏幕上,并根据其在屏幕上的位置更改一些精美的格式。
(我只需要调用TEXT_SetText(int widget_id, char* text)
来更新我的GUI小部件。)
我要完成的工作是像这样为 TEXT_SetText()创建一个“包装器” ...
static char text[128], buf[2][32];
#define GET_VAR_AUTO(var_info, i) \
((var_info.getCustomString != NULL && var_info.getCustomString(buf[i])) ? buf[i] : \
(var_info.getRealValue != NULL ? var_info.getRealValue() : 0))
void my_TEXT_SetText(int widget_id, char* format, var_info_t a, var_info_t b){
snprintf(text, sizeof(text), format, GET_VAR_AUTO(a, 0), GET_VAR_AUTO(b, 1));
TEXT_SetText(widget_id, text);
}
...以便致电 my_TEXT_SetText(0, "Regulator is %s at %.1f\260C", var2_info, var1_info);
在我的框中将显示一个漂亮的 Regulator is Enabled at 15.3°C
。
这里的真正优点是,您可以通过<随时随地(不对变量或其值“一无所知”)周期性地调用同一函数来实时更新文本。另外,您可以通过增加snprintf
参数的数量来简单地扩展同一文本内的变量数量。
问题:GET_VAR_AUTO宏在语法上不正确,因为它在可能的结果中混合了char*
的{{1}}和buf[i]
的{{1}}三元运算符:
错误:条件表达式中的类型不匹配
BUT ,知道float
是 variadic函数,它会根据格式字符串(%f,%s ,...感谢getRealValue()
函数),是否可以找到一种三元运算符的通用抽象类型,该类型将被sprintf
接受?>
我尝试了很多事情,但无法同时使va_arg()
和sprintf
起作用。
(很遗憾,我使用的是C,而不是C ++)
答案 0 :(得分:4)
不,根本就没有任何泛型类型,除非在非常特殊的情况下,否则无法在C语言中完成。
例如,浮点参数在x86-64上的浮点/ SSE寄存器中传递,而整数参数在整数寄存器中传递。无法传递在变量参数中作为“两个”传递的变量参数,因为在像这样的函数调用中
printf("%f %d", f, d );
R1 F1 R2
参数将像这样在两个通用寄存器和一个浮点寄存器中传递,而在
中传递printf("%d %f", d, f );
R1 R2 F1
它们将再次通过相同寄存器!
的确,
#include <stdio.h>
int main(void) {
printf("%f %s\n", "hello world", 2.5);
}
已与适用于x86-64 / Linux的GCC一起编译并运行,不会打印任何随机垃圾,而只会打印2.50000 hello world
。因此,在x86-64上,您最多只能在浮点数和通用寄存器中传递 last 参数。
您可以做的是编写类似 own printf
的函数,该函数将接受 pointer使其无效的参数,然后根据实际类型。
仍然会很棘手。
如果您真的很绝望,请考虑尝试libffi。
答案 1 :(得分:2)
定义
#define FLOAT_STR_SIZE_MAX (32) /* or what ever you feel suits */
#define FTOA(b, s, x) \
(snprintf(b, s, "%f", x), b)
并替换
(var_info.getRealValue != NULL ? var_info.getRealValue() : 0)
作者
FTOA((char[FLOAT_STR_SIZE_MAX]){'\0'},
FLOAT_STR_SIZE_MAX,
(var_info.getRealValue != NULL ? var_info.getRealValue() : 0.))
:-)