Я написал пользовательский std::basic_streambuf
и std::basic_ostream
потому что я хочу, чтобы поток вывода мог получить строку JNI таким же образом, как вы можете вызвать std::ostringstream::str()
. Эти классы довольно просты.
namespace myns {
class jni_utf16_streambuf : public std::basic_streambuf<char16_t>
{
JNIEnv * d_env;
std::vector<char16_t> d_buf;
virtual int_type overflow(int_type);
public:
jni_utf16_streambuf(JNIEnv *);
jstring jstr() const;
};
typedef std::basic_ostream<char16_t, std::char_traits<char16_t>> utf16_ostream;
class jni_utf16_ostream : public utf16_ostream
{
jni_utf16_streambuf d_buf;
public:
jni_utf16_ostream(JNIEnv *);
jstring jstr() const;
};
// ...
} // namespace myns
Кроме того, я сделал четыре перегрузки operator<<
, все в том же пространстве имен:
namespace myns {
// ...
utf16_ostream& operator<<(utf16_ostream&, jstring) throw(std::bad_cast);
utf16_ostream& operator<<(utf16_ostream&, const char *);
utf16_ostream& operator<<(utf16_ostream&, const jni_utf16_string_region&);
jni_utf16_ostream& operator<<(jni_utf16_ostream&, jstring);
// ...
} // namespace myns
Реализация jni_utf16_streambuf::overflow(int_type)
тривиальна. Он просто удваивает ширину буфера, помещает запрошенный символ и правильно устанавливает базовые, позиционные и конечные указатели. Он проверен, и я уверен, что он работает.
jni_utf16_ostream
отлично работает с вставкой символов юникода. Например, это прекрасно работает и приводит к потоку, содержащему "привет, мир":
myns::jni_utf16_ostream o(env);
o << u"hello, wor" << u'l' << u'd';
Моя проблема заключается в том, как только я попытаюсь вставить целочисленное значение, бит потока бит устанавливается, например:
myns::jni_utf16_ostream o(env);
if (o.badbit()) throw "bad bit before"; // does not throw
int32_t x(5);
o << x;
if (o.badbit()) throw "bad bit after"; // throws :(
Я не понимаю, почему это происходит! Есть ли какой-нибудь другой метод на std::basic_streambuf
мне нужно реализовать????
Похоже, что ответ заключается в том, что поддержка char16_t
частично реализована в GCC 4.8. Заголовки библиотеки не устанавливают графы, необходимые для преобразования чисел. Вот что говорит об этом проект Boost.Locale:
GNU GCC 4.5/C++ 0x Статус
Компилятор GNU C++ обеспечивает достойную поддержку символов C++ 0x:
Стандартная библиотека не устанавливает никаких std :: locale :: facets для этой поддержки, поэтому любая попытка форматирования чисел с использованием потоков char16_t или char32_t просто терпит неудачу. Стандартная библиотека пропускает специализацию для требуемых факулов char16_t/char32_t, поэтому "std" backend не может быть создан в качестве незаменимых символов, а также не может быть создан также грань codecvt.
Visual Studio 2010 (MSVC10)/C++ 0x Статус
Однако MSVC предоставляет все необходимые грани:
Стандартная библиотека не предоставляет установки std :: locale :: id для этих граней в DLL, поэтому она не может использоваться с флагами компилятора /MD,/MDd и требует статической ссылки библиотеки времени выполнения. char16_t и char32_t не являются отдельными типами, а скорее псевдонимами беззнаковых коротких и неподписанных типов, что противоречит требованиям C++ 0x, что делает невозможным запись char16_t/char32_t в поток и вызывает множественные ошибки.
o << (char16_t)x; // works