簡體   English   中英

如何移動std :: ostream?

[英]How can an std::ostream be moved?

由於設計無法移動std::ostream因此問題變為:如何移動std::ostream以便它可以寫入不同的目的地?

基本目標是讓一個工廠函數獲取URI並返回一些東西,讓我們稱之為omstream (輸出可移動流),它可以像std::ostream一樣使用:

omstream stream_factory(std::string const& uri);
void     process(std::ostream& out);

int main(int ac, char* av[]) {
    omstream destination{ stream_factory(ac == 2? av[1]: "example.txt") };
    process(destination);
}

omstream將負責正確移動對象:

class omstream
    : public std::ostream {
    // suitable members
public:
    omstream(/* suitable constructor arguments */);
    omstream(omstream&& other) // follow recipe of 27.9.1.11 [ofstream.cons] paragraph 4
        : std:ios(std::move(other))
        , std::ostream(std::move(other))
        // move any members {
        this->set_rdbuf(/* get the stream buffer */);
    }
    // other helpful or necessary members
};

問題是實現omstream (或者甚至是相應的類模板basic_omstream )需要什么?

你幾乎做對了。 你的例子是兩次構建ios基礎的移動。 您應該移動直接基類。 假設有成員streambuf ,也移動它:

class omstream
    : public std::ostream {
    // suitable members
public:
    omstream(/* suitable constructor arguments */);
    omstream(omstream&& other) // follow recipe of 27.9.1.11 [ofstream.cons] paragraph 4
        : std: ostream(std::move(other)),
        // move any members {
        this->set_rdbuf(/* install the stream buffer */);
    }
    // other helpful or necessary members
};

我在set_rdbuf注釋set_rdbuf “get”更改為“install”。 通常,這會將指向成員streambuf的指針安裝到ios基類中。

istream/ostream的移動和交換成員的當前非正統設計被設置為使得派生類(例如ofstreamomstream )的移動和交換成員更直觀。 食譜是:

移動基礎和成員,並在移動構造函數中設置rdbuf

嵌入式rdbuf是整個層次結構的復雜因素。

霍華德答案中公布的代碼是草稿(基於問題中的草案)。 霍華德的答案幫助解決了virtual基類std::ios一個令人困惑的問題:當移動派生流時,基類需要默認構造,因為流的std::ios部分將由std::ostream顯式移動使用std::ios::move()移動構造函數。 這個答案只是填補了缺失的部分。

下面的實現維護一個指向流緩沖區的指針,該緩沖區通常預期存在於堆上,並在std::unique_ptr<...>的幫助下在銷毀時釋放。 由於可能需要返回std::omstream長流的流緩沖區,例如std::coutstd::unique_ptr<...>設置為使用可能無效的刪除器如果omstream不擁有流緩沖區。

#include <ostream>
#include <memory>
#include <utility>

template <typename cT, typename Traits = std::char_traits<cT>>
class basic_omstream
    : public std::basic_ostream<cT, Traits>
{
    using deleter = void (*)(std::basic_streambuf<cT, Traits>*);

    static void delete_sbuf(std::basic_streambuf<cT, Traits>* sbuf) {
        delete sbuf;
    }
    static void ignore_sbuf(std::basic_streambuf<cT, Traits>*) {
    }
    std::unique_ptr<std::basic_streambuf<cT, Traits>, deleter> m_sbuf;
public:
    basic_omstream()
        : std::basic_ios<cT, Traits>()
        , std::basic_ostream<cT, Traits>(nullptr)
        , m_sbuf(nullptr, &ignore_sbuf) {
    }
    basic_omstream(std::basic_streambuf<cT, Traits>* sbuf,
                   bool owns_streambuf)
        : std::basic_ios<cT, Traits>()
        , std::basic_ostream<cT, Traits>(sbuf)
        , m_sbuf(sbuf, owns_streambuf? &delete_sbuf: &ignore_sbuf) {
        this->set_rdbuf(this->m_sbuf.get());
    }
    basic_omstream(basic_omstream&& other)
        : std::basic_ios<cT, Traits>() // default construct ios!
        , std::basic_ostream<cT, Traits>(std::move(other))
        , m_sbuf(std::move(other.m_sbuf)) {
        this->set_rdbuf(this->m_sbuf.get());
    }
    basic_omstream& operator=(basic_omstream&& other) {
        this->std::basic_ostream<cT, Traits>::swap(other);
        this->m_sbuf.swap(other.m_sbuf);
        this->set_rdbuf(this->m_sbuf.get());
        return *this;
    }
};

typedef basic_omstream<char>    omstream;
typedef basic_omstream<wchar_t> womstream;

使用std::ofstreamstd::ostringstream來初始化omstream不起作用,除非相應的流超過omstream 通常,將分配相應的流緩沖區。 例如,類omstream可以像下面的代碼一樣使用,該代碼基於給予合適的工廠函數的URI創建流:

#include <iostream>
#include <sstream>
#include <fstream>

omstream make_stream(std::string const& uri) {
    if (uri == "stream://stdout") {
        return omstream(std::cout.rdbuf(), false);
    }
    else if (uri == "stream://stdlog") {
        return omstream(std::clog.rdbuf(), false);
    }
    else if (uri == "stream://stderr") {
        return omstream(std::cerr.rdbuf(), false);
    }
    else if (uri.substr(0, 8) == "file:///") {
        std::unique_ptr<std::filebuf> fbuf(new std::filebuf);
        fbuf->open(uri.substr(8), std::ios_base::out);
        return omstream(fbuf.release(), true);
    }
    else if (uri.substr(0, 9) == "string://") {
        return omstream(new std::stringbuf(uri.substr(9)), true);
    }
    throw std::runtime_error("unknown URI: '" + uri + "'");
}

int main(int ac, char* av[])
{
    omstream out{ make_stream(ac == 2? av[1]: "stream://stdout") };
    out << "hello, world\n";
}

如果有其他可用的流緩沖區可以從URI構造,則可以將這些緩沖區添加到make_stream()函數中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM