Skip to content

Commit

Permalink
Refactored enum serialization/deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSWolf committed Apr 17, 2016
1 parent 44d18d0 commit 8d2d100
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 266 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ set(SOURCE_FILES
src/Phone.cpp src/Phone.h
src/Shape.cpp src/Shape.h
src/centiseconds.cpp src/centiseconds.h
src/enumTools.h
src/EnumConverter.h
src/mouthAnimation.cpp src/mouthAnimation.h
src/phoneExtraction.cpp src/phoneExtraction.h
src/platformTools.cpp src/platformTools.h
Expand Down
99 changes: 99 additions & 0 deletions src/EnumConverter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#pragma once

#include <initializer_list>
#include <utility>
#include <map>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <format.h>

template<typename TEnum>
class EnumConverter {
public:
EnumConverter() :
initialized(false)
{}

virtual ~EnumConverter() = default;

virtual boost::optional<std::string> tryToString(TEnum value) {
initialize();
auto it = valueToNameMap.find(value);
return it != valueToNameMap.end()
? it->second
: boost::optional<std::string>();
}

std::string toString(TEnum value) {
initialize();
auto result = tryToString(value);
if (!result) {
auto numericValue = static_cast<typename std::underlying_type<TEnum>::type>(value);
throw std::invalid_argument(fmt::format("{} is not a valid {} value.", numericValue, typeName));
}

return result.value();
}

virtual boost::optional<TEnum> tryParse(const std::string& s) {
initialize();
auto it = lowerCaseNameToValueMap.find(boost::algorithm::to_lower_copy(s));
return it != lowerCaseNameToValueMap.end()
? it->second
: boost::optional<TEnum>();
}

TEnum parse(const std::string& s) {
initialize();
auto result = tryParse(s);
if (!result) {
throw std::invalid_argument(fmt::format("{} is not a valid {} value.", s, typeName));
}

return result.value();
}

std::ostream& write(std::ostream& stream, TEnum value) {
return stream << toString(value);
}

std::istream& read(std::istream& stream, TEnum& value) {
std::string name;
stream >> name;
value = parse(name);
return stream;
}

const std::vector<TEnum>& getValues() {
initialize();
return values;
}

protected:
using member_data = std::vector<std::pair<TEnum, std::string>>;

virtual std::string getTypeName() = 0;
virtual member_data getMemberData() = 0;

private:
void initialize() {
if (initialized) return;

typeName = getTypeName();
for (const auto& pair : getMemberData()) {
TEnum value = pair.first;
std::string name = pair.second;
lowerCaseNameToValueMap[boost::algorithm::to_lower_copy(name)] = value;
valueToNameMap[value] = name;
values.push_back(value);
}
initialized = true;
}

bool initialized;
std::string typeName;
std::map<std::string, TEnum> lowerCaseNameToValueMap;
std::map<TEnum, std::string> valueToNameMap;
std::vector<TEnum> values;
};
36 changes: 15 additions & 21 deletions src/Exporter.cpp
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
#include "Exporter.h"
#include <logging.h>
#include <vector>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <tools.h>

using std::string;
using boost::property_tree::ptree;
using std::vector;
using std::tuple;
using std::make_tuple;

template <>
const string& getEnumTypeName<ExportFormat>() {
static const string name = "ExportFormat";
return name;

ExportFormatConverter& ExportFormatConverter::get() {
static ExportFormatConverter converter;
return converter;
}

string ExportFormatConverter::getTypeName() {
return "ExportFormat";
}

template <>
const vector<tuple<ExportFormat, string>>& getEnumMembers<ExportFormat>() {
static const vector<tuple<ExportFormat, string>> values = {
make_tuple(ExportFormat::TSV, "TSV"),
make_tuple(ExportFormat::XML, "XML"),
make_tuple(ExportFormat::JSON, "JSON")
EnumConverter<ExportFormat>::member_data ExportFormatConverter::getMemberData() {
return member_data{
{ ExportFormat::TSV, "TSV" },
{ ExportFormat::XML, "XML" },
{ ExportFormat::JSON, "JSON" }
};
return values;
}

std::ostream& operator<<(std::ostream& stream, ExportFormat value) {
return stream << enumToString(value);
return ExportFormatConverter::get().write(stream, value);
}

std::istream& operator>>(std::istream& stream, ExportFormat& value) {
string name;
stream >> name;
value = parseEnum<ExportFormat>(name);
return stream;
return ExportFormatConverter::get().read(stream, value);
}

// Makes sure there is at least one mouth shape
Expand Down
12 changes: 7 additions & 5 deletions src/Exporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ enum class ExportFormat {
JSON
};

template<>
const std::string& getEnumTypeName<ExportFormat>();

template<>
const std::vector<std::tuple<ExportFormat, std::string>>& getEnumMembers<ExportFormat>();
class ExportFormatConverter : public EnumConverter<ExportFormat> {
public:
static ExportFormatConverter& get();
protected:
std::string getTypeName() override;
member_data getMemberData() override;
};

std::ostream& operator<<(std::ostream& stream, ExportFormat value);

Expand Down
116 changes: 55 additions & 61 deletions src/Phone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,72 @@
#include "Phone.h"

using std::string;
using std::vector;
using std::tuple;
using std::make_tuple;

template <>
const string& getEnumTypeName<Phone>() {
static const string name = "Shape";
return name;
PhoneConverter& PhoneConverter::get() {
static PhoneConverter converter;
return converter;
}

template <>
const vector<tuple<Phone, string>>& getEnumMembers<Phone>() {
static const vector<tuple<Phone, string>> values = {
make_tuple(Phone::None, "None"),
make_tuple(Phone::Unknown, "Unknown"),
make_tuple(Phone::AO, "AO"),
make_tuple(Phone::AA, "AA"),
make_tuple(Phone::IY, "IY"),
make_tuple(Phone::UW, "UW"),
make_tuple(Phone::EH, "EH"),
make_tuple(Phone::IH, "IH"),
make_tuple(Phone::UH, "UH"),
make_tuple(Phone::AH, "AH"),
make_tuple(Phone::AE, "AE"),
make_tuple(Phone::EY, "EY"),
make_tuple(Phone::AY, "AY"),
make_tuple(Phone::OW, "OW"),
make_tuple(Phone::AW, "AW"),
make_tuple(Phone::OY, "OY"),
make_tuple(Phone::ER, "ER"),
make_tuple(Phone::P, "P"),
make_tuple(Phone::B, "B"),
make_tuple(Phone::T, "T"),
make_tuple(Phone::D, "D"),
make_tuple(Phone::K, "K"),
make_tuple(Phone::G, "G"),
make_tuple(Phone::CH, "CH"),
make_tuple(Phone::JH, "JH"),
make_tuple(Phone::F, "F"),
make_tuple(Phone::V, "V"),
make_tuple(Phone::TH, "TH"),
make_tuple(Phone::DH, "DH"),
make_tuple(Phone::S, "S"),
make_tuple(Phone::Z, "Z"),
make_tuple(Phone::SH, "SH"),
make_tuple(Phone::ZH, "ZH"),
make_tuple(Phone::HH, "HH"),
make_tuple(Phone::M, "M"),
make_tuple(Phone::N, "N"),
make_tuple(Phone::NG, "NG"),
make_tuple(Phone::L, "L"),
make_tuple(Phone::R, "R"),
make_tuple(Phone::Y, "Y"),
make_tuple(Phone::W, "W")
string PhoneConverter::getTypeName() {
return "Phone";
}

EnumConverter<Phone>::member_data PhoneConverter::getMemberData() {
return member_data{
{ Phone::None, "None" },
{ Phone::Unknown, "Unknown" },
{ Phone::AO, "AO" },
{ Phone::AA, "AA" },
{ Phone::IY, "IY" },
{ Phone::UW, "UW" },
{ Phone::EH, "EH" },
{ Phone::IH, "IH" },
{ Phone::UH, "UH" },
{ Phone::AH, "AH" },
{ Phone::AE, "AE" },
{ Phone::EY, "EY" },
{ Phone::AY, "AY" },
{ Phone::OW, "OW" },
{ Phone::AW, "AW" },
{ Phone::OY, "OY" },
{ Phone::ER, "ER" },
{ Phone::P, "P" },
{ Phone::B, "B" },
{ Phone::T, "T" },
{ Phone::D, "D" },
{ Phone::K, "K" },
{ Phone::G, "G" },
{ Phone::CH, "CH" },
{ Phone::JH, "JH" },
{ Phone::F, "F" },
{ Phone::V, "V" },
{ Phone::TH, "TH" },
{ Phone::DH, "DH" },
{ Phone::S, "S" },
{ Phone::Z, "Z" },
{ Phone::SH, "SH" },
{ Phone::ZH, "ZH" },
{ Phone::HH, "HH" },
{ Phone::M, "M" },
{ Phone::N, "N" },
{ Phone::NG, "NG" },
{ Phone::L, "L" },
{ Phone::R, "R" },
{ Phone::Y, "Y" },
{ Phone::W, "W" }
};
return values;
}

template<>
Phone parseEnum(const string& s) {
boost::optional<Phone> PhoneConverter::tryParse(const string& s) {
if (s == "SIL") return Phone::None;
Phone result;
return tryParseEnum(s, result) ? result : Phone::Unknown;
auto result = EnumConverter<Phone>::tryParse(s);
return result ? result : Phone::Unknown;
}

std::ostream& operator<<(std::ostream& stream, Phone value) {
return stream << enumToString(value);
return PhoneConverter::get().write(stream, value);
}

std::istream& operator>>(std::istream& stream, Phone& value) {
string name;
stream >> name;
value = parseEnum<Phone>(name);
return stream;
return PhoneConverter::get().read(stream, value);
}
19 changes: 10 additions & 9 deletions src/Phone.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "enumTools.h"
#include "EnumConverter.h"

// Defines a subset of the Arpabet
enum class Phone {
Expand Down Expand Up @@ -71,14 +71,15 @@ enum class Phone {
W // [w] as in [w]ay
};

template<>
const std::string& getEnumTypeName<Phone>();

template<>
const std::vector<std::tuple<Phone, std::string>>& getEnumMembers<Phone>();

template<>
Phone parseEnum(const std::string& s);
class PhoneConverter : public EnumConverter<Phone> {
public:
static PhoneConverter& get();
protected:
std::string getTypeName() override;
member_data getMemberData() override;
public:
boost::optional<Phone> tryParse(const std::string& s) override;
};

std::ostream& operator<<(std::ostream& stream, Phone value);

Expand Down
Loading

0 comments on commit 8d2d100

Please sign in to comment.