在头文件中设置 std::boolalpha 标志?

Set std::boolalpha flag in header file?

提问人:Jason1923 提问时间:8/30/2023 更新时间:9/5/2023 访问量:111

问:

是否可以在头文件中设置?我的用例是在需要时轻松跨文件进行打印调试。我可以将它们放在同一个标题中,并打开我的重载,而不是在 .std::boolalphastd::ostream& operator<<main()

C++ IOstream

评论

2赞 Some programmer dude 8/30/2023
如果你想在你的程序中打印“调试”,我建议你研究一下许多可用的日志框架之一。虽然它不会解决您的问题,但它将帮助您完成日志记录任务,包括调试和非调试信息。
1赞 Some programmer dude 8/30/2023
至于我建议您阅读例如此参考并了解有关流状态的更多信息。std::boolalpha
1赞 Pepijn Kramer 8/30/2023
如果使用它的是类或函数模板,您可能可以。但通常使用抽象基类来记录,并使用依赖项注入将接口注入到日志记录中。或者,最好使用报表方法定义一个虚拟基类,并进行一个转发到当天的日志记录系统(适配器)的实现,并将该接口注入到您自己的代码中(日志记录基础结构在您自己的代码中根本不可见)。这也将使单元测试变得更加容易。

答:

1赞 Pepijn Kramer 8/30/2023 #1

如何从生产代码中注销,并在其他地方进行日志记录机制的格式化和选择的示例。

#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;
}
1赞 tbxfreeware 8/30/2023 #2
标准库中的示例

全局对象(如 和 )的问题就是这样。它们是全球性的。当有人递给你一个,并说,请用它做一些流 I/O 时,你永远不能假设它们已经以任何特定的方式进行了配置。std::cinstd::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_engineoperator<<

        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++ 标准库中输出流使用的默认配置。这些都记录在函数中。请参阅 CppReferencetbx::configure_ostreamstd::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 ,它定义为隐藏的好友。Aoperator<<

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 忽略了它们。mainstd::coutA

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