2015-09-19 124 views
1

我已经从std::basic_stringstream<typename TString::value_type...>得到了一个模板类,如你所见。试图转换它们时发生问题。这可能是一个明显的问题,但我似乎无法找出解决方案。自定义Stringstream - 转换std :: wstring&std :: string

main为例,我有一个简单的std::wstring并用L"123"对它初始化。
在构建std::wstring之后,调用自定义类basic_stringstream的操作员(取决于std::wstringstd::string)。

检查WCStringStream对象以进行调试,显示它包含 - 而不是字符串L"123",它是输入字符串的第一个元素的地址。功能to_bytesfrom_bytes做返回正确的转换后的字符串,那么剩下的唯一问题是,运营商被称为在两个运营商的功能:

*this << std::wstring_convert<...>().xx_bytes(s); 

例子:
模板类std::wstring
输入是std::string
&operator<<(const std::string &s)正在被调用。
转换字符串。
&operator<<(const std::wstring &s)正在被调用。
字符串类型与模板类型匹配。
调用基类(basic_stringstream)的运算符。 (或者std::operator...

结果:
检查:{_Stringbuffer={_Seekhigh=0x007f6808 L"003BF76C췍췍췍췍췍췍췍췍췍...}...}
WCStringStream<std::wstring>::str()- >"003BF76C"

预期结果:
"123"

这里怎么回事?


#define WIN32_LEAN_AND_MEAN 
#define NOMINMAX 
#include <Windows.h> 
#include <iostream> 
#include <sstream> 
#include <codecvt> 

template<class TString> 
class WCStringStream : public std::basic_stringstream<typename TString::value_type, 
    std::char_traits<typename TString::value_type>, 
    std::allocator<typename TString::value_type> > 
{ 
    typedef typename TString::value_type CharTraits; 
    typedef std::basic_stringstream<CharTraits, std::char_traits<CharTraits>, std::allocator<CharTraits> > MyStream; 
    //more typedefs... 

public: 
    //Constructor... 
    inline WCStringStream(void) { } 
    inline WCStringStream(const TString &s) : MyStream(s) { } 
    //and more... 
    //operator>> overloads... 
    //defines for VS2010/2015 (C++11) included 

    inline WCStringStream &operator<<(const std::wstring &s) 
    { 
     if (typeid(TString) == typeid(s)) 
      MyStream::operator<<(s.c_str()); 
     else 
      *this << std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(s); 
     return *this; 
    } 

    inline WCStringStream &operator<<(const std::string &s) 
    { 
     if (typeid(TString) == typeid(s)) 
      MyStream::operator<<(s.c_str()); 
     else 
      *this << std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(s); 
     return *this; 
    } 
}; 

//Example main 
int main(int argc, char *argv[]) 
{ 
    typedef std::wstring fstring; 

    WCStringStream<std::wstring> ws; 
    WCStringStream<std::string> ss; 

    ws << fstring(L"123"); 
    int a = 0; 
    ws >> a; 
    std::cout << a << std::endl; 

    ss << fstring(L"123"); 
    int b = 0; 
    ss >> b; 
    std::cout << b << std::endl; 

    return 0; 
} 

我目前正在编纂VS2015,但我需要它VS2010太运行。

+0

我可以发誓VS2010/2015s股票'stringstream'已经为你做了这个 – Mgetz

+0

我不完全清楚你的描述中的实际问题是什么。我的第一个想法是,你应该看看模板专业化[http://www.cprogramming.com/tutorial/template_specialization.html] –

+0

@Mgetz有'std :: stringstream'和'std :: wstringstream',我想将它们与这里未显示的附加功能“组合”。 – Blacktempel

回答

1

问题是显式调用基类操作符,该操作符将const void *_Val重载并打印地址。

MyStream::operator<<(s.c_str()); 

的解决问题的办法:

if (typeid(TString) == typeid(s)) 
{ 
    MyStream &os = *this; 
    os << s.c_str(); 
} 

过程调用*this << s.c_str()导致递归,但使用的基类,它调用正确的char类型wchar_t/char全球重载操作。

一个也在工作的解决方案是使用成员函数write而不是操作符。

2

第一关:我觉得重载格式化功能在基类中的方法是不明智强烈建议不做!我意识到任何替代方案都需要更多的工作。

事实上,我认为你的首要问题其实是,你达到你的重载函数反正只是显示的方式是多么的脆弱(我认为字符串描述什么超载最终被调用,但我避风港”牛逼证实这些的确是正确的,部分原因是因为在这个问题提供的代码缺少必要的上下文):

WCStringStream<std::string> stream; 
stream << "calls std::operator<< (std::ostream&, char const*)\n"; 
stream << L"calls std::ostream::operator<< (void const*)\n"; 
stream << std::string("calls std::operator<< (std::ostream&, T&&)\n"; 
std::string const s("calls your operator\n"); 
stream << s; 

由于重载输出操作符的字符串和字符串文字不能被改变,他们做了错误的就代码思考rsions,我推荐使用一种完全不同的方法,尽管它仍然不会没有危险(*):虽然使用比标准提供的代码更精美的打包版本,但显式转换字符串。

假设始终使用char作为所有用途的字符类型,我将使用函数wcvt(),该函数在将所有字符串和字符串文字插入到流中时调用。由于在调用函数时它不知道要使用的流的类型,因此它基本上会返回对字符序列的引用,然后对字符串所使用的字符类型进行适当转换。这将是沿着这些路线的东西:

template <typename cT> 
class wconvert { 
    cT const* begin_; 
    cT const* end_; 
public: 
    wconvert(std::basic_string<cT> const& s) 
     : begin_(s.data()) 
     , end_(s.data() + s.size()) { 
    } 
    wconvert(cT const* s) 
    : begin_(s) 
    , end_(s + std::char_traits<cT>::length(s)) { 
    } 
    cT const* begin() const { return this->begin_; } 
    cT const* end() const { return this->end_; } 
    std::streamsize size() const { return this->end_ - this->begin_; } 
}; 

template <typename cT> 
wconvert<cT> wcvt(cT const* s) { 
    return wconvert<cT>(s); 
} 
template <typename cT> 
wconvert<cT> wcvt(std::basic_string<cT> const& s) { 
    return wconvert<cT>(s); 
} 

template <typename cT> 
std::basic_ostream<cT>& operator<< (std::basic_ostream<cT>& out, 
            wconvert<cT> const& cvt) { 
    return out.write(cvt.begin(), cvt.size()); 
} 

std::ostream& operator<< (std::ostream& out, wconvert<wchar_t> const& cvt) { 
    auto tmp = std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(cvt.begin(), cvt.end()); 
    return out.write(tmp.data(), tmp.size()); 
} 

std::wostream& operator<< (std::wostream& out, wconvert<char> const& cvt) { 
    auto tmp = std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(cvt.begin(), cvt.end()); 
    return out.write(tmp.data(), tmp.size()); 
} 

当然,使用这种方法需要使用wcvt(s)每当s可能需要被转换为字符串。很容易忘记这样做,它似乎最初的目标是而不是必须记住使用这种转换。但是,我看不到任何替代方案对于现有流的系统来说不那么脆弱。完全放弃使用流并使用完全独立的格式化I/O系统可能会产生较不脆弱的方法。

(*)最简单的方法是在程序中坚持字符类型,并使用此字符类型始终使用。我确实相信引入第二个字符类型wchar_t实际上是一个错误,并且它还引入了char16_tchar32_t,使现有的混乱更加复杂,这是一个更大的错误。尽管它实际上不会代表字符,但是代码的字节,我们会好得多,只有一种字符类型,即char

+0

首先,谢谢你的回答。这不是任何基类,它是一个没有任何进一步需要继承的类。我已经更新了这个问题,以便更清楚地说明,我认为'* this <<'操作符被调用是显而易见的,这些函数在类中,但它似乎不是。 - 最简单的方法是在程序中使用一个字符,尽管有一些外部库和函数不支持Unicode(还)。事实上,最初的目标是,不检查(在这个类之外)它是否是'std :: wstring'或'std :: string'。 – Blacktempel