简体   繁体   English

为什么 std::codecvt 仅用于文件 I/O 流?

[英]Why is std::codecvt only used by file I/O streams?

I've been implementing a codecvt for handling indentiation of output streams.我一直在实现一个用于处理输出流标识的 codecvt。 It can be used like this and works fine:它可以像这样使用并且工作正常:

std::cout << indenter::push << "im indentet" << indenter::pop << "\n im not..."

However, while I can imbue an std::codecvt to any std::ostream I was very confused when I found out that my code worked with std::cout as well as std::ofstream , but not for example for std::ostringstream even while all of which inherit from the base class std::ostream .但是,虽然我可以将std::codecvt任何std::ostream但当我发现我的代码与std::cout以及std::ofstream一起使用时,我感到非常困惑,但例如不适用于std::ostringstream即使所有这些都继std::ostreamstd::ostream

The facet is constructed normally, the code compiles, it doesn't throw any exceptions... It's just that none of the member functions of the std::codecvt are called.构面正常构造,代码编译,它不会抛出任何异常......只是std::codecvt的成员函数都没有被调用。

For me that is very confusing and I had to spend a lot of time figuring out that std::codecvt won't do anything on non file I/O streams.对我来说,这是非常令人困惑的,我不得不花很多时间弄清楚std::codecvt不会对非文件 I/O 流做任何事情。

Is there any reason std::codecvt is not being used by all classes inherited by std::ostream ?是否有任何原因std::codecvt没有被std::ostream继承的所有类使用?

Furthermore does anyone have an idea on which structs I could fall back on to implement the indenter?此外,有没有人知道我可以依靠哪些结构来实现压头?

Edit: this is the part of the language I'm referring to:编辑:这是我所指语言的一部分:

All file I/O operations performed through std::basic_fstream use the std::codecvt<CharT, char, std::mbstate_t> facet of the locale imbued in the stream.通过 std::basic_fstream 执行的所有文件 I/O 操作使用流中灌输的语言环境的 std::codecvt<CharT, char, std::mbstate_t> 方面。

Source: https://en.cppreference.com/w/cpp/locale/codecvt来源: https : //en.cppreference.com/w/cpp/locale/codecvt


Update 1:更新 1:

I've made a small example illustrating my problem:我做了一个小例子来说明我的问题:

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

static auto invocation_counter = 0u;

struct custom_facet : std::codecvt<char, char, std::mbstate_t>
{
  using parent_t = std::codecvt<char, char, std::mbstate_t>;

  custom_facet() : parent_t(std::size_t { 0u }) {}

  using parent_t::intern_type;
  using parent_t::extern_type;
  using parent_t::state_type;

  virtual std::codecvt_base::result do_out (state_type& state, const intern_type* from, const intern_type* from_end, const intern_type*& from_next,
                                                               extern_type* to, extern_type* to_end, extern_type*& to_next) const override
  {
    while (from < from_end && to < to_end)
    {
      *to = *from;

      to++;
      from++;
    }

    invocation_counter++;

    from_next = from;
    to_next = to;

    return std::codecvt_base::noconv;
  }

  virtual bool do_always_noconv() const throw() override
  {
    return false;
  }
};

std::ostream& imbueFacet (std::ostream& ostream)
{
  ostream.imbue(std::locale { ostream.getloc(), new custom_facet{} });

  return ostream;
}

int main()
{
  std::ios::sync_with_stdio(false);

  std::cout << "invocation_counter = " << invocation_counter << "\n";

  {
    auto ofstream = std::ofstream { "testFile.txt" };

    ofstream << imbueFacet << "test\n";
  }

  std::cout << "invocation_counter = " << invocation_counter << "\n";

  {
     auto osstream = std::ostringstream {};

     osstream << imbueFacet << "test\n";
  }

  std::cout << "invocation_counter = " << invocation_counter << "\n";
}

I would except invocation_counter to increase after streaming in the std::ostringstream , but it doesn't.我希望invocation_counterstd::ostringstream式传输后增加,但事实并非如此。


Update 2:更新 2:

After more research I found out that I could use std::wbuffer_converter .经过更多研究,我发现我可以使用std::wbuffer_converter To quote https://en.cppreference.com/w/cpp/locale/wbuffer_convert引用https://en.cppreference.com/w/cpp/locale/wbuffer_convert

std::wbuffer_convert is a wrapper over stream buffer of type std::basic_streambuf<char> which gives it the appearance of std::basic_streambuf<Elem> . std::wbuffer_convertstd::basic_streambuf<char>类型的流缓冲区的包装器,它赋予它std::basic_streambuf<Elem>的外观。 All I/O performed through std::wbuffer_convert undergoes character conversion as defined by the facet Codecvt.通过std::wbuffer_convert执行的所有 I/O std::wbuffer_convert经历由构面 Codecvt 定义的字符转换。 [...] [...]

This class template makes the implicit character conversion functionality of std::basic_filebuf available for any std::basic_streambuf .此类模板使std::basic_filebuf的隐式字符转换功能可用于任何std::basic_streambuf

This way I can apply a facet to a std::ostringstream :这样我就可以将一个方面应用于std::ostringstream

auto osstream = std::ostringstream {};

osstream << "test\n";
  
auto facet = custom_facet{};
  
std::wstring_convert<custom_facet, char> conv;
  
auto str = conv.to_bytes(osstream.str());

However, I lose the ability to concate facets using the streaming operator << .但是,我失去了使用流操作符<<连接面的能力。

This confuses me even more why the std::codecvt is not implicity used by ALL output streams.这让我更加困惑,为什么所有输出流都没有隐式使用std::codecvt All output streams inherit from std::basic_streambuf whose interface is suitable to using std::codecvt , which is just using an input and an output character sequence, fully implemented in std::basic_streambuf .所有输出流都继承自std::basic_streambuf其接口适合使用std::codecvt ,它只是使用输入和输出字符序列,在std::basic_streambuf完全实现。

So why is the parsing of std::codecvt implemented in std::basic_filebuf instead of std::basic_streambuf ?那么为什么在std::basic_filebuf而不是std::basic_streambuf实现了std::codecvt的解析? std::basic_filebuf inherits std::basic_streambuf after all... std::basic_filebuf毕竟继承了std::basic_streambuf ......

Either I have some fundamental misunderstanding on how streams work in C++ or std::codecvt is poorly integrated in the standard.要么我std::codecvt在 C++ 中的工作方式有一些根本性的误解,要么std::codecvt与标准的集成很差。 Maybe this is why it is marked as deprecated?也许这就是它被标记为已弃用的原因?

The std::codecvt facet was originally intended to handle I/O conversions between disk and memory character representation. std::codecvt facet 最初旨在处理磁盘内存字符表示之间的 I/O 转换。 Quoted from paragraph 39.4.6 of Bjarne Stroustrup's The C++ Programming Language fourth edition:引自 Bjarne Stroustrup 的The C++ Programming Language第四版的第39.4.6段:

Sometimes, the representation of characters stored in a file differs from the desired representation of those same characters in main memory.有时,存储在文件中的字符表示与主存储器中相同字符的所需表示不同。 ... the codecvt facet provides a mechanism for converting characters from one representation to another as they are read or written. ... codecvt facet 提供了一种机制,可以在读取或写入字符时将字符从一种表示形式转换为另一种表示形式。

The intended purpose was thus to use std::codecvt only for adapting characters between file (disk) and memory, which partly answers your question:因此,预期目的是仅使用std::codecvt来适应文件(磁盘)和内存之间的字符,这部分回答了您的问题:

Why is std::codecvt only used by file I/O streams?为什么 std::codecvt 仅用于文件 I/O 流?

From the docs we see that:文档中我们看到:

All file I/O operations performed through std::basic_fstream<CharT> use the std::codecvt<CharT, char, std::mbstate_t> facet of the locale imbued in the stream.通过std::basic_fstream<CharT>执行的所有文件 I/O 操作使用流中灌输的语言环境的std::codecvt<CharT, char, std::mbstate_t>方面。

Which then answers the question why std::ofstream (uses a file-based streambuffer) and std::cout ( linked to standard output FILE stream ) invokes std::codecvt .然后回答了为什么std::ofstream (使用基于文件的流std::codecvt )和std::cout链接到标准输出 FILE 流)调用std::codecvt

Now, to use the high-level std::ostream interface you need to provide an underlying streambuf .现在,要使用高级std::ostream接口,您需要提供底层streambuf The std::ofstream provides a filebuf and the std::ostringstream provides a stringbuf (which is not linked to the use of std::codecvt ). std::ofstream提供了一个filebuf ,而std::ostringstream提供了一个stringbuf (与std::codecvt的使用std::codecvt )。 See this post over the streams , which also highlights the following:请参阅关于流的这篇文章,其中还强调了以下内容:

...in the case of ofstream, there are also a few extra functions which forward to additional functions in the filebuf interface ...在ofstream的情况下,还有一些额外的功能可以转发到filebuf接口中的其他功能

But, to invoke the character conversion functionality of a std::codecvt when you have a std::ostringstream which is a std::ostream with an underlying std::basic_streambuf you can use, as indicated in your post, the std::wbuffer_convert .但是,当你有一个std::ostringstream时调用std::codecvt的字符转换功能,它是一个带有底层std::basic_streambufstd::ostream ,你可以使用,如你的帖子所示, std::wbuffer_convert

You have only used the std::wstring_convert in your second update and not the std::wbuffer_convert .您在第二次更新中只使用了std::wstring_convert而不是std::wbuffer_convert

When using the std::wbuffer_convert you can wrap the original std::ostringstream with a std::ostream as follows:使用std::wbuffer_convert您可以使用std::ostream包装原始std::ostringstream ,如下所示:

// Create a std::ostringstream
auto osstream = std::ostringstream{};

// Create the wrapper for the ostringstream
std::wbuffer_convert<custom_facet, char> wrapper(osstream.rdbuf());

// Now create a std::ostream which uses the wrapper to send data to
// the original std::ostringstream
std::ostream normal_ostream(&wrapper);
normal_ostream << "test\n";

// Flush the stream to invoke the conversion
normal_ostream << std::flush;

// Check the invocation_counter
std::cout << "invocation_counter after wrapping std::ostringstream with "
                "std::wbuffer_convert = "
            << invocation_counter << "\n";

Together with the complete example here , the output would be:连同这里的完整示例,输出将是:

invocation_counter start of test1 = 0
invocation_counter after std::ofstream = 1
> test printed to std::cout
invocation_counter after std::cout = 2
invocation_counter after std::ostringstream (should not have changed)= 2
ic after test1 = 2
invocation_counter after std::ostringstream with std::wstring_convert = 3
ic after test2 = 3
invocation_counter after wrapping std::ostringstream with std::wbuffer_convert = 4
ic after test3 = 4

Conclusion结论

std::codecvt was intended for converting between disk and memory representation . std::codecvt用于在磁盘和内存表示之间进行转换。 That is why the std::codecvt implementation is only called with streams using an underlying filebuf such as std::ofstream and std::cout .这就是为什么std::codecvt实现只使用基本流称为filebufstd::ofstreamstd::cout However, a stream using an underlying stringbuf can be wrapped using std::wbuffer_convert into a std::ostream instance which would then invoke the underlying std::codecvt .然而,使用底层流stringbuf可以使用包裹std::wbuffer_convertstd::ostream实例,它会然后调用底层std::codecvt

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

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