15

次のようなことができるマクロを作成しようとしています。FORMAT(a << "b" << c << d)結果は文字列になります。これは、ostringstreamを作成し、を挿入a...dして、を返すのと同じ.str()です。何かのようなもの:

string f(){
   ostringstream o;
   o << a << "b" << c << d;
   return o.str()
}

基本的に、FORMAT(a << "b" << c << d) == f()

まず、私は試しました:

1: #define FORMAT(items)                                                   \
   ((std::ostringstream&)(std::ostringstream() << items)).str()

最初の項目がC文字列(const char *)の場合、文字列のアドレスを16進数で出力し、次の項目は正常に出力されます。最初の項目がである場合、std::stringコンパイルに失敗します(一致する演算子はありません<<)。

これ:

2: #define FORMAT(items)                                                   \
   ((std::ostringstream&)(std::ostringstream() << 0 << '\b' << items)).str()

正しい出力のように見えますが、もちろん文字列に0とが含まれています。\b

以下は機能しているようですが、警告付きでコンパイルされます(一時的なアドレスを取得):

3: #define FORMAT(items)                                                   \
   ((std::ostringstream&)(*((std::ostream*)(&std::ostringstream())) << items)).str()

1がc-stringのアドレスを出力し、?でコンパイルできない理由を誰かが知っていstd::stringますか?1と3は本質的に同じではありませんか?

C++0xの可変個引数テンプレートがformat(a, "b", c, d)可能になると思います。しかし、これを今解決する方法はありますか?

4

7 に答える 7

21

あなたはすでにこれをかなり釘付けにしています。しかし、それに従うのは少し難しいです。そこで、あなたの言ったことを要約してみましょう...


ここでの困難は次のとおりです。

  • 一時的なオブジェクトで遊んでいるostringstreamので、アドレスを取ることは禁忌です。

  • ostreamこれは一時的なものであるため、キャストによって単純にオブジェクトに変換することはできません。

  • コンストラクター [明らかに] と のstr()両方がクラスostringstreamメソッドです。(はい、 を使用する必要があります.str()ostringstreamオブジェクトを直接使用すると、 が呼び出されios::operator void*()、文字列オブジェクトではなく、ポインターのような良い/悪い値が返されます。)

  • operator<<(...)継承されたostreamメソッドとグローバル関数の両方として存在します。いずれの場合もostream&参照を返します。

  • ここでの選択肢ostringstream()<<"foo"は、継承されたメソッドostream::operator<<(void* )とグローバル関数operator<<(ostream&,const char* )です。オブジェクト参照に変換してグローバル関数を呼び出すostream::operator<<(void* )ことができないため、継承が優先されます。ostream【コプロに感謝!】


したがって、これを実現するには、次のことが必要です。

  • 一時的に割り当てostringstreamます。
  • に変換しostreamます。
  • データを追加します。
  • に変換し直しostringstreamます。
  • そして呼び出しstr()ます。

割り当て中: ostringstream()

変換: いくつかの選択肢があります。他の人が提案しました:

  • ostringstream() << std::string() // Kudos to *David Norman*
  • ostringstream() << std::dec // Kudos to *cadabra*

または、次を使用できます。

使用できません:

  • operator<<( ostringstream(), "" )
  • (ostream &) ostringstream()

追加: 今は簡単です。

元に戻す: をそのまま使用できます(ostringstream&)。しかし、a のdynamic_cast方が安全です。返される可能性は低いdynamic_cast返さNULLれるべきではない) 場合、次.str()のコマンドでコアダンプがトリガーされます。

呼び出しstr(): 推測します。


すべてを一緒に入れて。

#define FORMAT(ITEMS)                                             \
  ( ( dynamic_cast<ostringstream &> (                             \
         ostringstream() . seekp( 0, ios_base::cur ) << ITEMS )   \
    ) . str() )

参考文献:

.

于 2008-11-20T17:03:09.757 に答える
21

これが私が使用するものです。これらはすべて、ヘッダー ファイル内の 1 つのきちんとしたクラス定義に収まります。

更新: litbのおかげでコードが大幅に改善されました。

// makestring.h:

class MakeString
{
    public:
        std::stringstream stream;
        operator std::string() const { return stream.str(); }

        template<class T>
        MakeString& operator<<(T const& VAR) { stream << VAR; return *this; }
};

使用方法は次のとおりです。

string myString = MakeString() << a << "b" << c << d;
于 2008-11-19T22:58:14.343 に答える
4

あなたが抱えている問題は、ostreamのメンバーではないという事実に関連してoperator << (ostream&, char*)おり、一時的なostreamインスタンスは非const参照にバインドできません。代わりに、void*ostreamのメンバーであるオーバーロードを選択するため、その制限はありません。

最良の方法(ただし、想像力を働かせれば、最も簡単でもエレガントでもありません!)は、Boost Preprocessorを使用して、多数のオブジェクトにテンプレート化された多数の関数オーバーロードを生成することです(インクルードは省略され、仮定されていますusing namespace std;)。

#define MAKE_OUTPUT(z, n, data) \
    BOOST_PP_TUPLE_ELEM(2, 0, data) << BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 1, data), n);

#define MAKE_FORMAT(z, n, data) \
    template <BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T)> \
    inline string format(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, p)) \
    { \
      ostringstream s; \
      BOOST_PP_REPEAT_##z(z, n, MAKE_OUTPUT, (s, p)); \
      return s.str(); \
    }

正確に動作することは保証されていません(テストせずに作成しました)が、基本的にはそれがアイデアです。次に、を呼び出しBOOST_PP_REPEAT(N, MAKE_FORMAT, ())て、文字列を必要に応じてフォーマットする最大N個のパラメーターを使用する一連の関数を作成します(Nを選択した整数に置き換えます。値を大きくするとコンパイル時間に悪影響を与える可能性があります)。可変個引数テンプレートを備えたコンパイラを入手するまでは、これで十分です。ブーストプリプロセッサのドキュメントを読む必要があります。このような機能には非常に強力な機能があります。(関数を生成するために呼び出しを呼び出した#undef後、後でマクロを実行できます)BOOST_PP_REPEAT

于 2008-11-19T22:26:22.147 に答える
2

ostream状態を台無しにしないカダブラのような答えは次のとおりです。

#define FORMAT(items)     static_cast<std::ostringstream &>((std::ostringstream() << std::string() << items)).str()

コプロの答えの最初の段落は、物事がこのように振る舞う理由を説明していると思います。

于 2008-11-19T22:41:01.087 に答える
1

mrreeのソリューション(「優先」とマークされたもの、美しく説明されたもの、G ++で完全に機能するもの)を使用すると、MSVC++で問題が発生しました。このマクロで作成されたすべての文字列が空になりました。

数時間後(そしてここで頭をかいて「リロードされた」質問をすることがたくさんありました)、seekp()呼び出しが原因であることがわかりました。MSVC ++がそれとどのように違うのかわかりませんが、

ostringstream().seekp( 0, ios_base::cur )

カダブラと

ostringstream() << std::dec

MSVC++でも動作します。

于 2009-01-30T10:35:06.167 に答える
1

実用的な解決策は次のとおりです。

#define FORMAT(items)                                                   \
   ((std::ostringstream&)(std::ostringstream() << std::dec << items)).str()

私は最初の議論の振る舞いを完全には理解していません。

于 2008-11-19T22:30:31.950 に答える
0

マクロの代わりに関数を使用しないのはなぜですか?

于 2008-11-19T22:31:36.050 に答える