web-dev-qa-db-ja.com

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

テキストをフォーマットする「エラー」関数を保持するクラスがあります。可変数の引数を受け入れ、printfを使用してフォーマットします。

例:

class MyClass
{
public:
    void Error(const char* format, ...);
};

Errorメソッドはパラメーターを受け取り、printf/sprintfを呼び出してフォーマットし、それから何かを行う必要があります。すべての書式設定を自分で書きたくないので、既存の書式設定の使用方法を試してみるのは理にかなっています。

75
user5722
void Error(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);
    vfprintf(stderr, format, argptr);
    va_end(argptr);
}

文字列を表示する前に操作し、実際に最初にバッファに保存する必要がある場合は、vsnprintfの代わりにvsprintfを使用します。 vsnprintfは、偶発的なバッファオーバーフローエラーを防ぎます。

141
John Kugelman

これはyaが望むことをするのでvsnprintfを見てください http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

最初にva_list arg配列を初期化してから呼び出す必要があります。

そのリンクの例:/ * vsprintfの例* /

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

void Error (char * format, ...)
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsnprintf (buffer, 255, format, args);


  //do something with the error

  va_end (args);
}
31
Lodle

楕円で関数を使用することはあまり安全ではありません。ログ機能のパフォーマンスが重要でない場合は、boost :: formatのように演算子のオーバーロードを使用することを検討してください。次のようなものを書くことができます。

#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

class formatted_log_t {
public:
    formatted_log_t(const char* msg ) : fmt(msg) {}
    ~formatted_log_t() { cout << fmt << endl; }

    template <typename T>
    formatted_log_t& operator %(T value) {
        fmt % value;
        return *this;
    }

protected:
    boost::format                fmt;
};

formatted_log_t log(const char* msg) { return formatted_log_t( msg ); }

// use
int main ()
{
    log("hello %s in %d-th time") % "world" % 10000000;
    return 0;
}

次のサンプルは、楕円で発生する可能性のあるエラーを示しています。

int x = SOME_VALUE;
double y = SOME_MORE_VALUE;
printf( "some var = %f, other one %f", y, x ); // no errors at compile time, but error at runtime. compiler do not know types you wanted
log( "some var = %f, other one %f" ) % y % x; // no errors. %f only for compatibility. you could write %1% instead.

スタックオーバーフローの既存の質問について詳しく読む必要がありました。

C++の引数の可変数の受け渡し も同様の質問です。 Mike Fには次の説明があります。

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

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

これはまさに私が探していたものです。次のようなテスト実装を実行しました。

void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}
3
user5722

variadic functions を探しています。 printf()とsprintf()は可変機能関数です-可変数の引数を受け入れることができます。

これには基本的に次の手順が必要です。

  1. 最初のパラメーターは、後続のパラメーターの数を示す必要があります。したがって、printf()では、「format」パラメーターがこの指示を与えます-5つの書式指定子がある場合、さらに5つの引数(合計6つの引数)を探します。最初の引数は整数(たとえば、「myfunction」 (3、a、b、c) "「3」は「3つの引数」を意味します)

  2. 次に、va_start()などの関数を使用して、連続する各引数をループして取得します。

これを行う方法に関するチュートリアルがたくさんあります-幸運を祈ります!

3
poundifdef

以下の簡単な例。大きなバッファを渡し、バッファが十分に大きいかどうかをテストする必要があることに注意してください

void Log(LPCWSTR pFormat, ...) 
{
    va_list pArg;
    va_start(pArg, pFormat);
    char buf[1000];
    int len = _vsntprintf(buf, 1000, pFormat, pArg);
    va_end(pArg);
    //do something with buf
}
2
DougN

サンプルをご覧ください http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/ 、メソッドに引数の数を渡しますが、それを省略してコードを変更できます適切に(例を参照)。

0
stefanB