简体   繁体   English

如何在不复制和保留std :: string对象的情况下获取C ++ std :: string char数据的所有权?

[英]How can I take ownership of a C++ std::string char data without copying and keeping std::string object?

How can I take ownership of std::string char data without copying and withoug keeping source std::string object? 如何在不复制和保留源std :: string对象的情况下获取std :: string char数据的所有权? (I want to use moving semantics but between different types.) (我想使用移动语义但在不同类型之间。)

I use the C++11 Clang compiler and Boost . 我使用C ++ 11 Clang编译器和Boost

Basically I want to do something equivalent to this: 基本上我想做一些与此相当的事情:

{
    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.
}

To clarify, I do need to get rid of std::string: further down the road. 为了澄清,我确实需要摆脱std :: string:继续前进。 The code deals with both string and binary data and should handle it in the same format. 该代码处理字符串和二进制数据,并应以相同的格式处理它。 And I do want the data from std::string, because that comes from another code layer that works with std::string. 我确实想要来自std :: string的数据,因为它来自另一个与std :: string一起使用的代码层。

To give more perspective where I run into wanting to do so: for example I have an asynchronous socket wrapper that should be able to take both std::string and binary data from user for writing. 为了给出更多透视图,我想要这样做:例如,我有一个异步套接字包装器,它应该能够从用户那里获取std :: string和二进制数据进行写入。 Both "API" write versions (taking std::string or row binary data) internally resolve to the same (binary) write. 两个“API”写入版本(将std :: string或行二进制数据)内部解析为相同(二进制)写入。 I need to avoid any copying as the string may be long. 我需要避免任何复制,因为字符串可能很长。

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 in this example to illustrate ownership transfer: any other approach with the same semantics is fine to me. 这个例子中的std :: unique_ptr用于说明所有权转移:任何其他具有相同语义的方法对我来说都没问题。

I am wondering about solutions to this specific case (with std::string char buffer) and this sort of problem with strings, streams and similar general: tips to approach moving buffers around between string, stream, std containers and buffer types. 我想知道这个特定情况的解决方案(使用std :: string char缓冲区)和字符串,流和类似的一般问题:在字符串,流,std容器和缓冲区类型之间接近移动缓冲区的提示。

I would also appreciated tips and links with C++ design approaches and specific techniques when it comes to passing buffer data around between different API's/types without copying. 我还要感谢有关C ++设计方法和特定技术的提示和链接,以便在不复制的情况下在不同的API /类型之间传递缓冲区数据。 I mention but not using streams because I'm shaky on that subject. 我提到但不使用流,因为我对这个主题感到不稳定。

How can I take ownership of std::string char data without copying and withoug keeping source std::string object ? 如何在不复制和保留源std :: string对象的情况下获取std :: string char数据的所有权? (I want to use moving semantics but between different types) (我想使用移动语义但在不同类型之间)

You cannot do this safely. 你不能安全地做到这一点。

For a specific implementation, and in some circumstances, you could do something awful like use aliasing to modify private member variables inside the string to trick the string into thinking it no longer owns a buffer. 对于特定的实现,在某些情况下,你可以做一些非常糟糕的事情,比如使用别名来修改字符串中的私有成员变量,以欺骗字符串使其认为它不再拥有缓冲区。 But even if you're willing to try this it won't always work. 但即使你愿意尝试这一点,也不会一直有效。 Eg consider the small string optimization where a string does not have a pointer to some external buffer holding the data, the data is inside the string object itself. 例如,考虑小字符串优化,其中字符串没有指向保存数据的某个外部缓冲区的指针,数据在字符串对象本身内。


If you want to avoid copying you could consider changing the interface to scheduledWrite. 如果要避免复制,可以考虑将接口更改为scheduledWrite。 One possibility is something like: 一种可能性是:

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});
}

This class take ownership of a string using move semantics and shared_ptr: 此类使用move语义和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;

};

Example usage: 用法示例:

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

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

// use charbuffer...

You could use polymorphism to resolve this. 您可以使用多态来解决此问题。 The base type is the interface to your unified data buffer implementation. 基类型是统一数据缓冲区实现的接口。 Then you would have two derived classes. 那么你将有两个派生类。 One for std::string as the source, and the other uses your own data representation. 一个用于std::string作为源,另一个用于您自己的数据表示。

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_;
    //...
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM