16

ソース std::string オブジェクトをコピーしたり保持したりせずに、std::string char データの所有権を取得するにはどうすればよいですか? (移動セマンティクスを使用したいのですが、異なるタイプ間で。)

私は C++11 ClangコンパイラとBoostを使用しています。

基本的に、これと同等のことをしたい:

{
    std::string s(“Possibly very long user string”);
    const char* mine = s.c_str();

    // 'mine' will be passed along,
    pass(mine);

    //Made-up call
    s.release_data();

    // 's' should not release data, but it should properly destroy itself otherwise.
}

明確にするために、 std::string: を取り除く必要があります。このコードは、文字列データとバイナリ データの両方を処理し、同じ形式で処理する必要があります。そして、std::string で動作する別のコード レイヤーからデータが取得されるため、std::string からのデータが必要です。

私がやりたいと思っている場所をより詳しく説明するには、たとえば、書き込みのためにユーザーから std::string とバイナリデータの両方を取得できる非同期ソケットラッパーを持っています。両方の "API" 書き込みバージョン (std::string または行バイナリ データを取る) は、内部的に同じ (バイナリ) 書き込みに解決されます。文字列が長くなる可能性があるため、コピーを避ける必要があります。

WriteId     write( std::unique_ptr< std::string > strToWrite )
{

    // Convert std::string data to contiguous byte storage
    // that will be further passed along to other
    // functions (also with the moving semantics).
    // strToWrite.c_str() would be a solution to my problem
    // if I could tell strToWrite to simply give up its
    // ownership. Is there a way?

    unique_ptr<std::vector<char> > dataToWrite= ??

    //
    scheduleWrite( dataToWrite );
}

void scheduledWrite( std::unique_ptr< std::vecor<char> > data)
{
    …
}

この例の std::unique_ptr は、所有権の譲渡を説明するためのものです。同じセマンティクスを持つ他のアプローチは、私にとっては問題ありません。

この特定のケース (std::string char バッファーを使用) の解決策と、文字列、ストリーム、および同様の一般的なこの種の問題の解決策について疑問に思っています: 文字列、ストリーム、std コンテナー、およびバッファー型の間でバッファーを移動する方法のヒント

また、コピーせずに異なる API/タイプ間でバッファ データを渡す場合のヒントと C++ 設計アプローチおよび特定のテクニックとのリンクをいただければ幸いです。私はストリームを使用していませんが、その件については不安定なので、言及していません。

4

3 に答える 3

11

ソース std::string オブジェクトをコピーしたり保持したりせずに、 std::string char データの所有権を取得するにはどうすればよいですか? (移動セマンティクスを使用したいが、異なるタイプ間で)

これを安全に行うことはできません。

特定の実装では、状況によっては、エイリアシングを使用して文字列内のプライベート メンバ変数を変更し、文字列をだましてバッファを所有していないと思わせるようなひどいことを行うことができます。しかし、これを試してみても、常にうまくいくとは限りません。たとえば、文字列がデータを保持する外部バッファへのポインタを持たず、データが文字列オブジェクト自体の内部にある小さな文字列の最適化を考えてみましょう。


コピーを避けたい場合は、インターフェースをscheduledWriteに変更することを検討できます。1 つの可能性は次のようなものです。

template<typename Container>
void scheduledWrite(Container data)
{
    // requires data[i], data.size(), and &data[n] == &data[0] + n for n [0,size)
    …
}

// move resources from object owned by a unique_ptr
WriteId write( std::unique_ptr< std::vector<char> > vecToWrite)
{
    scheduleWrite(std::move(*vecToWrite));
}

WriteId write( std::unique_ptr< std::string > strToWrite)
{
    scheduleWrite(std::move(*strToWrite));
}

// move resources from object passed by value (callers also have to take care to avoid copies)
WriteId write(std::string strToWrite)
{
    scheduleWrite(std::move(strToWrite));
}

// assume ownership of raw pointer
// requires data to have been allocated with new char[]
WriteId write(char const *data,size_t size) // you could also accept an allocator or deallocation function and make ptr_adapter deal with it
{
    struct ptr_adapter {
        std::unique_ptr<char const []> ptr;
        size_t m_size;
        char const &operator[] (size_t i) { return ptr[i]; }
        size_t size() { return m_size; }
    };

    scheduleWrite(ptr_adapter{data,size});
}
于 2012-07-02T21:29:40.640 に答える
2

このクラスは、移動セマンティクスと shared_ptr を使用して文字列の所有権を取得します。

struct charbuffer
{
  charbuffer()
  {}

  charbuffer(size_t n, char c)
  : _data(std::make_shared<std::string>(n, c))
  {}

  explicit charbuffer(std::string&& str)
  : _data(std::make_shared<std::string>(str))
  {}

  charbuffer(const charbuffer& other)
  : _data(other._data)
  {}

  charbuffer(charbuffer&& other)
  {
    swap(other);
  }

  charbuffer& operator=(charbuffer other)
  {
    swap(other);
    return *this;
  }

  void swap(charbuffer& other)
  {
    using std::swap;
    swap(_data, other._data);
  }

  char& operator[](int i)
  { 
    return (*_data)[i];
  } 

  char operator[](int i) const
  { 
    return (*_data)[i];
  } 

  size_t size() const
  {
    return _data->size();
  }

  bool valid() const
  { 
    return _data;
  }

private:
  std::shared_ptr<std::string> _data;

};

使用例:

std::string s("possibly very long user string");

charbuffer cb(std::move(s)); // s is empty now

// use charbuffer...
于 2012-07-02T22:30:07.847 に答える
1

これを解決するには、ポリモーフィズムを使用できます。基本型は、統合されたデータ バッファーの実装へのインターフェイスです。次に、2 つの派生クラスが作成されます。1 つstd::stringはソースとして、もう 1 つは独自のデータ表現を使用します。

struct MyData {
    virtual void * data () = 0;
    virtual const void * data () const = 0;
    virtual unsigned len () const = 0;
    virtual ~MyData () {}
};

struct MyStringData : public MyData {
    std::string data_src_;
    //...
};

struct MyBufferData : public MyData {
    MyBuffer data_src_;
    //...
};
于 2012-07-02T22:24:16.250 に答える