Используйте printf для аргументов тернарного оператора со смешанными типами - PullRequest
0 голосов
/ 13 декабря 2018

У меня каверзный, но интересный вопрос.Держитесь там!

Давайте предположим, что я пишу встроенный код, и я нахожусь в системе, где я должен хранить значения в 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), чтобы обновить мои виджеты с графическим интерфейсом.)

Что я хочу сделать, так это создать «обертку» для 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* с buf[i] и float с getRealValue() в возможных результатах троичного оператора:

ошибка: несоответствие типов в условном выражении

НО, зная, что sprintf - это переменная функция , которая обрабатывает свои аргументы в соответствии с типами, указанными в строке формата (% f,% s, ... благодаря va_arg()функция), есть ли способ найти общий абстрактный тип для троичного оператора , который будет принят sprintf?

Я много чего пробовал, но не могузаставить char* и float работать одновременно.

(К сожалению, я использую C, а не C ++)

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Определите

#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.))

: -)

0 голосов
/ 13 декабря 2018

Нет, просто нет универсального типа, и это не может быть сделано в Си, за исключением очень особых случаев.

Например, аргументы с плавающей запятой передаются в регистрах с плавающей запятой / SSE на x86-64например, тогда как целочисленные аргументы в целочисленных регистрах.Невозможно передать переменный аргумент, который передается как «оба» в переменных аргументах, потому что при вызове функции, подобном

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 -подобная функция, которая принимает указатели для аннулирования для аргументов, а затем разыменовывает их в зависимости от фактического типа.

Это все равно будет довольно сложно.

Если вы действительно в отчаянии, попробуйте libffi .

...