将 printf 用于混合类型的三元运算符参数
Use printf for ternary operator arguments with mixed types
我有一个棘手但有趣的问题。坚持住!
假设我编写嵌入式代码,并且我在一个系统中,我必须以 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 小部件。)
我想要完成的是创建一个 "wrapper" 到 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 %.1f0C", var2_info, var1_info);
会在我的盒子里显示一个漂亮的 Regulator is Enabled at 15.3°C
。
这里的真正优势是您可以实时更新文本 只需定期从 "anywhere" 调用相同的函数(没有 "knowing anything"关于变量或其值)。您也可以通过增加 snprintf
参数的数量来简单地扩展同一文本中的变量数量。
问题: GET_VAR_AUTO 宏在语法上不正确,因为它混合了来自 buf[i]
的 char*
和来自 float
getRealValue()
在三元运算符的可能结果中:
error: type mismatch in conditional expression
但是, 知道 sprintf
是一个 可变参数函数 ,它根据格式字符串(%f、%s、...感谢 va_arg()
函数),有没有办法找到三元运算符 的通用抽象类型,这将是被 sprintf
接受 ?
我尝试了很多方法,但无法让 char*
和 float
同时工作。
(不幸的是我使用的是 C,而不是 C++)
不,根本没有任何泛型类型,除非在非常特殊的情况下,否则不能在 C 中完成。
例如,浮点参数在 x86-64 上的浮动 point/SSE 寄存器中传递,而整数参数在整数寄存器中传递。无法传递在变量参数中作为 "both" 传递的变量参数,因为在像
这样的函数调用中
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);
}
使用 GCC 为 x86-64/Linux 和 运行 编译,将不会打印任何随机垃圾,而是 2.50000 hello world
。因此,在 x86-64 上,您最多可以做的是在浮点和通用寄存器中传递 last 参数。
你可以做的是编写你自己的 printf
-like 函数,它会接受 指向 void 的指针参数,然后根据实际类型取消引用它们。
还是会比较棘手
如果你真的很绝望,可以考虑尝试 libffi。
定义
#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]){'[=12=]'},
FLOAT_STR_SIZE_MAX,
(var_info.getRealValue != NULL ? var_info.getRealValue() : 0.))
:-)
我有一个棘手但有趣的问题。坚持住!
假设我编写嵌入式代码,并且我在一个系统中,我必须以 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 小部件。)
我想要完成的是创建一个 "wrapper" 到 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 %.1f0C", var2_info, var1_info);
会在我的盒子里显示一个漂亮的 Regulator is Enabled at 15.3°C
。
这里的真正优势是您可以实时更新文本 只需定期从 "anywhere" 调用相同的函数(没有 "knowing anything"关于变量或其值)。您也可以通过增加 snprintf
参数的数量来简单地扩展同一文本中的变量数量。
问题: GET_VAR_AUTO 宏在语法上不正确,因为它混合了来自 buf[i]
的 char*
和来自 float
getRealValue()
在三元运算符的可能结果中:
error: type mismatch in conditional expression
但是, 知道 sprintf
是一个 可变参数函数 ,它根据格式字符串(%f、%s、...感谢 va_arg()
函数),有没有办法找到三元运算符 的通用抽象类型,这将是被 sprintf
接受 ?
我尝试了很多方法,但无法让 char*
和 float
同时工作。
(不幸的是我使用的是 C,而不是 C++)
不,根本没有任何泛型类型,除非在非常特殊的情况下,否则不能在 C 中完成。
例如,浮点参数在 x86-64 上的浮动 point/SSE 寄存器中传递,而整数参数在整数寄存器中传递。无法传递在变量参数中作为 "both" 传递的变量参数,因为在像
这样的函数调用中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);
}
使用 GCC 为 x86-64/Linux 和 运行 编译,将不会打印任何随机垃圾,而是 2.50000 hello world
。因此,在 x86-64 上,您最多可以做的是在浮点和通用寄存器中传递 last 参数。
你可以做的是编写你自己的 printf
-like 函数,它会接受 指向 void 的指针参数,然后根据实际类型取消引用它们。
还是会比较棘手
如果你真的很绝望,可以考虑尝试 libffi。
定义
#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]){'[=12=]'},
FLOAT_STR_SIZE_MAX,
(var_info.getRealValue != NULL ? var_info.getRealValue() : 0.))
:-)