2015-09-19 2 views
1

У меня есть класс шаблона, полученный из std::basic_stringstream<typename TString::value_type...>, как вы можете видеть. Проблема возникает при попытке их преобразования. Вероятно, это очевидная проблема, хотя я не могу понять решение.Пользовательский Stringstream - Преобразование std :: wstring & std :: string

Как пример в main, у меня есть простой std::wstring и инициализируйте его L"123".
После того, как был сконструирован std::wstring, оператор пользовательского класса basic_stringstream (в зависимости от std::wstring или std::string).

Проверка объекта WCStringStream для целей отладки показывает, что он содержит - вместо строки L"123", адрес первого элемента введенной строки. Функции to_bytes и from_bytes сделать возвращать правильный преобразованную строку, поэтому единственной проблемой является оператором называют в обоих оператор-функций:

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

Пример:
Шаблон класса является std::wstring.
Ввод std::string.
&operator<<(const std::string &s) называется.
Строка преобразуется.
&operator<<(const std::wstring &s) называется.
Сопряжения типа String с типом шаблона.
Вызывается оператор базового класса (basic_stringstream). (Или std::operator...)

Результат:
Inspecting: {_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 stock '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() результатов в рекурсии, но с использованием базового класса, он вызывает глобальный перегруженный оператор для правильного полукокса типа 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; 

с перегруженными выходными операторами для строк и строковых литералов не может быть изменен, и они делают неправильный думаю относительно кода conve 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 может быть строка, которая должна быть преобразована. Легко забыть об этом, и, похоже, первоначальная цель состояла в том, чтобы не должны помнить об использовании такого преобразования. Однако я не вижу альтернативы, которая менее хрупка с системой существующих потоков. В целом отказ от использования потоков и использование полностью отдельной системы форматированных ввода-вывода могут обеспечить менее хрупкий подход.

(*) Самый простой способ получить право - придерживаться только символьного типа в программе и всегда, используя этот тип символа. Я действительно полагаю, что на самом деле было ошибкой ввести второй тип символов, wchar_t, и это еще большая ошибка, чтобы еще больше усложнить существующий беспорядок, также представив char16_t и char32_t. Нам было бы намного лучше, был только один тип символа, char, хотя на самом деле он не будет представлять символ, а байты кодировки.

+0

Прежде всего, спасибо за ваш ответ. Это не какой-либо базовый класс, это класс без каких-либо дальнейших потребностей наследовать от него. Я обновил вопрос, чтобы быть более ясным во всем, я думал, что вызываемый оператор '* this <<' был очевиден, что функции находятся внутри класса, но похоже, что это не так. - Да проще всего придерживаться одного символа в программе, хотя есть некоторые внешние библиотеки и функции, которые не поддерживают Unicode (пока). В самом деле, первоначальная цель - не проверять (вне этого класса), если это 'std :: wstring' или' std :: string'. – Blacktempel

Смежные вопросы