Introduced template functions to unify enum<->string conversions

This commit is contained in:
Daniel Wolf 2016-03-08 21:44:57 +01:00
parent ad9d8e6567
commit 35ec1f8a45
11 changed files with 259 additions and 50 deletions

View File

@ -96,6 +96,7 @@ set(SOURCE_FILES
src/Phone.cpp
src/Shape.cpp
src/centiseconds.cpp
src/enumTools.cpp
src/mouthAnimation.cpp
src/phoneExtraction.cpp
src/platformTools.cpp

View File

@ -2,39 +2,78 @@
#include "Phone.h"
using std::string;
using std::vector;
using std::tuple;
using std::make_tuple;
template <typename L, typename R>
boost::bimap<L, R>
makeBimap(std::initializer_list<typename boost::bimap<L, R>::value_type> list) {
return boost::bimap<L, R>(list.begin(), list.end());
template <>
const string& getEnumTypeName<Phone>() {
static const string name = "Shape";
return name;
}
boost::bimap<string, Phone> phonesByName = makeBimap<string, Phone>({
{ "None", Phone::None },
{ "Unknown", Phone::Unknown },
{ "AO", Phone::AO }, { "AA", Phone::AA }, { "IY", Phone::IY }, { "UW", Phone::UW },
{ "EH", Phone::EH }, { "IH", Phone::IH }, { "UH", Phone::UH }, { "AH", Phone::AH },
{ "AE", Phone::AE }, { "EY", Phone::EY }, { "AY", Phone::AY }, { "OW", Phone::OW },
{ "AW", Phone::AW }, { "OY", Phone::OY }, { "ER", Phone::ER }, { "P", Phone::P },
{ "B", Phone::B }, { "T", Phone::T }, { "D", Phone::D }, { "K", Phone::K },
{ "G", Phone::G }, { "CH", Phone::CH }, { "JH", Phone::JH }, { "F", Phone::F },
{ "V", Phone::V }, { "TH", Phone::TH }, { "DH", Phone::DH }, { "S", Phone::S },
{ "Z", Phone::Z }, { "SH", Phone::SH }, { "ZH", Phone::ZH }, { "HH", Phone::HH },
{ "M", Phone::M }, { "N", Phone::N }, { "NG", Phone::NG }, { "L", Phone::L },
{ "R", Phone::R }, { "Y", Phone::Y }, { "W", Phone::W },
});
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")
};
return values;
}
Phone stringToPhone(const string& s) {
template<>
Phone parseEnum(const string& s) {
if (s == "SIL") return Phone::None;
auto it = phonesByName.left.find(s);
return (it != phonesByName.left.end()) ? it->second : Phone::Unknown;
Phone result;
return tryParseEnum(s, result) ? result : Phone::Unknown;
}
string phoneToString(Phone phone) {
auto it = phonesByName.right.find(phone);
return (it != phonesByName.right.end()) ? it->second : phoneToString(Phone::Unknown);
std::ostream& operator<<(std::ostream& stream, Phone value) {
return stream << enumToString(value);
}
std::ostream &operator<<(std::ostream &stream, const Phone phone) {
return stream << phoneToString(phone);
std::istream& operator>>(std::istream& stream, Phone& value) {
string name;
stream >> name;
value = parseEnum<Phone>(name);
return stream;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "enumTools.h"
// Defines a subset of the Arpabet
enum class Phone {
None,
@ -69,8 +71,15 @@ enum class Phone {
W // [w] as in [w]ay
};
Phone stringToPhone(const std::string& s);
template<>
const std::string& getEnumTypeName<Phone>();
std::string phoneToString(Phone phone);
template<>
const std::vector<std::tuple<Phone, std::string>>& getEnumMembers<Phone>();
std::ostream& operator <<(std::ostream& stream, const Phone phone);
template<>
Phone parseEnum(const std::string& s);
std::ostream& operator<<(std::ostream& stream, Phone value);
std::istream& operator>>(std::istream& stream, Phone& value);

View File

@ -1,10 +1,40 @@
#include "Shape.h"
std::string shapeToString(Shape shape) {
char c = 'A' + static_cast<char>(shape);
return std::string(&c, 1);
#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;
}
std::ostream &operator<<(std::ostream &stream, const Shape shape) {
return stream << shapeToString(shape);
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")
};
return values;
}
std::ostream& operator<<(std::ostream& stream, Shape value) {
return stream << enumToString(value);
}
std::istream& operator>>(std::istream& stream, Shape& value) {
string name;
stream >> name;
value = parseEnum<Shape>(name);
return stream;
}

View File

@ -1,7 +1,8 @@
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include "enumTools.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
@ -18,6 +19,12 @@ enum class Shape {
H // L
};
std::string shapeToString(Shape shape);
template<>
const std::string& getEnumTypeName<Shape>();
std::ostream& operator <<(std::ostream& stream, const Shape shape);
template<>
const std::vector<std::tuple<Shape, std::string>>& getEnumMembers<Shape>();
std::ostream& operator<<(std::ostream& stream, Shape value);
std::istream& operator>>(std::istream& stream, Shape& value);

1
src/enumTools.cpp Normal file
View File

@ -0,0 +1 @@
#include "enumTools.h"

94
src/enumTools.h Normal file
View File

@ -0,0 +1,94 @@
#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

@ -1,5 +1,4 @@
#include "logging.h"
#include <array>
#include <boost/log/sinks/unlocked_frontend.hpp>
#include <boost/log/expressions.hpp>
#include <centiseconds.h>
@ -10,15 +9,40 @@ using std::lock_guard;
using boost::log::sinks::text_ostream_backend;
using boost::log::record_view;
using boost::log::sinks::unlocked_sink;
using std::vector;
using std::tuple;
using std::make_tuple;
namespace expr = boost::log::expressions;
string toString(LogLevel level) {
constexpr size_t levelCount = static_cast<size_t>(LogLevel::EndSentinel);
static const std::array<const string, levelCount> strings = {
"Trace", "Debug", "Info", "Warning", "Error", "Fatal"
template <>
const string& getEnumTypeName<LogLevel>() {
static const string name = "LogLevel";
return name;
}
template <>
const vector<tuple<LogLevel, string>>& getEnumMembers<LogLevel>() {
static const vector<tuple<LogLevel, string>> values = {
make_tuple(LogLevel::Trace, "Trace"),
make_tuple(LogLevel::Debug, "Debug"),
make_tuple(LogLevel::Info, "Info"),
make_tuple(LogLevel::Warning, "Warning"),
make_tuple(LogLevel::Error, "Error"),
make_tuple(LogLevel::Fatal, "Fatal")
};
return strings.at(static_cast<size_t>(level));
return values;
}
std::ostream& operator<<(std::ostream& stream, LogLevel value) {
return stream << enumToString(value);
}
std::istream& operator>>(std::istream& stream, LogLevel& value) {
string name;
stream >> name;
value = parseEnum<LogLevel>(name);
return stream;
}
PausableBackendAdapter::PausableBackendAdapter(boost::shared_ptr<text_ostream_backend> backend) :

View File

@ -9,6 +9,7 @@
#include <mutex>
#include <tuple>
#include "centiseconds.h"
#include "enumTools.h"
enum class LogLevel {
Trace,
@ -20,12 +21,15 @@ enum class LogLevel {
EndSentinel
};
std::string toString(LogLevel level);
template<>
const std::string& getEnumTypeName<LogLevel>();
template<typename CharT, typename TraitsT>
std::basic_ostream<CharT, TraitsT>& operator<< (std::basic_ostream<CharT, TraitsT>& stream, LogLevel level) {
return stream << toString(level);
}
template<>
const std::vector<std::tuple<LogLevel, std::string>>& getEnumMembers<LogLevel>();
std::ostream& operator<<(std::ostream& stream, LogLevel value);
std::istream& operator>>(std::istream& stream, LogLevel& value);
using LoggerType = boost::log::sources::severity_logger_mt<LogLevel>;

View File

@ -80,7 +80,7 @@ map<centiseconds, Shape> animate(const map<centiseconds, Phone> &phones) {
for (auto it = shapes.cbegin(); it != shapes.cend(); ++it) {
if (next(it) == shapes.cend()) break;
logTimedEvent("shape", it->first, next(it)->first, shapeToString(it->second));
logTimedEvent("shape", it->first, next(it)->first, enumToString(it->second));
}
return shapes;

View File

@ -287,7 +287,7 @@ map<centiseconds, Phone> getPhoneAlignment(const vector<s3wid_t>& wordIds, uniqu
// Add map entries
centiseconds start(startFrame);
result[start] = stringToPhone(phoneName);
result[start] = parseEnum<Phone>(phoneName);
centiseconds end(startFrame + duration);
result[end] = Phone::None;