将printf用于混合类型的三元运算符

时间:2018-12-13 16:03:18

标签: c types printf conditional-operator mixed

我有一个棘手但有趣的问题。挂在那里!

让我们假设我编写嵌入式代码,并且我在一个系统中必须将值存储为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 ++)

2 个答案:

答案 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.))

:-)