mirror of https://github.com/SeanOMik/libki.git
dml: Add XML input/output
<RECORD> elements can now be parsed. Tests need to be written for this.
This commit is contained in:
parent
881119e6af
commit
cad6f44208
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "FieldBase.h"
|
#include "FieldBase.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
{
|
{
|
||||||
|
@ -10,6 +11,7 @@ namespace dml
|
||||||
class Field final : public FieldBase
|
class Field final : public FieldBase
|
||||||
{
|
{
|
||||||
friend Record;
|
friend Record;
|
||||||
|
friend FieldBase;
|
||||||
public:
|
public:
|
||||||
virtual ~Field() = default;
|
virtual ~Field() = default;
|
||||||
|
|
||||||
|
@ -23,9 +25,86 @@ namespace dml
|
||||||
m_value = value;
|
m_value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *get_type_name() const final;
|
||||||
|
|
||||||
void write_to(std::ostream &ostream) const final;
|
void write_to(std::ostream &ostream) const final;
|
||||||
void read_from(std::istream &istream) final;
|
void read_from(std::istream &istream) final;
|
||||||
size_t get_size() const final;
|
size_t get_size() const final;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an XML node from this field's data.
|
||||||
|
*
|
||||||
|
* The document is only used to allocate necessary resources, and
|
||||||
|
* so the returned node has not been appended to the document.
|
||||||
|
*/
|
||||||
|
rapidxml::xml_node<> *as_xml(rapidxml::xml_document<> &doc) const final
|
||||||
|
{
|
||||||
|
// Create the node:
|
||||||
|
// Copy our current name and value into buffers that are
|
||||||
|
// lifetime-dependant on the xml_document, rather than this Field.
|
||||||
|
char *name_buffer = doc.allocate_string(m_name.c_str(), 0);
|
||||||
|
char *value_buffer = doc.allocate_string(get_value_string().c_str(), 0);
|
||||||
|
auto *node = doc.allocate_node(
|
||||||
|
rapidxml::node_type::node_element, name_buffer, value_buffer);
|
||||||
|
|
||||||
|
// Create the TYPE attribute
|
||||||
|
char *type_attr_value_buffer = doc.allocate_string(get_type_name(), 0);
|
||||||
|
auto *type_attr = doc.allocate_attribute("TYPE", type_attr_value_buffer);
|
||||||
|
node->append_attribute(type_attr);
|
||||||
|
|
||||||
|
// If we're not transferable, set NOXFER to TRUE
|
||||||
|
// NOXFER defaults to FALSE, so we don't need to write it otherwise.
|
||||||
|
if (!m_transferable)
|
||||||
|
{
|
||||||
|
auto *noxfer_attr = doc.allocate_attribute("NOXFER", "TRUE");
|
||||||
|
node->append_attribute(noxfer_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads data from an XML Field node into this field.
|
||||||
|
* Example: <FieldName TYPE="STR">Value</FieldName>
|
||||||
|
*
|
||||||
|
* If the field in the XML data does not have the same type
|
||||||
|
* as this field, then an exception is thrown.
|
||||||
|
*/
|
||||||
|
void from_xml(const rapidxml::xml_node<> *node) final
|
||||||
|
{
|
||||||
|
// Use the name of the node as the field name and,
|
||||||
|
// default transferable to TRUE.
|
||||||
|
m_name = node->name();
|
||||||
|
m_transferable = true;
|
||||||
|
|
||||||
|
for (auto *attr = node->first_attribute();
|
||||||
|
attr; attr = attr->next_attribute())
|
||||||
|
{
|
||||||
|
const std::string name = attr->name();
|
||||||
|
if (name != "TYPE")
|
||||||
|
{
|
||||||
|
const std::string value = attr->value();
|
||||||
|
if (value != get_type_name())
|
||||||
|
{
|
||||||
|
// TODO: Exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (name == "NOXFER")
|
||||||
|
{
|
||||||
|
const std::string value = attr->value();
|
||||||
|
m_transferable = value != "TRUE";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string value = node->value();
|
||||||
|
if (!value.empty())
|
||||||
|
set_value_from_string(value);
|
||||||
|
}
|
||||||
protected:
|
protected:
|
||||||
Field(std::string name, const Record &record)
|
Field(std::string name, const Record &record)
|
||||||
: FieldBase(name, record)
|
: FieldBase(name, record)
|
||||||
|
@ -47,6 +126,26 @@ namespace dml
|
||||||
clone->m_value = m_value;
|
clone->m_value = m_value;
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the value of another Field into this one
|
||||||
|
* if the types are the same.
|
||||||
|
*/
|
||||||
|
void set_value(FieldBase *other) final
|
||||||
|
{
|
||||||
|
if (other->is_type<ValueT>())
|
||||||
|
{
|
||||||
|
auto *real_other = dynamic_cast<Field<ValueT> *>(other);
|
||||||
|
set_value(real_other->get_value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_value_string() const;
|
||||||
|
void set_value_from_string(std::string value);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Field<BYT> BytField;
|
typedef Field<BYT> BytField;
|
||||||
|
@ -60,5 +159,32 @@ namespace dml
|
||||||
typedef Field<FLT> FltField;
|
typedef Field<FLT> FltField;
|
||||||
typedef Field<DBL> DblField;
|
typedef Field<DBL> DblField;
|
||||||
typedef Field<GID> GidField;
|
typedef Field<GID> GidField;
|
||||||
|
|
||||||
|
template <typename ValueT>
|
||||||
|
std::string Field<ValueT>::get_value_string() const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << m_value;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ValueT>
|
||||||
|
void Field<ValueT>::set_value_from_string(const std::string value)
|
||||||
|
{
|
||||||
|
std::istringstream iss(value);
|
||||||
|
iss >> m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string StrField::get_value_string() const;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void StrField::set_value_from_string(std::string value);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string WStrField::get_value_string() const;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void WStrField::set_value_from_string(std::string value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
#include <rapidxml.hpp>
|
||||||
#include "../util/Serializable.h"
|
#include "../util/Serializable.h"
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
|
@ -29,6 +30,29 @@ namespace dml
|
||||||
{
|
{
|
||||||
return (typeid(ValueT).hash_code() == m_type_hash);
|
return (typeid(ValueT).hash_code() == m_type_hash);
|
||||||
}
|
}
|
||||||
|
virtual const char *get_type_name() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an XML node from this field's data.
|
||||||
|
*
|
||||||
|
* The document is only used to allocate necessary resources, and
|
||||||
|
* so the returned node has not been appended to the document.
|
||||||
|
*/
|
||||||
|
virtual rapidxml::xml_node<> *as_xml(rapidxml::xml_document<> &doc) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads data from an XML Field node into this field.
|
||||||
|
* Example: <FieldName TYPE="STR">Value</FieldName>
|
||||||
|
*
|
||||||
|
* If the field in the XML data does not have the same type
|
||||||
|
* as this field, then an exception is thrown.
|
||||||
|
*/
|
||||||
|
virtual void from_xml(const rapidxml::xml_node<> *node) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Field from XML data.
|
||||||
|
*/
|
||||||
|
static FieldBase *create_from_xml(const Record& record, const rapidxml::xml_node<> *node);
|
||||||
protected:
|
protected:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
bool m_transferable;
|
bool m_transferable;
|
||||||
|
@ -41,6 +65,12 @@ namespace dml
|
||||||
* and value but with a different owner Record.
|
* and value but with a different owner Record.
|
||||||
*/
|
*/
|
||||||
virtual FieldBase *clone(const Record &record) const = 0;
|
virtual FieldBase *clone(const Record &record) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the value of another Field into this one
|
||||||
|
* if the types are the same.
|
||||||
|
*/
|
||||||
|
virtual void set_value(FieldBase *other) = 0;
|
||||||
private:
|
private:
|
||||||
const Record &m_record;
|
const Record &m_record;
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,6 +87,25 @@ namespace dml
|
||||||
void write_to(std::ostream &ostream) const final;
|
void write_to(std::ostream &ostream) const final;
|
||||||
void read_from(std::istream &istream) final;
|
void read_from(std::istream &istream) final;
|
||||||
size_t get_size() const final;
|
size_t get_size() const final;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an XML node from this record's data.
|
||||||
|
*
|
||||||
|
* The document is only used to allocate necessary resources, and
|
||||||
|
* so the returned node has not been appended to the document.
|
||||||
|
*/
|
||||||
|
rapidxml::xml_node<> *as_xml(rapidxml::xml_document<> &doc) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads data from an XML Record node into this record.
|
||||||
|
*
|
||||||
|
* If a Field already exists, and the type is the same,
|
||||||
|
* then the new value is copied into the original Field.
|
||||||
|
* If the type is not the same, then the original Field
|
||||||
|
* is deleted, and replaced with the new one.
|
||||||
|
*/
|
||||||
|
void from_xml(rapidxml::xml_node<> *node);
|
||||||
private:
|
private:
|
||||||
FieldList m_fields;
|
FieldList m_fields;
|
||||||
FieldNameMap m_field_map;
|
FieldNameMap m_field_map;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "ki/dml/FieldBase.h"
|
#include "ki/dml/FieldBase.h"
|
||||||
|
#include "ki/dml/Field.h"
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
{
|
{
|
||||||
|
@ -26,5 +27,48 @@ namespace dml
|
||||||
{
|
{
|
||||||
return m_transferable;
|
return m_transferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FieldBase* FieldBase::create_from_xml(const Record& record, const rapidxml::xml_node<>* node)
|
||||||
|
{
|
||||||
|
auto *type_attr = node->first_attribute("TYPE");
|
||||||
|
if (!type_attr)
|
||||||
|
{
|
||||||
|
// TODO: Exceptions
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const std::string type = type_attr->value();
|
||||||
|
|
||||||
|
FieldBase *field;
|
||||||
|
if (type == "BYT")
|
||||||
|
field = new BytField("", record);
|
||||||
|
else if (type == "UBYT")
|
||||||
|
field = new UBytField("", record);
|
||||||
|
else if (type == "SHRT")
|
||||||
|
field = new ShrtField("", record);
|
||||||
|
else if (type == "USHRT")
|
||||||
|
field = new UShrtField("", record);
|
||||||
|
else if (type == "INT")
|
||||||
|
field = new IntField("", record);
|
||||||
|
else if (type == "UINT")
|
||||||
|
field = new UIntField("", record);
|
||||||
|
else if (type == "STR")
|
||||||
|
field = new StrField("", record);
|
||||||
|
else if (type == "WSTR")
|
||||||
|
field = new WStrField("", record);
|
||||||
|
else if (type == "FLT")
|
||||||
|
field = new FltField("", record);
|
||||||
|
else if (type == "DBL")
|
||||||
|
field = new DblField("", record);
|
||||||
|
else if (type == "GID")
|
||||||
|
field = new GidField("", record);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Exceptions
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
field->from_xml(node);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,10 @@ namespace dml
|
||||||
|
|
||||||
Record::~Record()
|
Record::~Record()
|
||||||
{
|
{
|
||||||
m_fields.clear();
|
|
||||||
m_field_map.clear();
|
|
||||||
for (auto it = m_fields.begin(); it != m_fields.end(); ++it)
|
for (auto it = m_fields.begin(); it != m_fields.end(); ++it)
|
||||||
delete *it;
|
delete *it;
|
||||||
|
m_fields.clear();
|
||||||
|
m_field_map.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Record::Record(const Record& record)
|
Record::Record(const Record& record)
|
||||||
|
@ -78,5 +78,57 @@ namespace dml
|
||||||
m_fields.push_back(field);
|
m_fields.push_back(field);
|
||||||
m_field_map.insert({ field->get_name(), field });
|
m_field_map.insert({ field->get_name(), field });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rapidxml::xml_node<> *Record::as_xml(rapidxml::xml_document<> &doc) const
|
||||||
|
{
|
||||||
|
auto *node = doc.allocate_node(rapidxml::node_type::node_element, "RECORD");
|
||||||
|
for (auto it = m_fields.begin(); it != m_fields.end(); ++it)
|
||||||
|
node->append_node((*it)->as_xml(doc));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Record::from_xml(rapidxml::xml_node<> *node)
|
||||||
|
{
|
||||||
|
// Make sure that we've been passed a <RECORD> element.
|
||||||
|
const std::string node_name = node->name();
|
||||||
|
if (node_name != "RECORD")
|
||||||
|
{
|
||||||
|
// TODO: Exceptions
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every child node inside a <RECORD> element is a Field.
|
||||||
|
for (auto *field_node = node->first_node();
|
||||||
|
field_node; field_node = field_node->next_sibling())
|
||||||
|
{
|
||||||
|
FieldBase *field = FieldBase::create_from_xml(*this, field_node);
|
||||||
|
if (has_field(field->get_name()))
|
||||||
|
{
|
||||||
|
// Is the old field the same type as the one created from
|
||||||
|
// the XML data?
|
||||||
|
FieldBase *old_field = m_field_map.at(field->get_name());
|
||||||
|
if (field->m_type_hash == old_field->m_type_hash)
|
||||||
|
{
|
||||||
|
// Set the value of the old field to the value of the new
|
||||||
|
// one.
|
||||||
|
old_field->set_value(field);
|
||||||
|
delete field;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Since the types are different, we can't set the value
|
||||||
|
// of the old field to the value of the new one so,
|
||||||
|
// replace the old field with this new one instead.
|
||||||
|
ptrdiff_t index = std::find(
|
||||||
|
m_fields.begin(), m_fields.end(), old_field) - m_fields.begin();
|
||||||
|
m_fields[index] = field;
|
||||||
|
m_field_map[field->get_name()] = field;
|
||||||
|
delete old_field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
add_field(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,5 +26,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(BYT);
|
return sizeof(BYT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* BytField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "BYT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -35,5 +35,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(DBL);
|
return sizeof(DBL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* DblField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "DBL";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,5 +35,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(FLT);
|
return sizeof(FLT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* FltField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "FLT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(GID);
|
return sizeof(GID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* GidField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "GID";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(INT);
|
return sizeof(INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* IntField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "INT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(USHRT);
|
return sizeof(USHRT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* ShrtField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "SHRT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,5 +40,23 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(USHRT) + m_value.length();
|
return sizeof(USHRT) + m_value.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* StrField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "STR";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string StrField::get_value_string() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void StrField::set_value_from_string(std::string value)
|
||||||
|
{
|
||||||
|
m_value = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,5 +26,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(BYT);
|
return sizeof(BYT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* UBytField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "UBYT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(UINT);
|
return sizeof(UINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* UIntField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "UINT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,11 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(USHRT);
|
return sizeof(USHRT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* UShrtField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "USHRT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "ki/dml/Field.h"
|
#include "ki/dml/Field.h"
|
||||||
#include "ki/util/ValueBytes.h"
|
#include "ki/util/ValueBytes.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
{
|
{
|
||||||
|
@ -46,5 +47,37 @@ namespace dml
|
||||||
{
|
{
|
||||||
return sizeof(USHRT) + (m_value.length() * sizeof(char16_t));
|
return sizeof(USHRT) + (m_value.length() * sizeof(char16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* WStrField::get_type_name() const
|
||||||
|
{
|
||||||
|
return "WSTR";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string WStrField::get_value_string() const
|
||||||
|
{
|
||||||
|
#if _MSC_VER >= 1900
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
|
||||||
|
auto p = reinterpret_cast<const int16_t *>(m_value.data());
|
||||||
|
STR temp = convert.to_bytes(p, p + m_value.size());
|
||||||
|
#else
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
|
STR temp = convert.to_bytes(m_value);
|
||||||
|
#endif
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void WStrField::set_value_from_string(std::string value)
|
||||||
|
{
|
||||||
|
#if _MSC_VER >= 1900
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
|
||||||
|
m_value = reinterpret_cast<const char16_t *>(convert.from_bytes(value).data());
|
||||||
|
#else
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
|
m_value = convert.from_bytes(value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue