Clean up OptionValue

Make the vtables less absurdly huge (knocks 100KB off aegisub32.exe),
eliminate some copies of the values when constructing the options, and
use an enum class for the value type.
This commit is contained in:
Thomas Goyne 2014-03-31 09:24:56 -07:00
parent 371f602100
commit c59b9d59b8
10 changed files with 174 additions and 128 deletions

View File

@ -125,43 +125,43 @@ void Options::Flush() const {
for (auto const& ov : values) {
switch (ov.second->GetType()) {
case OptionValue::Type_String:
case OptionType::String:
put_option(obj_out, ov.first, ov.second->GetString());
break;
case OptionValue::Type_Int:
case OptionType::Int:
put_option(obj_out, ov.first, ov.second->GetInt());
break;
case OptionValue::Type_Double:
case OptionType::Double:
put_option(obj_out, ov.first, ov.second->GetDouble());
break;
case OptionValue::Type_Color:
case OptionType::Color:
put_option(obj_out, ov.first, ov.second->GetColor().GetRgbFormatted());
break;
case OptionValue::Type_Bool:
case OptionType::Bool:
put_option(obj_out, ov.first, ov.second->GetBool());
break;
case OptionValue::Type_List_String:
case OptionType::ListString:
put_array(obj_out, ov.first, "string", ov.second->GetListString());
break;
case OptionValue::Type_List_Int:
case OptionType::ListInt:
put_array(obj_out, ov.first, "int", ov.second->GetListInt());
break;
case OptionValue::Type_List_Double:
case OptionType::ListDouble:
put_array(obj_out, ov.first, "double", ov.second->GetListDouble());
break;
case OptionValue::Type_List_Color:
case OptionType::ListColor:
put_array(obj_out, ov.first, "color", ov.second->GetListColor());
break;
case OptionValue::Type_List_Bool:
case OptionType::ListBool:
put_array(obj_out, ov.first, "bool", ov.second->GetListBool());
break;
}

View File

@ -59,9 +59,9 @@ void ConfigVisitor::Visit(const json::Object& object) {
}
}
template<class OptionValueType, class ValueType>
std::unique_ptr<OptionValue> ConfigVisitor::ReadArray(json::Array const& src, std::string const& array_type, void (OptionValueType::*)(const std::vector<ValueType>&)) {
std::vector<ValueType> arr;
template<class OptionValueType>
std::unique_ptr<OptionValue> ConfigVisitor::ReadArray(json::Array const& src, std::string const& array_type) {
typename OptionValueType::value_type arr;
arr.reserve(src.size());
for (json::Object const& obj : src) {
@ -74,10 +74,10 @@ std::unique_ptr<OptionValue> ConfigVisitor::ReadArray(json::Array const& src, st
return nullptr;
}
arr.push_back(ValueType(obj.begin()->second));
arr.push_back((typename OptionValueType::value_type::value_type)(obj.begin()->second));
}
return util::make_unique<OptionValueType>(name, arr);
return util::make_unique<OptionValueType>(name, std::move(arr));
}
void ConfigVisitor::Visit(const json::Array& array) {
@ -95,15 +95,15 @@ void ConfigVisitor::Visit(const json::Array& array) {
const std::string& array_type = front.begin()->first;
if (array_type == "string")
AddOptionValue(ReadArray(array, array_type, &OptionValueListString::SetListString));
AddOptionValue(ReadArray<OptionValueListString>(array, array_type));
else if (array_type == "int")
AddOptionValue(ReadArray(array, array_type, &OptionValueListInt::SetListInt));
AddOptionValue(ReadArray<OptionValueListInt>(array, array_type));
else if (array_type == "double")
AddOptionValue(ReadArray(array, array_type, &OptionValueListDouble::SetListDouble));
AddOptionValue(ReadArray<OptionValueListDouble>(array, array_type));
else if (array_type == "bool")
AddOptionValue(ReadArray(array, array_type, &OptionValueListBool::SetListBool));
AddOptionValue(ReadArray<OptionValueListBool>(array, array_type));
else if (array_type == "color")
AddOptionValue(ReadArray(array, array_type, &OptionValueListColor::SetListColor));
AddOptionValue(ReadArray<OptionValueListColor>(array, array_type));
else
Error<OptionJsonValueArray>("Array type not handled");
}

View File

@ -46,8 +46,8 @@ class ConfigVisitor final : public json::ConstVisitor {
template<class ErrorType>
void Error(const char *message);
template<class OptionValueType, class ValueType>
std::unique_ptr<OptionValue> ReadArray(json::Array const& src, std::string const& array_type, void (OptionValueType::*set_list)(const std::vector<ValueType>&));
template<class OptionValueType>
std::unique_ptr<OptionValue> ReadArray(json::Array const& src, std::string const& array_type);
void AddOptionValue(std::unique_ptr<OptionValue>&& opt);
public:

View File

@ -27,91 +27,119 @@ namespace agi {
DEFINE_BASE_EXCEPTION_NOINNER(OptionValueError, Exception)
DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionValueErrorNotFound, OptionValueError, "options/not_found")
DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionValueErrorInvalidType, OptionValueError, "options/invalid_type")
DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionValueErrorInvalidListType, OptionValueError, "options/array/invalid_type")
/// Option type
/// No bitsets here.
enum class OptionType {
String = 0, ///< String
Int = 1, ///< Integer
Double = 2, ///< Double
Color = 3, ///< Color
Bool = 4, ///< Bool
ListString = 100, ///< List of Strings
ListInt = 101, ///< List of Integers
ListDouble = 102, ///< List of Doubles
ListColor = 103, ///< List of Colors
ListBool = 104 ///< List of Bools
};
/// @class OptionValue
/// Holds an actual option.
class OptionValue {
agi::signal::Signal<OptionValue const&> ValueChanged;
std::string name;
std::string TypeToString(OptionType type) const {
switch (type) {
case OptionType::String: return "String";
case OptionType::Int: return "Integer";
case OptionType::Double: return "Double";
case OptionType::Color: return "Color";
case OptionType::Bool: return "Bool";
case OptionType::ListString: return "List of Strings";
case OptionType::ListInt: return "List of Integers";
case OptionType::ListDouble: return "List of Doubles";
case OptionType::ListColor: return "List of Colors";
case OptionType::ListBool: return "List of Bools";
}
throw agi::InternalError("Invalid option type", nullptr);
}
OptionValueErrorInvalidType TypeError(OptionType type) const {
return OptionValueErrorInvalidType("Invalid type for option " + name + ": expected " + TypeToString(type) + ", got " + TypeToString(GetType()));
}
template<typename T>
T *As(OptionType type) {
if (GetType() == type)
return static_cast<T *>(this);
throw TypeError(type);
}
template<typename T>
const T *As(OptionType type) const {
if (GetType() == type)
return static_cast<const T *>(this);
throw TypeError(type);
}
protected:
void NotifyChanged() { ValueChanged(*this); }
OptionValueErrorInvalidType TypeError(std::string type, std::string op = " retrieve ") const {
return OptionValueErrorInvalidType("Attempt to" + op + type + " with non-" + type + " value " + GetName());
}
OptionValueErrorInvalidListType ListTypeError(std::string type, std::string op = " retrieve ") const {
return OptionValueErrorInvalidListType("Attempt to" + op + type + " with non-" + type + " list " + GetName());
}
OptionValue(std::string name) : name(std::move(name)) { }
public:
virtual ~OptionValue() {};
/// Option type
/// No bitsets here.
enum OptionType {
Type_String = 0, ///< String
Type_Int = 1, ///< Integer
Type_Double = 2, ///< Double
Type_Color = 3, ///< Color
Type_Bool = 4, ///< Bool
Type_List_String = 100, ///< List of Strings
Type_List_Int = 101, ///< List of Integers
Type_List_Double = 102, ///< List of Doubles
Type_List_Color = 103, ///< List of Colors
Type_List_Bool = 104 ///< List of Bools
};
virtual ~OptionValue() {}
std::string GetName() const { return name; }
virtual OptionType GetType() const = 0;
virtual bool IsDefault() const = 0;
virtual void Reset() = 0;
virtual std::string GetString() const { throw TypeError("string"); }
virtual int64_t GetInt() const { throw TypeError("int"); }
virtual double GetDouble() const { throw TypeError("double"); }
virtual Color GetColor() const { throw TypeError("color"); }
virtual bool GetBool() const { throw TypeError("bool"); }
std::string const& GetString() const;
int64_t const& GetInt() const;
double const& GetDouble() const;
Color const& GetColor() const;
bool const& GetBool() const;
virtual void SetString(const std::string) { throw TypeError("string", " set "); }
virtual void SetInt(const int64_t) { throw TypeError("int", " set "); }
virtual void SetDouble(const double) { throw TypeError("double", " set "); }
virtual void SetColor(const Color) { throw TypeError("color", " set "); }
virtual void SetBool(const bool) { throw TypeError("bool", " set "); }
void SetString(const std::string);
void SetInt(const int64_t);
void SetDouble(const double);
void SetColor(const Color);
void SetBool(const bool);
virtual std::vector<std::string> const& GetListString() const { throw ListTypeError("string"); }
virtual std::vector<int64_t> const& GetListInt() const { throw ListTypeError("int"); }
virtual std::vector<double> const& GetListDouble() const { throw ListTypeError("double"); }
virtual std::vector<Color> const& GetListColor() const { throw ListTypeError("color"); }
virtual std::vector<bool> const& GetListBool() const { throw ListTypeError("string"); }
std::vector<std::string> const& GetListString() const;
std::vector<int64_t> const& GetListInt() const;
std::vector<double> const& GetListDouble() const;
std::vector<Color> const& GetListColor() const;
std::vector<bool> const& GetListBool() const;
virtual void SetListString(const std::vector<std::string>&) { throw ListTypeError("string", " set "); }
virtual void SetListInt(const std::vector<int64_t>&) { throw ListTypeError("int", " set "); }
virtual void SetListDouble(const std::vector<double>&) { throw ListTypeError("double", " set "); }
virtual void SetListColor(const std::vector<Color>&) { throw ListTypeError("color", " set "); }
virtual void SetListBool(const std::vector<bool>&) { throw ListTypeError("string", " set "); }
void SetListString(std::vector<std::string>);
void SetListInt(std::vector<int64_t>);
void SetListDouble(std::vector<double>);
void SetListColor(std::vector<Color>);
void SetListBool(std::vector<bool>);
virtual void Set(const OptionValue *new_value)=0;
DEFINE_SIGNAL_ADDERS(ValueChanged, Subscribe)
};
#define CONFIG_OPTIONVALUE(type_name, type) \
class OptionValue##type_name final : public OptionValue { \
type value; \
type value_default; \
public: \
OptionValue##type_name(std::string member_name, type member_value) \
: OptionValue(std::move(member_name)) \
, value(member_value), value_default(member_value) { } \
type Get##type_name() const { return value; } \
void Set##type_name(const type new_val) { value = new_val; NotifyChanged(); } \
OptionType GetType() const { return OptionValue::Type_##type_name; } \
void Reset() { value = value_default; NotifyChanged(); } \
bool IsDefault() const { return value == value_default; } \
void Set(const OptionValue *new_val) { Set##type_name(new_val->Get##type_name()); } \
#define CONFIG_OPTIONVALUE(type_name, type) \
class OptionValue##type_name final : public OptionValue { \
type value; \
type value_default; \
public: \
typedef type value_type; \
OptionValue##type_name(std::string member_name, type member_value) \
: OptionValue(std::move(member_name)) \
, value(member_value), value_default(member_value) { } \
type const& GetValue() const { return value; } \
void SetValue(type new_val) { value = std::move(new_val); NotifyChanged(); } \
OptionType GetType() const { return OptionType::type_name; } \
void Reset() { value = value_default; NotifyChanged(); } \
bool IsDefault() const { return value == value_default; } \
void Set(const OptionValue *new_val) { SetValue(new_val->Get##type_name()); } \
};
CONFIG_OPTIONVALUE(String, std::string)
@ -120,21 +148,22 @@ CONFIG_OPTIONVALUE(Double, double)
CONFIG_OPTIONVALUE(Color, Color)
CONFIG_OPTIONVALUE(Bool, bool)
#define CONFIG_OPTIONVALUE_LIST(type_name, type) \
class OptionValueList##type_name final : public OptionValue { \
std::vector<type> array; \
std::vector<type> array_default; \
std::string name; \
public: \
#define CONFIG_OPTIONVALUE_LIST(type_name, type) \
class OptionValueList##type_name final : public OptionValue { \
std::vector<type> array; \
std::vector<type> array_default; \
std::string name; \
public: \
typedef std::vector<type> value_type; \
OptionValueList##type_name(std::string name, std::vector<type> const& value = std::vector<type>()) \
: OptionValue(std::move(name)) \
, array(value), array_default(value) { } \
std::vector<type> const& GetList##type_name() const { return array; } \
void SetList##type_name(const std::vector<type>& val) { array = val; NotifyChanged(); } \
OptionType GetType() const { return OptionValue::Type_List_##type_name; } \
void Reset() { array = array_default; NotifyChanged(); } \
bool IsDefault() const { return array == array_default; } \
void Set(const OptionValue *nv) { SetList##type_name(nv->GetList##type_name()); } \
: OptionValue(std::move(name)) \
, array(value), array_default(value) { } \
std::vector<type> const& GetValue() const { return array; } \
void SetValue(std::vector<type> val) { array = std::move(val); NotifyChanged(); } \
OptionType GetType() const { return OptionType::List##type_name; } \
void Reset() { array = array_default; NotifyChanged(); } \
bool IsDefault() const { return array == array_default; } \
void Set(const OptionValue *nv) { SetValue(nv->GetList##type_name()); } \
};
CONFIG_OPTIONVALUE_LIST(String, std::string)
@ -143,4 +172,22 @@ CONFIG_OPTIONVALUE_LIST(Double, double)
CONFIG_OPTIONVALUE_LIST(Color, Color)
CONFIG_OPTIONVALUE_LIST(Bool, bool)
#define CONFIG_OPTIONVALUE_ACCESSORS(ReturnType, Type) \
inline ReturnType const& OptionValue::Get##Type() const { return As<OptionValue##Type>(OptionType::Type)->GetValue(); } \
inline void OptionValue::Set##Type(ReturnType v) { As<OptionValue##Type>(OptionType::Type)->SetValue(std::move(v)); }
CONFIG_OPTIONVALUE_ACCESSORS(std::string, String)
CONFIG_OPTIONVALUE_ACCESSORS(int64_t, Int)
CONFIG_OPTIONVALUE_ACCESSORS(double, Double)
CONFIG_OPTIONVALUE_ACCESSORS(Color, Color)
CONFIG_OPTIONVALUE_ACCESSORS(bool, Bool)
CONFIG_OPTIONVALUE_ACCESSORS(std::vector<std::string>, ListString)
CONFIG_OPTIONVALUE_ACCESSORS(std::vector<int64_t>, ListInt)
CONFIG_OPTIONVALUE_ACCESSORS(std::vector<double>, ListDouble)
CONFIG_OPTIONVALUE_ACCESSORS(std::vector<Color>, ListColor)
CONFIG_OPTIONVALUE_ACCESSORS(std::vector<bool>, ListBool)
#undef CONFIG_OPTIONVALUE
#undef CONFIG_OPTIONVALUE_LIST
#undef CONFIG_OPTIONVALUE_ACCESSORS
} // namespace agi

View File

@ -176,8 +176,7 @@ void BaseGrid::OnShowColMenu(wxCommandEvent &event) {
int item = event.GetId() - MENU_SHOW_COL;
showCol[item] = !showCol[item];
std::vector<bool> map(std::begin(showCol), std::end(showCol));
OPT_SET("Subtitle/Grid/Column")->SetListBool(map);
OPT_SET("Subtitle/Grid/Column")->SetListBool(std::vector<bool>(std::begin(showCol), std::end(showCol)));
SetColumnWidths();
Refresh(false);

View File

@ -106,7 +106,7 @@ void DialogPasteOver::OnOK(wxCommandEvent &) {
std::vector<bool> options;
for (size_t i = 0; i < ListBox->GetCount(); ++i)
options.push_back(ListBox->IsChecked(i));
OPT_SET("Tool/Paste Lines Over/Fields")->SetListBool(options);
OPT_SET("Tool/Paste Lines Over/Fields")->SetListBool(std::move(options));
EndModal(0);
}

View File

@ -117,7 +117,7 @@ void init() {
migrations.emplace_back("duplicate -> split");
}
OPT_SET("App/Hotkey Migrations")->SetListString(migrations);
OPT_SET("App/Hotkey Migrations")->SetListString(std::move(migrations));
}
void clear() {

View File

@ -117,7 +117,7 @@ wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, co
const agi::OptionValue *opt = OPT_GET(opt_name);
switch (opt->GetType()) {
case agi::OptionValue::Type_Bool: {
case agi::OptionType::Bool: {
wxCheckBox *cb = new wxCheckBox(this, -1, name);
flex->Add(cb, 1, wxEXPAND, 0);
cb->SetValue(opt->GetBool());
@ -125,28 +125,28 @@ wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, co
return cb;
}
case agi::OptionValue::Type_Int: {
case agi::OptionType::Int: {
wxSpinCtrl *sc = new wxSpinCtrl(this, -1, std::to_wstring((int)opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetInt());
sc->Bind(wxEVT_SPINCTRL, IntUpdater(opt_name, parent));
Add(flex, name, sc);
return sc;
}
case agi::OptionValue::Type_Double: {
case agi::OptionType::Double: {
wxSpinCtrlDouble *scd = new wxSpinCtrlDouble(this, -1, wxString::Format("%g", opt->GetDouble()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetDouble(), inc);
scd->Bind(wxEVT_SPINCTRL, DoubleUpdater(opt_name, parent));
Add(flex, name, scd);
return scd;
}
case agi::OptionValue::Type_String: {
case agi::OptionType::String: {
wxTextCtrl *text = new wxTextCtrl(this, -1 , to_wx(opt->GetString()));
text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent));
Add(flex, name, text);
return text;
}
case agi::OptionValue::Type_Color: {
case agi::OptionType::Color: {
auto cb = new ColourButton(this, wxSize(40,10), false, opt->GetColor());
cb->Bind(EVT_COLOR, ColourUpdater(opt_name, parent));
Add(flex, name, cb);
@ -166,13 +166,13 @@ void OptionPage::OptionChoice(wxFlexGridSizer *flex, const wxString &name, const
Add(flex, name, cb);
switch (opt->GetType()) {
case agi::OptionValue::Type_Int: {
case agi::OptionType::Int: {
int val = opt->GetInt();
cb->Select(val < (int)choices.size() ? val : 0);
cb->Bind(wxEVT_COMBOBOX, IntCBUpdater(opt_name, parent));
break;
}
case agi::OptionValue::Type_String: {
case agi::OptionType::String: {
wxString val(to_wx(opt->GetString()));
if (cb->FindString(val) != wxNOT_FOUND)
cb->SetStringSelection(val);
@ -201,8 +201,8 @@ void OptionPage::OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const
parent->AddChangeableOption(opt_name);
const agi::OptionValue *opt = OPT_GET(opt_name);
if (opt->GetType() != agi::OptionValue::Type_String)
throw PreferenceIncorrectType("Option must be agi::OptionValue::Type_String for BrowseButton.");
if (opt->GetType() != agi::OptionType::String)
throw PreferenceIncorrectType("Option must be agi::OptionType::String for BrowseButton.");
wxTextCtrl *text = new wxTextCtrl(this, -1 , to_wx(opt->GetString()));
text->SetMinSize(wxSize(160, -1));

View File

@ -200,7 +200,7 @@ void SubsTextEditCtrl::SetSyntaxStyle(int id, wxFont &font, std::string const& n
StyleSetBold(id, OPT_GET("Colour/Subtitle/Syntax/Bold/" + name)->GetBool());
StyleSetForeground(id, to_wx(OPT_GET("Colour/Subtitle/Syntax/" + name)->GetColor()));
const agi::OptionValue *background = OPT_GET("Colour/Subtitle/Syntax/Background/" + name);
if (background->GetType() == agi::OptionValue::Type_Color)
if (background->GetType() == agi::OptionType::Color)
StyleSetBackground(id, to_wx(background->GetColor()));
}

View File

@ -212,50 +212,50 @@ TEST_F(lagi_option, empty_array_decays_to_first_used_type) {
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListBool());
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidType);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListColor());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidType);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListDouble());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidType);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListInt());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidType);
}
{
empty_arr_options opt;
EXPECT_NO_THROW(opt.Get("arr")->GetListString());
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType);
EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListColor(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidType);
EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidType);
}
}