2011-03-03 19:57:34 +00:00
|
|
|
|
#ifndef MYSQLXX_STRING_H
|
|
|
|
|
#define MYSQLXX_STRING_H
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <time.h>
|
2014-02-21 01:57:01 +00:00
|
|
|
|
#include <math.h>
|
2011-03-03 19:57:34 +00:00
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
2011-03-05 20:47:46 +00:00
|
|
|
|
#include <Yandex/Common.h>
|
2011-03-03 19:57:34 +00:00
|
|
|
|
#include <Yandex/DateLUT.h>
|
|
|
|
|
|
|
|
|
|
#include <mysqlxx/Types.h>
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Обрезать длинный запрос до указанной длины для текста исключения.
|
2011-03-22 18:29:37 +00:00
|
|
|
|
#define MYSQLXX_QUERY_PREVIEW_LENGTH 1000
|
2011-03-03 19:57:34 +00:00
|
|
|
|
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
namespace mysqlxx
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
class ResultBase;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Представляет одно значение, считанное из MySQL.
|
|
|
|
|
* Объект сам не хранит данные, а является всего лишь обёрткой над парой (const char *, size_t).
|
|
|
|
|
* Если уничтожить UseQueryResult/StoreQueryResult или Connection,
|
|
|
|
|
* или считать следующий Row при использовании UseQueryResult, то объект станет некорректным.
|
|
|
|
|
* Позволяет преобразовать значение (распарсить) в различные типы данных:
|
|
|
|
|
* - с помощью функций вида getUInt(), getString(), ... (рекомендуется);
|
|
|
|
|
* - с помощью шаблонной функции get<Type>(), которая специализирована для многих типов (для шаблонного кода);
|
|
|
|
|
* - шаблонная функция get<Type> работает также для всех типов, у которых есть конструктор из String
|
|
|
|
|
* (это сделано для возможности расширения);
|
|
|
|
|
* - с помощью operator Type() - но этот метод реализован лишь для совместимости и не рекомендуется
|
|
|
|
|
* к использованию, так как неудобен (часто возникают неоднозначности).
|
|
|
|
|
*
|
|
|
|
|
* При ошибке парсинга, выкидывается исключение.
|
|
|
|
|
* При попытке достать значение, которое равно NULL, выкидывается исключение
|
|
|
|
|
* - используйте метод isNull() для проверки.
|
|
|
|
|
*
|
|
|
|
|
* Во всех распространённых системах, time_t - это всего лишь typedef от Int64 или Int32.
|
|
|
|
|
* Для того, чтобы можно было писать row[0].get<time_t>(), ожидая, что значение вида '2011-01-01 00:00:00'
|
|
|
|
|
* корректно распарсится согласно текущей тайм-зоне, сделано так, что метод getUInt и соответствующие методы get<>()
|
|
|
|
|
* также умеют парсить дату и дату-время.
|
|
|
|
|
*/
|
2011-03-03 19:57:34 +00:00
|
|
|
|
class String
|
|
|
|
|
{
|
|
|
|
|
public:
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/** Параметр res_ используется только для генерации подробной информации в исключениях.
|
|
|
|
|
* Можно передать NULL - тогда подробной информации в исключениях не будет.
|
|
|
|
|
*/
|
|
|
|
|
String(const char * data_, size_t length_, const ResultBase * res_) : m_data(data_), m_length(length_), res(res_)
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить значение bool.
|
2011-03-04 20:58:19 +00:00
|
|
|
|
bool getBool() const
|
|
|
|
|
{
|
2011-03-05 16:56:30 +00:00
|
|
|
|
if (unlikely(isNull()))
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Value is NULL");
|
2011-03-05 16:56:30 +00:00
|
|
|
|
|
2011-03-04 20:58:19 +00:00
|
|
|
|
return m_length > 0 && m_data[0] != '0';
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить беззнаковое целое.
|
2011-03-04 20:58:19 +00:00
|
|
|
|
UInt64 getUInt() const
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
2011-03-05 16:56:30 +00:00
|
|
|
|
if (unlikely(isNull()))
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Value is NULL");
|
2011-03-05 16:56:30 +00:00
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
return readUIntText(m_data, m_length);;
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить целое со знаком или дату или дату-время (в unix timestamp согласно текущей тайм-зоне).
|
2011-03-04 20:58:19 +00:00
|
|
|
|
Int64 getInt() const
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
2011-03-10 20:31:02 +00:00
|
|
|
|
return getIntOrDateTime();
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить число с плавающей запятой.
|
2011-03-04 20:58:19 +00:00
|
|
|
|
double getDouble() const
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
2011-03-05 16:56:30 +00:00
|
|
|
|
if (unlikely(isNull()))
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Value is NULL");
|
2011-03-05 16:56:30 +00:00
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
return readFloatText(m_data, m_length);
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить дату-время (из значения вида '2011-01-01 00:00:00').
|
2011-03-10 20:31:02 +00:00
|
|
|
|
DateTime getDateTime() const
|
|
|
|
|
{
|
|
|
|
|
return DateTime(data(), size());
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить дату (из значения вида '2011-01-01' или '2011-01-01 00:00:00').
|
2011-03-10 20:31:02 +00:00
|
|
|
|
Date getDate() const
|
|
|
|
|
{
|
|
|
|
|
return Date(data(), size());
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Получить строку.
|
2011-03-10 20:31:02 +00:00
|
|
|
|
std::string getString() const
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
2011-03-05 16:56:30 +00:00
|
|
|
|
if (unlikely(isNull()))
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Value is NULL");
|
2011-03-05 16:56:30 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
return std::string(m_data, m_length);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Является ли NULL.
|
2011-03-10 20:31:02 +00:00
|
|
|
|
bool isNull() const
|
|
|
|
|
{
|
|
|
|
|
return m_data == NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Для совместимости (используйте вместо этого метод isNull())
|
2011-03-10 20:31:02 +00:00
|
|
|
|
bool is_null() const { return isNull(); }
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/** Получить любой поддерживаемый тип (для шаблонного кода).
|
|
|
|
|
* Поддерживаются основные типы, а также любые типы с конструктором от String (для удобства расширения).
|
|
|
|
|
*/
|
2011-03-10 20:31:02 +00:00
|
|
|
|
template <typename T> T get() const;
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
/// Для совместимости. Не рекомендуется к использованию, так как неудобен (часто возникают неоднозначности).
|
2011-03-10 20:31:02 +00:00
|
|
|
|
template <typename T> operator T() const { return get<T>(); }
|
|
|
|
|
|
|
|
|
|
const char * data() const { return m_data; }
|
|
|
|
|
size_t length() const { return m_length; }
|
|
|
|
|
size_t size() const { return m_length; }
|
2011-11-29 18:53:44 +00:00
|
|
|
|
bool empty() const { return 0 == m_length; }
|
2011-03-10 20:31:02 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const char * m_data;
|
|
|
|
|
size_t m_length;
|
2011-03-18 20:26:54 +00:00
|
|
|
|
const ResultBase * res;
|
2011-03-10 20:31:02 +00:00
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
bool checkDateTime() const
|
|
|
|
|
{
|
|
|
|
|
return (m_length == 10 || m_length == 19) && m_data[4] == '-' && m_data[7] == '-';
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
time_t getDateTimeImpl() const
|
|
|
|
|
{
|
2013-08-11 03:40:14 +00:00
|
|
|
|
DateLUTSingleton & date_lut = DateLUTSingleton::instance();
|
2011-03-10 20:31:02 +00:00
|
|
|
|
|
2011-03-03 19:57:34 +00:00
|
|
|
|
if (m_length == 10)
|
|
|
|
|
{
|
|
|
|
|
return date_lut.makeDate(
|
2011-03-15 21:14:05 +00:00
|
|
|
|
(m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'),
|
|
|
|
|
(m_data[5] - '0') * 10 + (m_data[6] - '0'),
|
|
|
|
|
(m_data[8] - '0') * 10 + (m_data[9] - '0'));
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
2011-03-18 20:26:54 +00:00
|
|
|
|
else if (m_length == 19)
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
|
|
|
|
return date_lut.makeDateTime(
|
2011-03-15 21:14:05 +00:00
|
|
|
|
(m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'),
|
|
|
|
|
(m_data[5] - '0') * 10 + (m_data[6] - '0'),
|
|
|
|
|
(m_data[8] - '0') * 10 + (m_data[9] - '0'),
|
|
|
|
|
(m_data[11] - '0') * 10 + (m_data[12] - '0'),
|
|
|
|
|
(m_data[14] - '0') * 10 + (m_data[15] - '0'),
|
|
|
|
|
(m_data[17] - '0') * 10 + (m_data[18] - '0'));
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
2011-03-18 20:26:54 +00:00
|
|
|
|
else
|
|
|
|
|
throwException("Cannot parse DateTime");
|
|
|
|
|
|
|
|
|
|
return 0; /// чтобы не было warning-а.
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
time_t getDateImpl() const
|
2011-03-05 16:56:30 +00:00
|
|
|
|
{
|
2013-08-11 03:40:14 +00:00
|
|
|
|
DateLUTSingleton & date_lut = DateLUTSingleton::instance();
|
2011-03-05 16:56:30 +00:00
|
|
|
|
|
|
|
|
|
if (m_length == 10 || m_length == 19)
|
|
|
|
|
{
|
|
|
|
|
return date_lut.makeDate(
|
2011-03-15 21:14:05 +00:00
|
|
|
|
(m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'),
|
|
|
|
|
(m_data[5] - '0') * 10 + (m_data[6] - '0'),
|
|
|
|
|
(m_data[8] - '0') * 10 + (m_data[9] - '0'));
|
2011-03-05 16:56:30 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Cannot parse Date");
|
2013-01-06 16:07:01 +00:00
|
|
|
|
|
|
|
|
|
return 0; /// чтобы не было warning-а.
|
2011-03-05 16:56:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
Int64 getIntImpl() const
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
2011-03-18 20:26:54 +00:00
|
|
|
|
return readIntText(m_data, m_length);;
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
Int64 getIntOrDateTime() const
|
2011-03-05 16:56:30 +00:00
|
|
|
|
{
|
2011-03-10 20:31:02 +00:00
|
|
|
|
if (unlikely(isNull()))
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Value is NULL");
|
2011-03-03 19:57:34 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
if (checkDateTime())
|
|
|
|
|
return getDateTimeImpl();
|
|
|
|
|
else
|
|
|
|
|
return getIntImpl();
|
|
|
|
|
}
|
2011-03-03 19:57:34 +00:00
|
|
|
|
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
2011-03-10 20:31:02 +00:00
|
|
|
|
Int64 getIntOrDate() const
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
2011-03-10 20:31:02 +00:00
|
|
|
|
if (unlikely(isNull()))
|
2011-03-18 20:26:54 +00:00
|
|
|
|
throwException("Value is NULL");
|
2011-03-10 20:31:02 +00:00
|
|
|
|
|
|
|
|
|
if (checkDateTime())
|
|
|
|
|
return getDateImpl();
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-08-11 03:40:14 +00:00
|
|
|
|
DateLUTSingleton & date_lut = DateLUTSingleton::instance();
|
2011-03-10 20:31:02 +00:00
|
|
|
|
return date_lut.toDate(getIntImpl());
|
|
|
|
|
}
|
2011-03-03 19:57:34 +00:00
|
|
|
|
}
|
2011-03-18 20:26:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Прочитать беззнаковое целое в простом формате из не-0-terminated строки.
|
|
|
|
|
UInt64 readUIntText(const char * buf, size_t length) const
|
|
|
|
|
{
|
|
|
|
|
UInt64 x = 0;
|
|
|
|
|
const char * end = buf + length;
|
|
|
|
|
|
|
|
|
|
while (buf != end)
|
|
|
|
|
{
|
|
|
|
|
switch (*buf)
|
|
|
|
|
{
|
|
|
|
|
case '+':
|
|
|
|
|
break;
|
|
|
|
|
case '0':
|
|
|
|
|
case '1':
|
|
|
|
|
case '2':
|
|
|
|
|
case '3':
|
|
|
|
|
case '4':
|
|
|
|
|
case '5':
|
|
|
|
|
case '6':
|
|
|
|
|
case '7':
|
|
|
|
|
case '8':
|
|
|
|
|
case '9':
|
|
|
|
|
x *= 10;
|
|
|
|
|
x += *buf - '0';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throwException("Cannot parse unsigned integer");
|
|
|
|
|
}
|
|
|
|
|
++buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Прочитать знаковое целое в простом формате из не-0-terminated строки.
|
|
|
|
|
Int64 readIntText(const char * buf, size_t length) const
|
|
|
|
|
{
|
|
|
|
|
bool negative = false;
|
|
|
|
|
Int64 x = 0;
|
|
|
|
|
const char * end = buf + length;
|
|
|
|
|
|
|
|
|
|
while (buf != end)
|
|
|
|
|
{
|
|
|
|
|
switch (*buf)
|
|
|
|
|
{
|
|
|
|
|
case '+':
|
|
|
|
|
break;
|
|
|
|
|
case '-':
|
|
|
|
|
negative = true;
|
|
|
|
|
break;
|
|
|
|
|
case '0':
|
|
|
|
|
case '1':
|
|
|
|
|
case '2':
|
|
|
|
|
case '3':
|
|
|
|
|
case '4':
|
|
|
|
|
case '5':
|
|
|
|
|
case '6':
|
|
|
|
|
case '7':
|
|
|
|
|
case '8':
|
|
|
|
|
case '9':
|
|
|
|
|
x *= 10;
|
|
|
|
|
x += *buf - '0';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throwException("Cannot parse signed integer");
|
|
|
|
|
}
|
|
|
|
|
++buf;
|
|
|
|
|
}
|
|
|
|
|
if (negative)
|
|
|
|
|
x = -x;
|
|
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки.
|
|
|
|
|
double readFloatText(const char * buf, size_t length) const
|
|
|
|
|
{
|
|
|
|
|
bool negative = false;
|
|
|
|
|
double x = 0;
|
|
|
|
|
bool after_point = false;
|
|
|
|
|
double power_of_ten = 1;
|
|
|
|
|
const char * end = buf + length;
|
|
|
|
|
|
|
|
|
|
while (buf != end)
|
|
|
|
|
{
|
|
|
|
|
switch (*buf)
|
|
|
|
|
{
|
|
|
|
|
case '+':
|
|
|
|
|
break;
|
|
|
|
|
case '-':
|
|
|
|
|
negative = true;
|
|
|
|
|
break;
|
|
|
|
|
case '.':
|
|
|
|
|
after_point = true;
|
|
|
|
|
break;
|
|
|
|
|
case '0':
|
|
|
|
|
case '1':
|
|
|
|
|
case '2':
|
|
|
|
|
case '3':
|
|
|
|
|
case '4':
|
|
|
|
|
case '5':
|
|
|
|
|
case '6':
|
|
|
|
|
case '7':
|
|
|
|
|
case '8':
|
|
|
|
|
case '9':
|
|
|
|
|
if (after_point)
|
|
|
|
|
{
|
|
|
|
|
power_of_ten /= 10;
|
|
|
|
|
x += (*buf - '0') * power_of_ten;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
x *= 10;
|
|
|
|
|
x += *buf - '0';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'e':
|
|
|
|
|
case 'E':
|
|
|
|
|
{
|
|
|
|
|
++buf;
|
|
|
|
|
Int32 exponent = readIntText(buf, end - buf);
|
2014-02-21 02:15:18 +00:00
|
|
|
|
x *= exp10(exponent);
|
|
|
|
|
if (negative)
|
|
|
|
|
x = -x;
|
2014-02-21 15:56:42 +00:00
|
|
|
|
return x;
|
2011-03-18 20:26:54 +00:00
|
|
|
|
}
|
|
|
|
|
case 'i':
|
|
|
|
|
case 'I':
|
|
|
|
|
x = std::numeric_limits<double>::infinity();
|
|
|
|
|
if (negative)
|
|
|
|
|
x = -x;
|
|
|
|
|
return x;
|
|
|
|
|
case 'n':
|
|
|
|
|
case 'N':
|
|
|
|
|
x = std::numeric_limits<double>::quiet_NaN();
|
|
|
|
|
return x;
|
|
|
|
|
default:
|
|
|
|
|
throwException("Cannot parse floating point number");
|
|
|
|
|
}
|
|
|
|
|
++buf;
|
|
|
|
|
}
|
|
|
|
|
if (negative)
|
|
|
|
|
x = -x;
|
|
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Выкинуть исключение с подробной информацией
|
|
|
|
|
void throwException(const char * text) const;
|
2011-03-03 19:57:34 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2011-03-05 16:56:30 +00:00
|
|
|
|
template <> inline bool String::get<bool >() const { return getBool(); }
|
|
|
|
|
template <> inline char String::get<char >() const { return getInt(); }
|
|
|
|
|
template <> inline signed char String::get<signed char >() const { return getInt(); }
|
|
|
|
|
template <> inline unsigned char String::get<unsigned char >() const { return getUInt(); }
|
|
|
|
|
template <> inline short String::get<short >() const { return getInt(); }
|
|
|
|
|
template <> inline unsigned short String::get<unsigned short >() const { return getUInt(); }
|
|
|
|
|
template <> inline int String::get<int >() const { return getInt(); }
|
|
|
|
|
template <> inline unsigned int String::get<unsigned int >() const { return getUInt(); }
|
|
|
|
|
template <> inline long String::get<long >() const { return getInt(); }
|
|
|
|
|
template <> inline unsigned long String::get<unsigned long >() const { return getUInt(); }
|
|
|
|
|
template <> inline long long String::get<long long >() const { return getInt(); }
|
|
|
|
|
template <> inline unsigned long long String::get<unsigned long long >() const { return getUInt(); }
|
|
|
|
|
template <> inline float String::get<float >() const { return getDouble(); }
|
|
|
|
|
template <> inline double String::get<double >() const { return getDouble(); }
|
|
|
|
|
template <> inline std::string String::get<std::string >() const { return getString(); }
|
2011-03-10 20:31:02 +00:00
|
|
|
|
template <> inline Date String::get<Date >() const { return getDate(); }
|
|
|
|
|
template <> inline DateTime String::get<DateTime >() const { return getDateTime(); }
|
2011-03-04 20:58:19 +00:00
|
|
|
|
|
2013-08-11 03:40:14 +00:00
|
|
|
|
template <> inline VisitID_t String::get<VisitID_t >() const { return VisitID_t(getUInt()); }
|
2011-03-05 20:47:46 +00:00
|
|
|
|
|
|
|
|
|
template <typename T> inline T String::get() const { return T(*this); }
|
|
|
|
|
|
2011-03-04 20:58:19 +00:00
|
|
|
|
|
|
|
|
|
inline std::ostream & operator<< (std::ostream & ostr, const String & x)
|
2011-03-03 19:57:34 +00:00
|
|
|
|
{
|
|
|
|
|
return ostr.write(x.data(), x.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|