Refactored enum serialization/deserialization

This commit is contained in:
Daniel Wolf 2016-04-14 22:14:20 +02:00
parent 44d18d00f8
commit 8d2d100376
13 changed files with 246 additions and 263 deletions

View File

@ -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

99
src/EnumConverter.h Normal file
View File

@ -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;
};

View File

@ -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;
}
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")
string ExportFormatConverter::getTypeName() {
return "ExportFormat";
}
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

View File

@ -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);

View File

@ -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);
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "enumTools.h"
#include "EnumConverter.h"
// Defines a subset of the Arpabet
enum class Phone {
@ -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);

View File

@ -1,40 +1,33 @@
#include "Shape.h"
#include <tuple>
using std::string;
using std::vector;
using std::tuple;
using std::make_tuple;
template <>
const string& getEnumTypeName<Shape>() {
static const string name = "Shape";
return name;
ShapeConverter& ShapeConverter::get() {
static ShapeConverter converter;
return converter;
}
template <>
const vector<tuple<Shape, string>>& getEnumMembers<Shape>() {
static const vector<tuple<Shape, string>> values = {
make_tuple(Shape::A, "A"),
make_tuple(Shape::B, "B"),
make_tuple(Shape::C, "C"),
make_tuple(Shape::D, "D"),
make_tuple(Shape::E, "E"),
make_tuple(Shape::F, "F"),
make_tuple(Shape::G, "G"),
make_tuple(Shape::H, "H")
string ShapeConverter::getTypeName() {
return "Shape";
}
EnumConverter<Shape>::member_data ShapeConverter::getMemberData() {
return member_data{
{ Shape::A, "A" },
{ Shape::B, "B" },
{ Shape::C, "C" },
{ Shape::D, "D" },
{ Shape::E, "E" },
{ Shape::F, "F" },
{ Shape::G, "G" },
{ Shape::H, "H" }
};
return values;
}
std::ostream& operator<<(std::ostream& stream, Shape value) {
return stream << enumToString(value);
return ShapeConverter::get().write(stream, value);
}
std::istream& operator>>(std::istream& stream, Shape& value) {
string name;
stream >> name;
value = parseEnum<Shape>(name);
return stream;
return ShapeConverter::get().read(stream, value);
}

View File

@ -1,8 +1,7 @@
#pragma once
#include <iostream>
#include <vector>
#include "enumTools.h"
#include "EnumConverter.h"
// The classic Hanna-Barbera mouth shapes A-F phus the common supplements G-H
// For reference, see http://sunewatts.dk/lipsync/lipsync/article_02.php
@ -19,11 +18,13 @@ enum class Shape {
H // L
};
template<>
const std::string& getEnumTypeName<Shape>();
template<>
const std::vector<std::tuple<Shape, std::string>>& getEnumMembers<Shape>();
class ShapeConverter : public EnumConverter<Shape> {
public:
static ShapeConverter& get();
protected:
std::string getTypeName() override;
member_data getMemberData() override;
};
std::ostream& operator<<(std::ostream& stream, Shape value);

View File

@ -1,94 +0,0 @@
#pragma once
#include <vector>
#include <tuple>
#include <map>
#include <boost/algorithm/string.hpp>
#include <exception>
#include <format.h>
template<typename T>
const std::string& getEnumTypeName();
template<typename T>
const std::vector<std::tuple<T, std::string>>& getEnumMembers();
namespace detail {
template<typename T>
std::map<std::string, T> createLowerCaseNameToValueMap() {
std::map<std::string, T> map;
for (const auto& pair : getEnumMembers<T>()) {
map[boost::algorithm::to_lower_copy(std::get<std::string>(pair))] = std::get<T>(pair);
}
return map;
}
template<typename T>
std::map<T, std::string> createValueToNameMap() {
std::map<T, std::string> map;
for (const auto& pair : getEnumMembers<T>()) {
map[std::get<T>(pair)] = std::get<std::string>(pair);
}
return map;
}
template<typename T>
std::vector<T> getEnumValues() {
std::vector<T> result;
for (const auto& pair : getEnumMembers<T>()) {
result.push_back(std::get<T>(pair));
}
return result;
}
}
template<typename T>
bool tryParseEnum(const std::string& s, T& result) {
static const std::map<std::string, T> lookup = detail::createLowerCaseNameToValueMap<T>();
auto it = lookup.find(boost::algorithm::to_lower_copy(s));
if (it == lookup.end()) return false;
result = it->second;
return true;
}
template<typename T>
T parseEnum(const std::string& s) {
T result;
if (!tryParseEnum(s, result)) {
throw std::invalid_argument(fmt::format("{} is not a valid {} value.", s, getEnumTypeName<T>()));
}
return result;
}
template<typename T>
bool tryEnumToString(T value, std::string& result) {
static const std::map<T, std::string> lookup = detail::createValueToNameMap<T>();
auto it = lookup.find(value);
if (it == lookup.end()) return false;
result = it->second;
return true;
}
template<typename T>
std::string enumToString(T value) {
std::string result;
if (!tryEnumToString(value, result)) {
throw std::invalid_argument(fmt::format(
"{} is not a valid {} value.",
static_cast<typename std::underlying_type<T>::type>(value),
getEnumTypeName<T>()));
}
return result;
}
template<typename T>
const std::vector<T>& getEnumValues() {
static const auto result = detail::getEnumValues<T>();
return result;
}

View File

@ -5,39 +5,35 @@
using namespace logging;
using std::string;
using std::vector;
using std::tuple;
using std::make_tuple;
using std::shared_ptr;
using std::lock_guard;
template <>
const string& getEnumTypeName<Level>() {
static const string name = "LogLevel";
return name;
LevelConverter& LevelConverter::get() {
static LevelConverter converter;
return converter;
}
template <>
const vector<tuple<Level, string>>& getEnumMembers<Level>() {
static const vector<tuple<Level, string>> values = {
make_tuple(Level::Trace, "Trace"),
make_tuple(Level::Debug, "Debug"),
make_tuple(Level::Info, "Info"),
make_tuple(Level::Warn, "Warn"),
make_tuple(Level::Error, "Error"),
make_tuple(Level::Fatal, "Fatal")
string LevelConverter::getTypeName() {
return "Level";
}
EnumConverter<Level>::member_data LevelConverter::getMemberData() {
return member_data {
{ Level::Trace, "Trace" },
{ Level::Debug, "Debug" },
{ Level::Info, "Info" },
{ Level::Warn, "Warn" },
{ Level::Error, "Error" },
{ Level::Fatal, "Fatal" }
};
return values;
}
std::ostream& operator<<(std::ostream& stream, Level value) {
return stream << enumToString(value);
std::ostream& logging::operator<<(std::ostream& stream, Level value) {
return LevelConverter::get().write(stream, value);
}
std::istream& operator>>(std::istream& stream, Level& value) {
string name;
stream >> name;
value = parseEnum<Level>(name);
return stream;
std::istream& logging::operator>>(std::istream& stream, Level& value) {
return LevelConverter::get().read(stream, value);
}
Entry::Entry(Level level, const string& message) :
@ -77,7 +73,7 @@ void StreamSink::receive(const Entry& entry) {
}
StdErrSink::StdErrSink(shared_ptr<Formatter> formatter) :
StreamSink(std::shared_ptr<std::ostream>(&std::cerr, [](void*){}), formatter)
StreamSink(std::shared_ptr<std::ostream>(&std::cerr, [](void*) {}), formatter)
{}
PausableSink::PausableSink(shared_ptr<Sink> innerSink) :

View File

@ -3,10 +3,9 @@
#include <string>
#include <vector>
#include <mutex>
#include <tuple>
#include "enumTools.h"
#include "tools.h"
#include "Timed.h"
#include "EnumConverter.h"
namespace logging {
@ -20,19 +19,17 @@ namespace logging {
EndSentinel
};
}
class LevelConverter : public EnumConverter<Level> {
public:
static LevelConverter& get();
protected:
std::string getTypeName() override;
member_data getMemberData() override;
};
template<>
const std::string& getEnumTypeName<logging::Level>();
std::ostream& operator<<(std::ostream& stream, Level value);
template<>
const std::vector<std::tuple<logging::Level, std::string>>& getEnumMembers<logging::Level>();
std::ostream& operator<<(std::ostream& stream, logging::Level value);
std::istream& operator>>(std::istream& stream, logging::Level& value);
namespace logging {
std::istream& operator>>(std::istream& stream, Level& value);
struct Entry {
Entry(Level level, const std::string& message);

View File

@ -84,12 +84,12 @@ int main(int argc, char *argv[]) {
tclap::CmdLine cmd(appName, argumentValueSeparator, appVersion);
cmd.setExceptionHandling(false);
cmd.setOutput(new NiceCmdLineOutput());
auto logLevels = vector<logging::Level>(getEnumValues<logging::Level>());
auto logLevels = vector<logging::Level>(logging::LevelConverter::get().getValues());
tclap::ValuesConstraint<logging::Level> logLevelConstraint(logLevels);
tclap::ValueArg<logging::Level> logLevel("", "logLevel", "The minimum log level to log", false, logging::Level::Debug, &logLevelConstraint, cmd);
tclap::ValueArg<string> logFileName("", "logFile", "The log file path.", false, string(), "string", cmd);
tclap::ValueArg<string> dialog("d", "dialog", "The text of the dialog.", false, string(), "string", cmd);
auto exportFormats = vector<ExportFormat>(getEnumValues<ExportFormat>());
auto exportFormats = vector<ExportFormat>(ExportFormatConverter::get().getValues());
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
tclap::ValueArg<ExportFormat> exportFormat("f", "exportFormat", "The export format.", false, ExportFormat::TSV, &exportFormatConstraint, cmd);
tclap::UnlabeledValueArg<string> inputFileName("inputFile", "The input file. Must be a sound file in WAVE format.", true, "", "string", cmd);

View File

@ -275,7 +275,7 @@ Timeline<Phone> getPhoneAlignment(const vector<s3wid_t>& wordIds, unique_ptr<Aud
// Add entry
centiseconds start(phoneEntry->start);
centiseconds duration(phoneEntry->duration);
Timed<Phone> timedPhone(start, start + duration, parseEnum<Phone>(phoneName));
Timed<Phone> timedPhone(start, start + duration, PhoneConverter::get().parse(phoneName));
result.set(timedPhone);
logging::logTimedEvent("phone", timedPhone);