C: спецификатор динамического формата - PullRequest
0 голосов
/ 07 июня 2019

Я хочу записать произвольное число параметров в буфер в C. Число параметров может варьироваться, и поэтому спецификатор формата sprintf() должен соответственно измениться.

Iнашли решение (ниже), которое работает, но его не просто прочитать.Может ли спецификатор формата быть определен как макрос , который изменяется в зависимости от используемых параметров и можно ли сделать решение более простым?

#define USE_PARAM_1          1
#define USE_PARAM_2          0
#define USE_PARAM_3          1

...

    char buf [128];

    sprintf(buf, "Params:"
#if (USE_PARAM_1 == 1)
        "\tparam_1: %d"
#endif
#if (USE_PARAM_2 == 1)
        "\tparam_2: %d"
#endif
#if (USE_PARAM_3 == 1)
        "\tparam_3: %d"
#endif
#if (USE_PARAM_1 == 1)
        ,param_1
    #endif
#if (USE_PARAM_2 == 1)
        ,param_2
#endif
#if (USE_PARAM_3 == 1)
        ,param_3
#endif
    );

printf("%s\n", buf) будет отображать:

Params: param_1: 1  param_3: 3

РЕДАКТИРОВАТЬ:

Давайте предположим, что параметры на самом деле сами по себе являются меньшими буферами, и знание, которое было включено, не важно, потому что каждый из буферов уже содержит информацию:

    char buf [1024];

    sprintf(buf, "Buffers:"
#if (USE_BUF_1 == 1)
        "\t%s"
#endif
#if (USE_BUF_2 == 1)
        "\t%s"
#endif
#if (USE_BUF_3 == 1)
        "\t%s"
#endif
#if (USE_BUF_1 == 1)
        ,buf_1
    #endif
#if (USE_BUF_2 == 1)
        ,buf_2
#endif
#if (USE_BUF_3 == 1)
        ,buf_3
#endif
    );

printf("%s\n", buf) будет отображать:

Params: this_is_buf_1   this_is_buf_3

1 Ответ

1 голос
/ 07 июня 2019

Особенно учитывая, что вы не упомянули, сколько параметров вы ожидаете, я бы просто сделал это динамически:

#include <stdarg.h>
#include <stdio.h>
int sprintf_vparams(char *Buf, int N /*number of int params*/, ...)
{
    //no bufsize checking
    va_list ap; va_start(ap,N);
    char *buf = Buf;
    buf += sprintf(buf,"Params: ");
    for(int i=0; i<N; i++) buf += sprintf(buf,"\tparam_%d: %d", i+1, va_arg(ap,int));
    va_end(ap);
    return buf-Buf;
}

Функция не самая маленькая (186B на x86-64 с проверками переполнения буфераотключено), но вам не нужно будет генерировать статические строки уникального формата на сайтах вызовов + сайты вызовов будут такими же большими или чуть меньше, чем вы получили бы, если бы использовали sprintf напрямую.

Если вы можете разумно ожидать, что число ваших параметров будет меньше определенного числа, вы можете использовать встроенный переключатель:

static inline int sprintf_aparams(char *Buf, int N /*number of int params*/, int X[])
{
    #define P(Num) "\tparam_"  #Num ": %d"
    switch(N){
    case 0: return sprintf(Buf,"Params: ");
    case 1: return sprintf(Buf,"Params: " P(1), X[0]);
    case 2: return sprintf(Buf,"Params: " P(1) P(2), X[0], X[1]);
    case 3: return sprintf(Buf,"Params: " P(1) P(2) P(3), X[0], X[1], X[2]);
    case 4: return sprintf(Buf,"Params: " P(1) P(2) P(3) P(4), X[0], X[1], X[2], X[3]);
    case 5: return sprintf(Buf,"Params: " P(1) P(2) P(3) P(4) P(5), X[0], X[1], X[2], X[3], X[4]);
    case 6: return sprintf(Buf,"Params: " P(1) P(2) P(3) P(4) P(5) P(6), X[0], X[1], X[2], X[3], X[4], X[5]);
    default: abort(); /*not supported*/ return 0;
    }
    #undef P
}
#define MC_sprintf_params(Buf,...) sprintf_aparams(Buf, sizeof((int[]){__VA_ARGS__ })/sizeof(int), (int[]){__VA_ARGS__})

//usage:
int main()
{
    char buf[1024];
    MC_sprintf_params(buf,2,4,6,8,10,12); //print 6 params
}

Это будет абстракция с истинной нулевой стоимостью на современном компиляторе, таком как gcc или clang.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...