[英]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::ostream
类std::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
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_counter
在std::ostringstream
式传输后增加,但事实并非如此。
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 typestd::basic_streambuf<char>
which gives it the appearance ofstd::basic_streambuf<Elem>
.std::wbuffer_convert
是std::basic_streambuf<char>
类型的流缓冲区的包装器,它赋予它std::basic_streambuf<Elem>
的外观。 All I/O performed throughstd::wbuffer_convert
undergoes character conversion as defined by the facet Codecvt.通过
std::wbuffer_convert
执行的所有 I/Ostd::wbuffer_convert
经历由构面 Codecvt 定义的字符转换。 [...][...]
This class template makes the implicit character conversion functionality of
std::basic_filebuf
available for anystd::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 thestd::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_streambuf
的std::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
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
实现只使用基本流称为filebuf
如std::ofstream
和std::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_convert
成std::ostream
实例,它会然后调用底层std::codecvt
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.