1

C++ リーダーを実装する必要があるバイナリ パケット形式があります。ライブラリは Qt 4 を使用し、パケット ソースは任意の QIODevice (QTcpSocket、QFile、QBuffer など) にすることができます。フォーマットにはパケットフォーマットが含まれ、各パケットには多くのサブ構造が含まれている場合があります。リーダーに次の情報を返す必要があります。

  • パケット ヘッダー。
  • サブ構造の配列。
  • 読み取り操作のエラー ステータス - 成功、エラー、またはデータが不十分 (特にソケットまたは別の種類のバッファリング デバイスから読み取る場合)。

リーダー API にはさまざまなアプローチが考えられます。

  1. Packet read(Status &status);- 値で戻り、参照引数を介してエラー ステータスを返します。
  2. Packet *read(bool *ok);- エラーが発生した場合は NULL を返すか、十分なデータがない場合は、okそれに応じて true または false を変数 (NULL でない場合) に書き込みます。
  3. Packet *read();- エラーの場合は NULL を返すか、十分なデータがない場合は、別のメソッドを呼び出しbool wasError();て何が起こったかを確認します。okこれは、パラメーターのデフォルト値を にすることで、前のものとマージできますNULL
  4. Status read(Packet &packet);- 返されたステータスが のOk場合、読み取った値がpacket変数に格納されます。それ以外の場合は、エラーまたは EOF のいずれかを示します。
  5. Packet read();- 値で返す。EOF またはエラーの場合は、特別な「ヌル パケット」値を返します。wasError()何が起こったのかを確認するために呼び出します。

もちろん、他の組み合わせも可能です。最善の選択はないようです。アプローチ 1、2、および 4 では、呼び出し元が結果を格納する別の変数を宣言する必要があります。アプローチ 2 と 3 にはヒープをいじる必要がありますが、これは明らかな理由でやりたくありません。アプローチ 1 では、エラーの場合に何が返されるかが明確になりません。アプローチ5はそれを修正しますが、パケット構造に特別な「null」フラグを導入しますが、おそらくそこに属していません.

5 番目のアプローチを取ることもできますが、パケットとステータス情報を含む特別な構造を返しますが、それは別の「合成」タイプを導入し、「エラーが発生した場合、パケット フィールドには何が含まれますか?」という疑問が残ります。

または、3 番目のアプローチを取り、QSharedPointer<Packet>代わりに a を返すこともできます。これにより、呼び出し元が手動でヒープをいじる必要がなくなります。しかし、Packet構造体は、Pimpl のために、おそらくすでに一種のスマート ポインター (共有クラス) になっているでしょう。isNull()代わりにその内部ポインターを使用して、QString のようにメソッドを導入できるかもしれません。

それを行うためのより良い、または伝統的な方法はありますか?

4

1 に答える 1

0

さて、私は先に進み、次のように実装しました。

読み取りメソッドのシグネチャは次のとおりです。

Packet readPacket(bool *error = NULL);

Packet クラスは次のようになります。

class Packet {
    inline Packet(): p(NULL) {} // this is returned on error or EOF
    Packet(const Header &header, const Data &data);
    inline bool isNull() {return p == NULL;}
private:
    QSharedDataPointer<PacketPrivate> p;
};

パケット リーダーには、引数が関数に提供されなかっbool wasError()た場合にエラー ステータスを取得するために使用されるメソッドもあります。errorreadPacket()

この実装により、次のことが可能になります。

  1. のようなエレガントなループを作成するにはwhile (!(packet = reader->readPacket()).isNull()) ...
  2. Packet クラスに "error" や "null" などの無関係なフィールドが含まれないようにするため。
  3. ポインターを手動で割り当てたり削除したり、呼び出し側で明示的にスマート ポインターをいじったりする必要がないようにするため。
  4. デフォルトのコンストラクターを使用するときに初期化されていないフィールドを避けるため。

引数を提供するか、後でerror呼び出す必要があるため、まだ完全にはほど遠いです。wasError()しかし、呼び出し元にエラーのチェックを強制する唯一の方法は例外を使用することだと思いますが、移植性の理由からそれを行いたくありません。

于 2012-07-11T16:52:04.547 に答える