提问人:Jason1923 提问时间:8/30/2023 更新时间:9/5/2023 访问量:111
在头文件中设置 std::boolalpha 标志?
Set std::boolalpha flag in header file?
问:
是否可以在头文件中设置?我的用例是在需要时轻松跨文件进行打印调试。我可以将它们放在同一个标题中,并打开我的重载,而不是在 .std::boolalpha
std::ostream& operator<<
main()
答:
如何从生产代码中注销,并在其他地方进行日志记录机制的格式化和选择的示例。
#include <iostream>
#include <string>
// Header file my_reporting_itf.h
struct my_reporting_itf
{
// logging my never change the state of the object
// and must not throw (logging infrastructure error should be handled and hidden)
virtual void report_input_out_of_range(const std::string& value_name, int value) const noexcept = 0;
};
// Header file and C++ file std_cout_reporting_t.h and std_cout_reporting_t.cpp
// include my_reporting_itf.h
// in the cpp file you can use your
struct std_cout_reporting_t final :
public my_reporting_itf
{
// forward to logging infrastructure of your choice
void report_input_out_of_range(const std::string& value_name, int value) const noexcept override
{
// here is where you manage your logging formatting, and things like std::boolalpha
// the setting could be a flag in std_cout_reporting_t's constructor
std::cout << "Value : `" << value_name << "` is out of range, value = " << value << "\n";
}
};
// Header file and C++ file null_reporting_t.h and null_reporting_t.cpp
// include my_reporting_itf.h
struct null_reporting_t :
public my_reporting_itf
{
// for unit testing do absolutely nothing
void report_input_out_of_range(const std::string& value_name, int value) const noexcept override
{
}
};
// myclass.cpp and myclass.h
// A class with logging like this is unit testable
// in the header file for my_class_t you only have to include header file with my_reporting
// include my_reporting_itf.h (not dependend on any logging infrastructure
class my_class_t
{
public:
explicit my_class_t(const my_reporting_itf& report) :
m_report{ report }
{
};
void set_value(int value)
{
if (value < 0)
{
m_report.report_input_out_of_range("value", value);
}
}
private:
const my_reporting_itf& m_report;
};
// main.cpp
// here you include my_class_t.h, and std_cout_reporting_t.h
int main()
{
std_cout_reporting_t report;
// null_reporting_t report; for unit testing inject this (you can also inject a google_mock to test
// if report functions are actually called when needed)
my_class_t foo{ report };
// out of range will result in a report
foo.set_value(-1);
return 0;
}
标准库中的示例
全局对象(如 和 )的问题就是这样。它们是全球性的。当有人递给你一个,并说,请用它做一些流 I/O 时,你永远不能假设它们已经以任何特定的方式进行了配置。std::cin
std::cout
对于其他流对象(全局或其他对象)也是如此。您不能总是假设流具有所需的配置。
因此,最好创建某种 RAII 保护类,以便根据需要使用该类来配置 I/O 流。您可以在 ctor 中配置流,并在 dtor 中恢复以前的配置。
例如,请考虑 C++ 标准库中对随机数引擎和随机数分布的要求。对于这些对象,并且需要在执行任何 I/O 之前以某种特定方式配置其流。退出时,操作员必须将流还原到其原始配置。operator<<
operator>>
为了满足这些要求,我编写了以下 RAII 类。
namespace tbx
{
template <typename charT, typename traits>
class configure_ostream_for_random_engine
{
// RAII "guard" class sets ostream fmtflags and fill character
// as required by section 26.6.2.4 of the C++ standard.
// Destructor restores original fmtflags and fill character.
std::basic_ostream<charT, traits>& ost_;
std::ios_base::fmtflags const flags_;
charT const fill_character_;
public:
configure_ostream_for_random_engine(
std::basic_ostream<charT, traits>& ost)
: ost_{ ost }
, flags_{ ost.flags(std::ios_base::dec | std::ios_base::left) }
, fill_character_{ ost.fill(ost.widen(' ')) }
{}
~configure_ostream_for_random_engine()
{
ost_.flags(flags_);
ost_.fill(fill_character_);
}
private:
configure_ostream_for_random_engine(
configure_ostream_for_random_engine const&)
= delete;
configure_ostream_for_random_engine(
configure_ostream_for_random_engine&&)
= delete;
configure_ostream_for_random_engine& operator= (
configure_ostream_for_random_engine const&)
= delete;
configure_ostream_for_random_engine& operator= (
configure_ostream_for_random_engine&&)
= delete;
};
}
流输入有一个类似的类:。tbx::configure_istream_for_random_engine
编写流插入从配置对象开始。operator<<
这个例子来自类,其中被定义为“隐藏的朋友”。这就是为什么您将“朋友”视为以下定义的一部分。tbx::permuted_congruential_engine
operator<<
template <typename charT, typename traits>
friend auto operator<<
( std::basic_ostream<charT, traits>& ost
, permuted_congruential_engine const& e
) -> std::basic_ostream<charT, traits>&
{
tbx::configure_ostream_for_random_engine<charT, traits>
raii_guard(ost);
// Output engine state
//...
// On exit, original stream configuration is automatically
// restored by the dtor of raii_guard.
}
一般解决方案
您可以对其他流执行类似操作。
在下面的类中,ctor 建立 C++ 标准库中输出流使用的默认配置。这些都记录在函数中。请参阅 CppReference。tbx::configure_ostream
std::basic_ios<CharT,Traits>::init
然后,它更进一步,通过设置 .boolalpha
namespace tbx
{
template< typename charT, typename traits >
class configure_ostream
{
std::basic_ostream<charT, traits>& ost_;
std::streamsize const width_;
std::streamsize const precision_;
std::ios_base::fmtflags const flags_;
charT const fill_character_;
public:
configure_ostream(std::basic_ostream<charT, traits>& ost)
: ost_{ ost }
, width_{ ost.width(0) }
, precision_{ ost.precision(6) }
, flags_{ ost.flags(std::ios_base::dec | std::ios_base::skipws) }
, fill_character_{ ost.fill(ost.widen(' ')) }
{
// The defaults established above are the same as those
// set by function std::basic_ios<CharT,Traits>::init.
// https://en.cppreference.com/w/cpp/io/basic_ios/init
//
// Additional settings follow:
ost.setf(std::ios_base::boolalpha);
}
~configure_ostream()
{
ost_.width(width_);
ost_.precision(precision_);
ost_.flags(flags_);
ost_.fill(fill_character_);
}
private:
configure_ostream(configure_ostream const&)
= delete;
configure_ostream(configure_ostream&&)
= delete;
configure_ostream& operator= (configure_ostream const&)
= delete;
configure_ostream& operator= (configure_ostream&&)
= delete;
};
}
为了演示它的用法,我编写了 struct ,它定义为隐藏的好友。A
operator<<
struct A
{
int value{ 42 };
bool flag{ true };
friend auto operator<< (std::ostream& ost, A const& a)
-> std::ostream&
{
// This sets boolalpha, plus whatever other
// settings you put in class configure_ostream.
tbx::configure_ostream raii_guard(ost); // ***LOOK HERE***
ost << "value : " << a.value
<< "\nflag : " << a.flag
<< "\n\n";
return ost;
}
};
函数有一堆乱七八糟的设置,但 struct 忽略了它们。main
std::cout
A
int main()
{
// Set a bunch of random stream configurations.
std::cout
<< std::noboolalpha
<< std::left
<< std::hex
<< std::setfill('*')
<< std::setw(20);
// A{} ignores the random settings above,
// but "main" uses them.
std::cout << A{}
<< "main"
<< "\n42 decimal as hex : " << 42
<< "\nfalse : " << false
<< "\n\n";
return 0;
}
输出如下:
value : 42
flag : true
main****************
42 decimal as hex : 2a
false : 0
评论
std::boolalpha