【问题标题】:How does one write a stream insertion operator for the general case? (That is, for both `char` and `wchar_t` streams?)如何为一般情况编写流插入运算符? (也就是说,对于 `char` 和 `wchar_t` 流?)
【发布时间】:2010-12-28 19:27:06
【问题描述】:

我正在为我的一个班级实施stream insertion operator。我希望我的班级能够处理窄流和宽流。我正在使用模板来允许这种行为——除了字符文字之外,一切都与实际使用的流类型无关。如果是宽字符串,字符文字需要在文字前面加上L,否则不需要。

有没有办法将这种东西键入模板参数,这样我就不需要在这上面复制太多代码了?

(如果可能,我宁愿避免在运行时执行窄到宽字符或宽到窄字符的转换。)

我目前拥有的示例——它是一个模板,但由于宽字符文字,它不适用于窄字符流:

template <typename charT, typename traits>
std::basic_ostream<charT, traits>& operator<<(
    std::basic_ostream<charT, traits>& lhs,
    const Process& rhs
    )
{
    lhs << L"Process (0x" << std::setw(8) << std::hex
        << std::setfill(L'0') << rhs.GetId() << L") ";
    lhs << rhs.GetName() << std::endl;
    lhs << L"Command Line: " << rhs.GetCmdLine() << std::endl;
    const std::vector<Thread>& threads = rhs.GetThreads();
    for (std::vector<Thread>::const_iterator it = threads.begin(); 
        it != threads.end(); ++it)
    {
        lhs << L" --> " << *it << std::endl;
    }
    const std::map<void *, Module>& modules = rhs.GetModules();
    for (std::map<void *, Module>::const_iterator it = modules.begin(); 
        it != modules.end(); ++it)
    {
        lhs << L" --> " << it->second << std::endl;
    }
    return lhs;
}

【问题讨论】:

标签: c++ templates unicode iostream


【解决方案1】:

如果您不想要运行时开销,我认为虽然,但在这种情况下宏可以帮助您。

template <typename T>
inline const T* select(const char* narrow, const wchar_t* wide);

template <>
inline const char* select<char>(const char* narrow, const wchar_t* /*wide*/)
{
    return narrow;
}

template <>
inline const wchar_t* select<wchar_t>(const char* /*narrow*/, const wchar_t* wide)
{
    return wide;
}

#define doselect(T, str) select<T>(str, L ## str)

template <typename charT, typename traits>
std::basic_ostream<charT, traits>& operator<<(
    std::basic_ostream<charT, traits>& lhs,
    const Process& rhs
    )
{
    lhs << doselect(charT, "Process (0x") << std::setw(8) << std::hex
        << std::setfill(charT('0')) << rhs.GetId() << doselect(charT, ") ");
    lhs << rhs.GetName() << std::endl;
    lhs << doselect(charT, "Command Line: ") << rhs.GetCmdLine() << std::endl;
    const std::vector<Thread>& threads = rhs.GetThreads();
    for (std::vector<Thread>::const_iterator it = threads.begin(); 
        it != threads.end(); ++it)
    {
        lhs << doselect(charT, " --> ") << *it << std::endl;
    }
    const std::map<void *, Module>& modules = rhs.GetModules();
    for (std::map<void *, Module>::const_iterator it = modules.begin(); 
        it != modules.end(); ++it)
    {
        lhs << doselect(charT, " --> ") << it->second << std::endl;
    }
    return lhs;
}

您可以使用另一个不错的宏扩展doselect,以进一步减少代码重复。即doselect2(" --&gt; ") 会自动扩展为doselect(charT, " --&gt; ")

【讨论】:

    【解决方案2】:

    我所做的只是插入字符,并将它们转换为宽字符或窄字符。转换是在编译期间完成的,如果您只使用宽窄编码中相等的字符,它确实有效。这很烦人,但确实有效。

    template <typename charT, typename traits>
    std::basic_ostream<charT, traits>& operator<<(
        std::basic_ostream<charT, traits>& lhs,
        const Process& rhs
        )
    {
        lhs << charT('P') << charT('r') << charT('o') << charT('c') << charT('e')
            << charT('s') << charT('s') << charT(' ') << charT('(') << charT('0')
            << charT('x') << std::setw(8) << std::hex << std::setfill(charT('0'))
            << rhs.GetId() << charT(')') << charT(' ');
        lhs << rhs.GetName() << std::endl;
        lhs << charT('C') << charT('o') << charT('m') << charT('m') << charT('a')
            << charT('n') << charT('d') << charT(' ') << charT('L') << charT('i')
            << charT('n') << charT('e') << charT(':') << charT(' ')
            << rhs.GetCmdLine() << std::endl;
        const std::vector<Thread>& threads = rhs.GetThreads();
        for (std::vector<Thread>::const_iterator it = threads.begin(); 
            it != threads.end(); ++it)
        {
            lhs << charT(' ') << charT('-') << charT('-') << charT('>') << charT(' ')
                << *it << std::endl;
        }
        const std::map<void *, Module>& modules = rhs.GetModules();
        for (std::map<void *, Module>::const_iterator it = modules.begin(); 
            it != modules.end(); ++it)
        {
            lhs << charT(' ') << charT('-') << charT('-') << charT('>') << charT(' ')
                << it->second << std::endl;
        }
        return lhs;
    }
    

    如果您有很多字符串,您可以使用另一个模板,您将专注于宽或窄字符类型并用于存储字符串。然而,这将迫使您复制您的字符串(并且您将不符合 DRY 原则)。

    template <typename charT>
    struct ProcessInsertionOperatorHelper
    {
        static const charT* const String1;
        static const charT* const String2;
        static const charT* const String3;
        static const charT* const String4;
    };
    
    template <>
    const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String1 = L"Process (0x";
    template <>
    const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String2 = L") ";
    template <>
    const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String3 = L"Command Line: ";
    template <>
    const wchar_t* const ProcessInsertionOperatorHelper<wchar_t>::String4 = L" --> ";
    
    template <>
    struct ProcessInsertionOperatorHelper<char>
    {
    };
    
    template <typename charT, typename traits>
    std::basic_ostream<charT, traits>& operator<<(
        std::basic_ostream<charT, traits>& lhs,
        const Process& rhs
        )
    {
        lhs << ProcessInsertionOperatorHelper<charT>::String1 << std::setw(8)
            << std::hex << std::setfill(L'0') << rhs.GetId()
            << ProcessInsertionOperatorHelper<charT>::String2;
        lhs << rhs.GetName() << std::endl;
        lhs << ProcessInsertionOperatorHelper<charT>::String3
            << rhs.GetCmdLine() << std::endl;
        const std::vector<Thread>& threads = rhs.GetThreads();
        for (std::vector<Thread>::const_iterator it = threads.begin(); 
            it != threads.end(); ++it)
        {
            lhs << ProcessInsertionOperatorHelper<charT>::String4
                << *it << std::endl;
        }
        const std::map<void *, Module>& modules = rhs.GetModules();
        for (std::map<void *, Module>::const_iterator it = modules.begin(); 
            it != modules.end(); ++it)
        {
            lhs << ProcessInsertionOperatorHelper<charT>::String4
                << it->second << std::endl;
        }
        return lhs;
    }
    

    【讨论】:

    • 是的,这很难看。我也更喜欢@dalle 解决方案。
    猜你喜欢
    • 1970-01-01
    • 2017-07-09
    • 2012-08-10
    • 1970-01-01
    • 2012-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    相关资源
    最近更新 更多