可変引数リストを受け入れる別の関数に可変引数を渡す


114

だから私は2つの関数があり、どちらも同じような引数を持っています

void example(int a, int b, ...);
void exampleB(int b, ...);

ここでをexample呼び出しますがexampleB、変更せずに変数引数リストの変数を渡すにはどうすればよいですかexampleB(これはすでに他の場所でも使用されているため)。



2
その問題の解決策はvprintfを使用していたことですが、ここではそうではありません。
利用不可

これは、提案された複製に関連していますが、間違いなく同じではありません。Cでの可変関数の呼び出しを転送しますか?
ジョナサンレフラー

回答:


127

直接行うことはできません。あなたは取る関数を作成する必要がありますva_list

#include <stdarg.h>

static void exampleV(int b, va_list args);

void exampleA(int a, int b, ...)    // Renamed for consistency
{
    va_list args;
    do_something(a);                // Use argument a somehow
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

void exampleB(int b, ...)
{
    va_list args;
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

static void exampleV(int b, va_list args)
{
    ...whatever you planned to have exampleB do...
    ...except it calls neither va_start nor va_end...
}

2
私はこのような何かをしなければならなかった疑いがある、問題は例の機能は、基本的にvsprintfのためのラッパーと他の多くではないです:/
不可

@Xeross:これはexampleBの動作の外部仕様を変更しないことに注意してください-内部実装を変更するだけです。何が問題なのかわかりません。
ジョナサンレフラー、2010

最初のパラメーターは必須ですか、それともすべてのパラメーターを可変にすることができますか?
QWERTY配列の

1
@Qwerty:構文で, ...は、シグネチャで可変引数リストをとる関数に名前付きの最初の引数が必要です。変数の引数をに変換した場合、のみを受け取る別の関数にをva_list渡すことができますが、その関数(または関数が呼び出す関数)には、の内容を知るための何らかの方法が必要です。va_listva_listva_list
ジョナサンレフラー

46

たぶんここの池に岩を投げ入れますが、C ++ 11可変テンプレートでうまく動作するようです:

#include <stdio.h>

template<typename... Args> void test(const char * f, Args... args) {
  printf(f, args...);
}

int main()
{
  int a = 2;
  test("%s\n", "test");
  test("%s %d %d %p\n", "second test", 2, a, &a);
}

少なくとも、で動作しg++ます。


私は混乱しています-これはC ++> = 11を使用した正当なアプローチですか?
ダンカンジョーンズ

@DuncanJonesはい、パックは拡張されますargs...
ソードフィッシュ

15

va_listを受け取るこれらの関数のバージョンを作成し、それらを渡す必要があります。vprintf例として見てください:

int vprintf ( const char * format, va_list arg );

5

また、printfをラップしたかったので、ここで役立つ回答を見つけました。

可変数の引数をprintf / sprintfに渡す方法

私はパフォーマンスにまったく興味がありませんでした(このコードはいくつかの方法で改善できると確信しています。そうすることをお気軽にしてください:))、これは一般的なデバッグ印刷専用であるため、これを行いました:

//Helper function
std::string osprintf(const char *fmt, ...)
{
    va_list args;
    char buf[1000];
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args );
    va_end(args);
    return buf;
}

これはこのように使用できます

Point2d p;

cout << osprintf("Point2d: (%3i, %3i)", p.x, p.y);
instead of for example:
cout << "Point2d: ( " << setw(3) << p.x << ", " << p.y << " )";

c ++ ostreamはいくつかの点で美しいですが、括弧、コロン、コンマなどの小さな文字列を数値の間に挿入してこのようなものを印刷したい場合、実際には恐ろしくなります。


2

ちなみに、多くのC実装には内部v?printfバリエーションがあり、IMHOはC標準の一部であるはずでした。正確な詳細は異なりますが、一般的な実装では、文字出力関数ポインタと、何が起こるかを示す情報を含む構造体を受け入れます。これにより、printf、sprintf、およびfprintfはすべて同じ「コア」メカニズムを使用できます。たとえば、vsprintfは次のようになります。

void s_out(PRINTF_INFO * p_inf、char ch)
{
  (*(p_inf-> destptr)++)= ch;
  p_inf-> result ++;
}

int vsprintf(char * dest、const char * fmt、va_list args)
{
  PRINTF_INFO p_inf;
  p_inf.destptr = dest;
  p_inf.result = 0;
  p_inf.func = s_out;
  core_printf(&p_inf、fmt、args);
}

次に、core_printf関数は、出力される文字ごとにp_inf-> funcを呼び出します。出力関数は、文字をコンソール、ファイル、文字列、またはその他のものに送信できます。実装がcore_printf関数(およびそれが使用するセットアップメカニズム)を公開する場合、あらゆる種類のバリエーションで拡張できます。


1

可能な方法は#defineを使用することです:

#define exampleB(int b, ...)  example(0, b, __VA_ARGS__)

0

あなたがラップしているというコメントとvsprintfこれがC ++としてタグ付けされているというコメントに基づいて、これを実行しようとせずに、代わりにC ++ iostreamを使用するようにインターフェイスを変更することをお勧めします。これらにはprint、タイプセーフやprintf処理できないアイテムを印刷できるなど、一連の機能に勝る利点があります。現在、一部のやり直しにより、将来の苦痛を大幅に軽減することができます。


どのような利点を参照していますか?
cjcurrie 2013年

@cjcurrie:利点は、ユーザー定義型であっても型の安全性です。もちろん、C関数はユーザー定義型をまったく処理できません。
ジョナサンレフラー、2013

-1

新しいC ++ 0x標準を使用すると、可変テンプレートを使用してこれを実行したり、何も壊すことなく古いコードを新しいテンプレート構文に変換したりできる場合があります。


残念ながら、これはすべての場合に可能ではありません
ラムダ

-1

これが唯一の方法であり、それを行うための最良の方法でもあります。

static BOOL(__cdecl *OriginalVarArgsFunction)(BYTE variable1, char* format, ...)(0x12345678); //TODO: change address lolz

BOOL __cdecl HookedVarArgsFunction(BYTE variable1, char* format, ...)
{
    BOOL res;

    va_list vl;
    va_start(vl, format);

    // Get variable arguments count from disasm. -2 because of existing 'format', 'variable1'
    uint32_t argCount = *((uint8_t*)_ReturnAddress() + 2) / sizeof(void*) - 2;
    printf("arg count = %d\n", argCount);

    // ((int( __cdecl* )(const char*, ...))&oldCode)(fmt, ...);
    __asm
    {
        mov eax, argCount
        test eax, eax
        je noLoop
        mov edx, vl
        loop1 :
        push dword ptr[edx + eax * 4 - 4]
        sub eax, 1
        jnz loop1
        noLoop :
        push format
        push variable1
        //lea eax, [oldCode] // oldCode - original function pointer
        mov eax, OriginalVarArgsFunction
        call eax
        mov res, eax
        mov eax, argCount
        lea eax, [eax * 4 + 8] //+8 because 2 parameters (format and variable1)
        add esp, eax
    }
    return res;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.