web-dev-qa-db-ja.com

APIでvector <uint8_t> :: const_iteratorをどのように置き換える必要がありますか?

私は、コーデックライブラリのインターフェイスを洗練するタスクを与えられました。私たちはC++ 17を使用しており、標準ライブラリのみを使用できます(つまり、Boostはありません)。現在、おおよそ次のようなDecoderクラスがあります。

class Decoder : public Codec {

public:

    struct Result {
        vector<uint8_t>::const_iterator new_buffer_begin;
        optional<Metadata>              metadata;
        optional<Packet>                packet;
    };

    Result decode(vector<uint8_t>::const_iterator buffer_begin,
                  vector<uint8_t>::const_iterator buffer_end);

private:
    // irrelevant details
};

呼び出し元はDecoderをインスタンス化し、データのストリームをデコーダーにフィードします

  1. ファイルからデータのチャンクを読み取り(将来、他のソースが存在する可能性があります)、それをvector<uint8_t>に追加します。

  2. decode関数を呼び出し、ベクトルのイテレータを渡します。

  3. 返されたResultnew_buffer_begindecodeに渡されたbuffer_beginと同一である場合、何もデコードするのに十分なデータがバッファになかったことを意味します。呼び出し元は手順1に戻る必要があります。それ以外の場合、呼び出し元はデコードされたMetadataまたはPacketオブジェクトを使用し、次のパスにnew_buffer_beginを使用して手順2に戻ります。 。

このインターフェースが嫌いで改善の助けが必要なもの:

  • vector<uint8_t>::const_iteratorの使用は過度に具体的です。呼び出し側にvectorの使用を強制しないより一般的なアプローチはありますか? Cスタイルのインターフェースだけを使用することを検討していました。 uint8_t *と長さ。かなり一般的なC++の代替案はありますか?

  • 何かをデコードするのに十分なデータがある場合、metadataorpacketのみが値を持ちます。 std::variantまたは2つのコールバック(タイプごとに1つ)を使用すると、このコードがより自己文書化されると思います。どちらがもっと慣用的かはわかりませんが。それぞれの長所と短所は何ですか?さらに優れたアプローチはありますか?

17
splicer

vectorの強制は不適切であることに同意し、インターフェースをより有用にするためのあなたの試みを称賛します。

decodeuint8_tの連続したシーケンスを期待している場合、実証済みの(そして最も柔軟な)ソリューションは、const uint8_t*std::size_t(または代わりに2つのポインター、ただしポインターと長さはより慣用的です)。

C++ 20以降では、タイプ std::span<const uint8_t> の1つの引数でこれを行うことができます。または、ポインタに戻って、もしそれのために最新のライブラリツールを本当に使いたいのであれば、人々を std::experimental::observer_ptr と混同することができます。

また、decodeをイテレータのペアを受け入れるテンプレートにすることを検討することもできます。また、ドキュメントによってのみ、イテレータがcontiguousシーケンス。しかし、すべてをテンプレートにすることは、常にあなたが望むものであるとは限らず、それが常に有用であるとは限りません。

スパン の@ジャスティンの有効な提案に加えて:

  • std::byteの代わりに uint8_t を使用することも検討してください。したがって、
    Result decode(std::span<const std::byte> buffer);
    
    またはC++ 17を使用している場合は、 C++ガイドラインサポートライブラリ のスパン実装を使用します:
    #include <gsl/span>
    // etc.
    Result decode(gsl::span<const std::byte> buffer);
    
  • 生のメモリ以外のコンテナからのデコードをサポートする場合は、任意のイテレータ(C++ 17以前)または範囲(C++ 20)を使用します。イテレータのバージョン:

    template <typename InputIt>
    Result decode(InputIt start, InputIt end) { /* etc. */ }
    
  • DecoderCodecから継承するのではなく、逆方向に継承するのはおかしいです。

  • コールバックが良い選択であるかどうかの質問は、コードを確認せずに(私にとって)答えるのが難しいことです。ただし、実際にstd::variantを使用して、パケットまたはメタデータのいずれかを持っているという事実を表現してください。コールバックの代わりにバリアント std::visit を使用する場合は、代替を「組み合わせる」こともできます。
15
einpoklum

C++ 20には std::span があり、必要な処理を実行します。

    Result decode(std::span<uint8_t const> buffer);

std::span<T>は、意味的にはT* buffer, size_t sizeと同等です。


C++ 17では、 GSL'sstd::spanのように、gsl::spanと同等のspan型の実装がいくつかあります。 「スパン」とは何か、いつ使用するべきか? を参照してください。

外部ライブラリを使用できない場合は、独自のspanタイプを作成することを検討してください。そうでない場合は、uint8_t const* buffer_begin, uint8_t const* buffer_endが機能します。

5
Justin