Introduced template functions to unify enum<->string conversions
This commit is contained in:
parent
ad9d8e6567
commit
35ec1f8a45
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
15
src/Phone.h
15
src/Phone.h
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
13
src/Shape.h
13
src/Shape.h
|
@ -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);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#include "enumTools.h"
|
|
@ -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;
|
||||
}
|
|
@ -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) :
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue