4

私がやろうとしていることを何と呼ぶべきかわからない、悪いタイトルで前もって申し訳ありません。

いくつかの背景、読みたくない場合は、次の段落にスキップしてください。いくつかの条件で assert を呼び出す単体テスト クラスがあり、それが失敗した場合は、渡された文字列を出力します"Failed on index: " + istd::ostream私の考えは、 aを取る代わりにa を返すことstd::stringです。アサートが失敗した場合は を返しstd::cerr、アサートが成功した場合は を返しstd::stringstreamます。私はこれをすべてうまくやることができると思います。std::stringstream参照を返すことができるように、単体テスト クラスにa を格納する必要があります。

私がやりたいのは、標準リターンを返す代わりに、完了時に出力std::ostreamする拡張を返すことです。そのため、アサートごとにそれを覚えておく必要はありません。具体的には次のような考え方です。std::ostreamstd::endl

UnitTest("My test");
ut.assert(false) << "Hello world";
ut.assert(1 == 0) << "On the next line";

破棄時にこの新しいクラスはエンドラインを出力し、使用されなくなるとすぐに破棄されるという考えです (つまり、 << 演算子がなくなります)。これまでのところ、これが私が持っているものです(アサートのコードの一部を削除しました。実際にはクラス内にありますが、何が起こっているのかを示すにはこれで十分です):

class newline_ostream : public std::ostream
{   
    public:
    newline_ostream(std::ostream& other) : std::ostream(other.rdbuf()){}
    ~newline_ostream() { (*this) << std::endl; }
};

newline_ostream& assert(bool condition, std::string error)
{
    if(!condition)
    {
        return newline_ostream(std::cerr);
    }

    return newline_ostream(std::stringstream());
}

このメソッドを試してみると、基本的に、作成したばかりのオブジェクトを返すのは間違っているというメッセージが表示されます。これは左辺値ではないためです。参照を返さないように変更しようとすると、コピー コンストラクターがないと不平を言います (おそらく、これは拡張std::ostream中であり、コピー コンストラクターがないためです)。

私が探しているのは、コンパイラがassert()結果を書き込む一時的な newline_ostream を作成するメソッドであり、使用されなくなるとすぐに終了します (つまり、 << 演算子はなくなります)。これは可能ですか?

4

5 に答える 5

2

コピーstd::cerrできません (のコピー コンストラクターstd::basic_ostreamが削除されます)。したがって、コピー コンストラクターを実装する派生クラスを作成することは、実際にはオプションではありません。

(から派生するのではなく)への参照を含むnewline_ostreamクラスとして作成することをお勧めします。std::ostream

#include <iostream>

class newline_ostream
{
  std::ostream &_strm;
public:

  explicit newline_ostream(std::ostream &strm)
    :_strm(strm)
  {}

  /* In the destructor, we submit a final 'endl'
     before we die, as desired. */
  virtual ~newline_ostream() {
    _strm << std::endl;
  }

  template <typename T>
  newline_ostream &operator<<(const T& t) {
    _strm << t;
    return *this;
  }
};

int main()
{
  newline_ostream s(std::cerr);
  s << "This is a number " << 3 << '\n';

  /* Here we make a copy (using the default copy
     constructor of the new class), just to show
     that it works. */
  newline_ostream s2(s);
  s2 << "This is another number: " << 12;

  return 0;
}
于 2012-10-28T05:56:24.740 に答える
2

異端かもしれませんが、ストリームを派生させて別のストリーム タイプを生成する代わりに、より一般化された方法としてマニピュレータを定義することができます。

// compile with g++ -std=c++11 -Wall -pedantic

#include <iostream>

class sassert
{
public:
    sassert(bool b) :ps(), good(b) 
    {}

    friend std::ostream& operator<<(std::ostream& s, sassert&& a)
    { a.ps = &s; if(a.good) s.setstate(s.failbit); return s; }

    ~sassert()
    { 
        if(good && ps)  ps->clear(); 
        if(!good && ps) *ps << std::endl; 
    }

    //move semantics allow sassert to be a result of a calculation
    sassert(sassert&& s) :ps(s.ps), good(s.good) { s.ps=nullptr; }
    sassert& operator=(sassert s){ ps=s.ps; good=s.good; s.ps=0; return *this; }
private:
    std::ostream* ps;
    bool good;
};

int main()
{
    std::cout << sassert(false) << "this is a failed assertion";
    std::cout << sassert(true) << "this is a good assertion";
    std::cout << sassert(false) << "this is another failed assertion";
    std::cout << sassert(true) << "this is another good assertion";
    return 0;
}

生産を実行します

this is a failed assertion
this is another failed assertion
于 2012-10-28T08:19:27.127 に答える
1

それは本当にあなたが達成したいことの詳細に依存します.

たとえば、 Type Tunnelingについて聞いたことがない場合は、それについて読むのに良い機会かもしれません。シムを使ってクレイジーなことをする方法があります...

それ以外の場合は、単純なバージョンを次に示します。

class AssertMessage {
public:
    AssertMessage(): _out(nullptr) {}
    AssertMessage(std::ostream& out): _out(&out) {}

    AssertMessage(AssertMessage&& other): _out(other._out) { other._out = nullptr; }

    AssertMessage& operator=(AssertMessage&& other) {
        if (_out) { _out << "\n"; }
        _out = other._out;
        other._out = nullptr;
        return *this;
    }

    ~AssertMessage() { if (_out) { _out << "\n"; } }

    template <typename T>
    AssertMessage& operator<<(T const& t) {
        if (_out) { *_out << t; }
    }

private:
    std::ostream* _out;
}; // class AssertMessage

ポインターを埋め込むことで、グローバルな「null」オブジェクトが不要になることに注意してください。それがポインタと参照の主な違いです。また、移動コンストラクター/移動代入演算子を使用して、2つ以上の改行を出力しないようにすることにも注意してください。

assert次に、メソッドを記述できます。

AssertMessage UnitTest::assert(bool i) {
    return i ? AssertMessage() : AssertMessage(std::cerr);
}

しかし....もし私があなたなら、マクロの使用を真剣に検討します。追加の特典が得られるからです。

#define UT_ASSERT(Cond_) \
    assert(Cond_, #Cond_, __func__, __FILE__, __LINE__)

AssertMessage assert(bool test,
                     char const* condition,
                     char const* func,
                     char const* file,
                     int line)
{
    if (test) { return AssertMessage(); }

    return AssertMessage(std::cerr <<
        "Failed assert at " << file << "#" << line <<
        " in " << func << ": '" << condition << "', ");
}

そして、次のようなものが得られます。

Failed assert at project/test.cpp#45 in foo: 'x != 85', <your message>

大規模なテスト スイートでは、(少なくとも) ファイル名と行番号を取得することは非常に重要です。

最後に、マクロを使用するとさらに多くのことが得られます。メッセージ内で などの関数を呼び出すut.assert(x) << x.foo();場合x.foo()は、メッセージが出力されない場合でも完全に評価する必要があります。これはかなり無駄です。ただし、マクロを使用すると:

#define UT_ASSERT(Cond_, Message_) \
    while (!(Cond_)) { std::cerr << .... << Message_ << '\n'; break; }

次に、条件trueが本体に評価された場合、whileまったく実行されません。

于 2012-10-28T11:37:21.453 に答える
0

これにはプリプロセッサを使用することもできます。

#define U_ASSERT(ut, cond, stream) \
    do { ut.assert(cond) << stream << std::endl; } while (0)

U_ASSERT(ut, 1 == 0, "The result is " << (1 == 0));

ただし、これと既に使用している方法 (jogojapan による修正を加えたもの) は、基本的に唯一の代替手段です。これは、バッファの操作を開始しても、1 つの出力操作がいつ完了し、次の操作が開始されるかがわからないため、いつ改行を挿入するかわからないためです。

于 2012-10-28T05:59:53.993 に答える
0

私は自分に合った方法を見つけました(具体的には、参照ではなくコピーを返します):

class newline_ostream : public std::ostream
{   
    public:
    newline_ostream(const std::ostream& other) : std::ostream(other.rdbuf()){}
    newline_ostream(const newline_ostream& other) : std::ostream(other.rdbuf()){}
    ~newline_ostream() { (*this) << std::endl; }
};

newline_ostream assert(bool condition, std::string error)
{
    if(!condition)
    {
        return newline_ostream(std::cerr);
    }

    return newline_ostream(nullStream); // nullStream defined elsewhere (in the class)
}

使用法:

ut.assert(ps.getFaces()[i] == expected[i], ss.str()) << "Test " << i;

出力:

Test 0
Test 1
Test 2
Test 3
Test 5
Test 7
Test 9
Test 10
于 2012-10-28T06:03:30.537 に答える