web-dev-qa-db-ja.com

可変数の引数を渡す

可変数の引数を取るC関数があるとします。その中から可変数の引数を期待する別の関数を呼び出して、最初の関数に渡されたすべての引数を渡すにはどうすればよいでしょうか。

例:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }
322
Vicent Marti

楕円を渡すには、それらをva_listに変換し、2番目の関数でそのva_listを使用する必要があります。具体的には;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}
204
SmacL

いたずらで移植性のないトリックをしたいのでなければ、渡す引数の数を知らずに(たとえば)printfを呼び出す方法はありません。

一般的に使用される解決策は、常に別の形式の可変引数関数を提供することです。そのため、printfにはva_listの代わりに...をとるvprintfがあります。 ...バージョンは、va_listバージョンの単なるラッパーです。

57
Mike F

可変機能関数危険になります。より安全なトリックを次に示します。

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});
51
Rose Perrone

壮大なC++ 0xでは、可変長テンプレートを使用できます。

template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
  format_string(fmt, ts...);
}
27
user2023370

関数呼び出しにインラインアセンブリを使用できます。 (このコードでは、引数は文字であると想定しています)。

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm Push next;
        }
        __asm Push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }
7
Yoda

マクロも試すことができます。

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}
5
GeekyJ

最初にローカルバッファに格納することでフォーマッタを渡すことを解決できますが、これにはスタックが必要であり、対処する必要がある場合があります。私はフォローしようとしましたが、うまくいくようです。

#include <stdarg.h>
#include <stdio.h>

void print(char const* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    vprintf(fmt, arg);
    va_end(arg);
}

void printFormatted(char const* fmt, va_list arg)
{
    vprintf(fmt, arg);
}

void showLog(int mdl, char const* type, ...)
{
    print("\nMDL: %d, TYPE: %s", mdl, type);

    va_list arg;
    va_start(arg, type);
    char const* fmt = va_arg(arg, char const*);
    printFormatted(fmt, arg);
    va_end(arg);
}

int main() 
{
    int x = 3, y = 6;
    showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
    showLog(1, "ERR");
}

お役に立てれば。

5
VarunG

ロスのソリューションは少しクリーンアップしました。すべての引数がポインターである場合にのみ機能します。また、言語実装は、__VA_ARGS__が空の場合、前のコンマの省略をサポートする必要があります(Visual Studio C++とGCCの両方がサポートします)。

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
1
BSalita