【发布时间】:2021-03-06 17:27:01
【问题描述】:
我一直在实现一个编解码器来处理输出流的缩进。它可以像这样使用并且工作正常:
std::cout << indenter::push << "im indentet" << indenter::pop << "\n im not..."
然而,虽然我可以将std::codecvt 注入任何std::ostream,但当我发现我的代码可以与std::cout 和std::ofstream 一起工作时,我感到非常困惑,但对于std::ostringstream 甚至都不能而所有这些都继承自基类std::ostream。
facet构造正常,代码编译,不抛出异常……只是std::codecvt的成员函数没有被调用。
这对我来说非常令人困惑,我不得不花费大量时间弄清楚 std::codecvt 不会对非文件 I/O 流执行任何操作。
std::codecvt 没有被 std::ostream 继承的所有类使用有什么原因吗?
此外,有没有人知道我可以依靠哪些结构来实现缩进器?
编辑:这是我所指的语言的一部分:
通过 std::basic_fstream 执行的所有文件 I/O 操作都使用流中包含的语言环境的 std::codecvt
方面。
来源:https://en.cppreference.com/w/cpp/locale/codecvt
更新 1:
我做了一个小例子来说明我的问题:
#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";
}
除了invocation_counter 之外,我会在std::ostringstream 流式传输后增加,但事实并非如此。
更新 2:
经过更多研究,我发现我可以使用std::wbuffer_converter。引用https://en.cppreference.com/w/cpp/locale/wbuffer_convert
std::wbuffer_convert是流缓冲区类型的包装器std::basic_streambuf<char>这使它看起来像std::basic_streambuf<Elem>。所有 I/O 通过std::wbuffer_convert经历由定义的字符转换 方面编解码器。 [...]这个类模板进行隐式字符转换
std::basic_filebuf的功能可用于任何std::basic_streambuf.
这样我可以将构面应用于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());
但是,我无法使用流式运算符 << 连接构面。
这让我更加困惑,为什么 std::codecvt 不是所有输出流都隐式使用的。所有输出流都继承自std::basic_streambuf,其接口适合使用std::codecvt,只是使用输入和输出字符序列,完全在std::basic_streambuf中实现。
那么为什么std::codecvt的解析是在std::basic_filebuf而不是std::basic_streambuf中实现的呢?毕竟std::basic_filebuf 继承了std::basic_streambuf...
要么我对流在 C++ 中的工作方式有一些基本的误解,要么std::codecvt 在标准中的集成很差。也许这就是它被标记为已弃用的原因?
【问题讨论】:
-
我不知道方面的恶作剧,但也许只使用
std::format而完全忘记 iostreams? -
@PasserBy 我曾考虑过使用
std::format,但使用流的优势在于,它可以使用它获得的任何流。我在我的 json 序列化程序中使用缩进器,它能够使用从std::ostream派生的对象的引用写入任何输出流。这样我就可以序列化为std::ofstream、std::ostringstream或std::cout。使用std::format我会失去这种灵活性,因为序列化程序会递归调用每个对象成员进行序列化。 -
C++98 的 std::codecvt 没有被弃用,只有从它派生的 C++11 的 Unicode 转换语言环境被弃用。
标签: c++ file facet ostream codecvt