2008-09-03 18 views
43

私はC/C++でこれを行うことを検討しています。可変長引数を使用して関数をラップする方法は?

私はVariable Length Argumentsに出くわしたが、これはlibffiを使ってPython & Cとのソリューションを提案しています。

void myprintf(char* fmt, ...) 
{ 
    va_list args; 
    va_start(args,fmt); 
    printf(fmt,args); 
    va_end(args); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int a = 9; 
    int b = 10; 
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b); 
    return 0; 
} 

しかし、期待通りの結果がありません:私は何myprintf

printf機能をラップする場合

は今、以下のようなものです!

This is a number: 1244780 and 
this is a character: h and 
another number: 29953463 

どこか迷ったのですか?

+2

この質問に対する答えは、C++ 11が出ている今_very_異なっています。 –

+0

@MooingDuck確かに、私は 'Variadic templates'答えを追加しました。あなたはC++ 11でより良い方法があると思いますか? –

+0

@MooingDuck vararg関数は可変的なテンプレート関数ではありません。彼らは性質とタイプが異なります。 – rubenvb

答えて

63

を使用するC++では、純粋な

によって何を意味するかわかりませんよ。可変引数リストを使用している場合は、vprintfを使用する必要があります。などvprint、vsprintfの、また、vfprintf、(バッファオーバーランを防ぐことができますMicrosoftのCランタイムでの '安全' のバージョンなどもあります)

あなたはサンプル作品次のように

void myprintf(char* fmt, ...) 
{ 
    va_list args; 
    va_start(args,fmt); 
    vprintf(fmt,args); 
    va_end(args); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int a = 9; 
    int b = 10; 
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b); 
    return 0; 
} 
+0

なぜ "あなたは' va_args'で 'printf'を使うことができないのですか?なぜ 'vprintf'ですか? –

+1

@JohnStroodこれに直接答えるには:printf()は引数として 'va_list'を受け入れないので、異なる数の引数(例えば "...")が必要です。 printf()とvprintf()のマニュアルページを参照してください。 printf()は引数として 'va_list'を受け取り、%形式コードが期待する型の可変数の引数(int、long、floatなど)のみを受け入れます。関数のvprintf()ファミリだけがva_listを受け入れます。 – erco

1

あなたは、CまたはC++を使用していますか?次のC++バージョン、C++ 0xは、その問題の解決策を提供するvariadic templatesをサポートします。

別の回避策は、このような構文を達成するために過負荷に巧妙なオペレータによって達成することができる:仕事にこれを取得するために

void f(varargs va) { 
    BOOST_FOREACH(varargs::iterator i, va) 
     cout << *i << " "; 
} 

f(args = 1, 2, 3, "Hello"); 

、クラスvarargsは、プロキシオブジェクトを返すことoperator =をオーバーライドするために実装する必要がありますこれは次にoperator ,を上書きします。しかし、現在のC++でこのバリアント型を安全にすることは、私が知っている限り、型の消去によって機能しなければならないので、不可能です。

+1

C++ 03 could could上記を安全に行う方法がある 'boost :: tuple'を使用してください。 –

7

私はまた、あなたが私たちは、問題はあなたがva_argsと「printfの」を使用することはできませんということです

#include <cstdarg> 
#include <cstdio> 

class Foo 
{ void Write(const char* pMsg, ...); 
}; 

void Foo::Write(const char* pMsg, ...) 
{ 
    char buffer[4096]; 
    std::va_list arg; 
    va_start(arg, pMsg); 
    std::vsnprintf(buffer, 4096, pMsg, arg); 
    va_end(arg); 
    ... 
} 
0
void myprintf(char* fmt, ...) 
{ 
    va_ list args; 
    va_ start(args,fmt); 
    printf(fmt,args); ----> This is the fault. vprintf(fmt, args); should have been used. 
    va_ end(args); 
} 
If you're just trying to call printf, 
there's a printf variant called vprintf that takes 
the va_list directly : vprintf(fmt, args); 
9

をC++ 11このVariadic templatesを使用して一つの可能​​な解決策である。

template<typename... Args> 
void myprintf(const char* fmt, Args... args) 
{ 
    std::printf(fmt, args...) ; 
} 

EDIT

@rubenvbは、考慮すべきトレードオフがあると指摘しています。たとえば、コードを膨らませる各インスタンスのコードを生成します。

2

実際には、va_listバージョンのない関数をラッパーから呼び出す方法があります。この考え方は、アセンブラを使用し、スタック内の引数に触れずに、関数の戻りアドレスを一時的に置き換えることです。

Visual C x86の例。 call addr_printf通話printf()

__declspec(thread) static void* _tls_ret; 

static void __stdcall saveret(void *retaddr) { 
    _tls_ret = retaddr; 
} 

static void* __stdcall _getret() { 
    return _tls_ret; 
} 

__declspec(naked) 
static void __stdcall restret_and_return_int(int retval) { 
    __asm { 
     call _getret 
     mov [esp], eax ; /* replace current retaddr with saved */ 
     mov eax, [esp+4] ; /* retval */ 
     ret 4 
    } 
} 

static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) { 
    printf("calling printf(\"%s\")\n", fmt); 
} 

static void __stdcall _dbg_printf_end(int ret) { 
    printf("printf() returned %d\n", ret); 
} 

__declspec(naked) 
int dbg_printf(const char *fmt, ...) 
{ 
    static const void *addr_printf = printf; 
    /* prolog */ 
    __asm { 
     push ebp 
     mov ebp, esp 
     sub esp, __LOCAL_SIZE 
     nop 
    } 
    { 
     va_list args; 
     va_start(args, fmt); 
     _dbg_printf_beg(fmt, args); 
     va_end(args); 
    } 
    /* epilog */ 
    __asm { 
     mov esp, ebp 
     pop ebp 
    } 
    __asm { 
     call saveret 
     call addr_printf 
     push eax 
     push eax 
     call _dbg_printf_end 
     call restret_and_return_int 
    } 
} 
+1

私はこれまでこれを書くことを敢えてしないだろうが、私はそれを賞賛することはできない。 –

関連する問題