More operator <</>> Fixups.

This commit is contained in:
John Ralls 2019-12-03 11:14:06 -08:00
parent 98ca190700
commit 1ea3822665
2 changed files with 229 additions and 90 deletions

View File

@ -176,31 +176,8 @@ private:
GncOptionUIType m_ui_type;
};
/* These will work when m_value is a built-in class; GnuCash class and container
* values will need specialization unless they happen to define operators << and
* >>.
* Note that SWIG 3.0.12 chokes on the typename = std::enable_if_t<> form so we
* have to use the non-type parameter form.
*/
template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
{
oss << opt.get_value();
return oss;
}
template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
{
std::decay_t<decltype(opt.get_value())> value;
iss >> value;
opt.set_value(value);
return iss;
}
template <typename ValueType>
class GncOptionValue :
public OptionClassifier, public OptionUIItem
class GncOptionValue : public OptionClassifier, public OptionUIItem
{
public:
GncOptionValue<ValueType>(const char* section, const char* name,
@ -223,49 +200,8 @@ private:
ValueType m_default_value;
};
QofInstance* qof_instance_from_string(const std::string& str,
GncOptionUIType type);
std::string qof_instance_to_string(const QofInstance* inst);
template<> inline std::ostream&
operator<< <GncOptionValue<bool>>(std::ostream& oss,
const GncOptionValue<bool>& opt)
{
oss << (opt.get_value() ? "#t" : "#f");
return oss;
}
template<> inline std::ostream&
operator<< <GncOptionValue<QofInstance*>>(std::ostream& oss,
const GncOptionValue<QofInstance*>& opt)
{
oss << qof_instance_to_string(opt.get_value());
return oss;
}
template<> inline std::istream&
operator>> <GncOptionValue<bool>>(std::istream& iss,
GncOptionValue<bool>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(instr == "#t" ? true : false);
return iss;
}
template<> inline std::istream&
operator>> <GncOptionValue<QofInstance*>>(std::istream& iss,
GncOptionValue<QofInstance*>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
return iss;
}
template <typename ValueType>
class GncOptionValidatedValue :
public OptionClassifier, public OptionUIItem
class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem
{
public:
GncOptionValidatedValue<ValueType>(const char* section, const char* name,
@ -314,25 +250,177 @@ private:
ValueType m_validation_data;
};
template<> inline std::ostream&
operator<< <GncOptionValidatedValue<QofInstance*>>(std::ostream& oss,
const GncOptionValidatedValue<QofInstance*>& opt)
QofInstance* qof_instance_from_string(const std::string& str,
GncOptionUIType type);
std::string qof_instance_to_string(const QofInstance* inst);
/* These will work when m_value is a built-in class; GnuCash class and container
* values will need specialization unless they happen to define operators << and
* >>.
* Note that SWIG 3.0.12 chokes on elaborate enable_if so just hide the
* following templates from SWIG. (Ignoring doesn't work because SWIG still has
* to parse the templates to figure out the symbols.
*/
#ifndef SWIG
template<class OptionValueClass,
typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
std::decay_t<OptionValueClass>> &&
!(std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValue<QofInstance*>> ||
std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValidatedValue<QofInstance*>>), int> = 0>
std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
{
oss << qof_instance_to_string(opt.get_value());
std::cerr << qof_instance_to_string(opt.get_value());
oss << opt.get_value();
return oss;
}
template<> inline std::ostream&
operator<< <GncOptionValue<bool>>(std::ostream& oss,
const GncOptionValue<bool>& opt)
{
oss << (opt.get_value() ? "#t" : "#f");
return oss;
}
template<> inline std::istream&
operator>> <GncOptionValidatedValue<QofInstance*>>(std::istream& iss,
GncOptionValidatedValue<QofInstance*>& opt)
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
inline std::ostream&
operator<< (std::ostream& oss, const OptType& opt)
{
auto value = opt.get_value();
if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
type == GncOptionUIType::CURRENCY)
{
if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
{
oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " ";
}
oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value));
}
else
{
oss << qof_instance_to_string(value);
}
return oss;
}
template<class OptionValueClass,
typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>> &&
!(std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValue<QofInstance*>> ||
std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValidatedValue<QofInstance*>>), int> = 0>
std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
{
std::decay_t<decltype(opt.get_value())> value;
iss >> value;
opt.set_value(value);
return iss;
}
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
std::istream&
operator>> (std::istream& iss, OptType& opt)
{
std::string instr;
iss >> instr;
if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
type == GncOptionUIType::CURRENCY)
{
std::string name_space, mnemonic;
if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
{
iss >> name_space;
}
else
name_space = GNC_COMMODITY_NS_CURRENCY;
iss >> mnemonic;
instr = name_space + ":";
instr += mnemonic;
}
else
{
iss >> instr;
}
opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
return iss;
}
template<> inline std::istream&
operator>> <GncOptionValue<bool>>(std::istream& iss,
GncOptionValue<bool>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(instr == "#t" ? true : false);
return iss;
}
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
inline std::ostream&
gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
{
auto value = opt.get_value();
auto type = opt.get_ui_type();
if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
{
if (type == GncOptionUIType::COMMODITY)
{
oss << commodity_scm_intro;
oss << "\"" <<
gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" ";
}
oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\"";
if (type == GncOptionUIType::COMMODITY)
{
oss << ")";
}
}
else
{
oss << "\"" << qof_instance_to_string(value) << "\"";
}
return oss;
}
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
inline std::istream&
gnc_option_from_scheme (std::istream& iss, OptType& opt)
{
std::string instr;
auto type = opt.get_ui_type();
if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
{
std::string name_space, mnemonic;
if (type == GncOptionUIType::COMMODITY)
{
iss.ignore(strlen(commodity_scm_intro) + 1, '"');
std::getline(iss, name_space, '"');
iss.ignore(1, '"');
}
else
name_space = GNC_COMMODITY_NS_CURRENCY;
iss.ignore(1, '"');
std::getline(iss, mnemonic, '"');
if (type == GncOptionUIType::COMMODITY)
iss.ignore(2, ')');
else
iss.ignore(1, '"');
instr = name_space + ":";
instr += mnemonic;
}
else
{
iss.ignore(1, '"');
std::getline(iss, instr, '"');
}
opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
return iss;
}
#endif // SWIG
/**
* Used for numeric ranges and plot sizes.
*/
@ -559,8 +647,15 @@ operator<< <GncOptionAccountValue>(std::ostream& oss,
const GncOptionAccountValue& opt)
{
auto values{opt.get_value()};
bool first = true;
for (auto value : values)
oss << qof_instance_to_string(QOF_INSTANCE(value)) << " ";
{
if (first)
first = false;
else
oss << " ";
oss << qof_instance_to_string(QOF_INSTANCE(value));
}
return oss;
}

View File

@ -152,6 +152,28 @@ TEST_F(GncOptionTest, test_budget_ctor)
gnc_budget_destroy(budget);
}
TEST_F(GncOptionTest, test_budget_out)
{
auto budget = gnc_budget_new(m_book);
GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)};
auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
std::ostringstream oss;
oss << option;
EXPECT_EQ(budget_guid, oss.str());
gnc_budget_destroy(budget);
}
TEST_F(GncOptionTest, test_budget_in)
{
auto budget = gnc_budget_new(m_book);
auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
std::istringstream iss{budget_guid};
GncOption option{GncOptionValue<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
iss >> option;
EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
gnc_budget_destroy(budget);
}
TEST_F(GncOptionTest, test_commodity_ctor)
{
auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
@ -589,23 +611,23 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
TEST_F(GncOptionAccountTest, test_account_list_out)
{
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST, acclist};
GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
acclist}};
std::ostringstream oss;
std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
acc_guids += " ";
acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
acc_guids += " ";
oss << option;
EXPECT_EQ(acc_guids, oss.str());
GncOptionAccountList accsel{acclist[0]};
GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
"Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
accsel, {ACCT_TYPE_BANK});
accsel, {ACCT_TYPE_BANK}}};
acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
acc_guids += " ";
oss.str("");
oss << sel_option;
@ -615,35 +637,39 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
TEST_F(GncOptionAccountTest, test_account_list_in)
{
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST, acclist};
GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
acclist}};
std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
acc_guids += " ";
acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
acc_guids += " ";
std::istringstream iss{acc_guids};
iss >> option;
EXPECT_EQ(acclist, option.get_value());
EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
GncOptionAccountList accsel{acclist[0]};
GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
"Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
accsel, {ACCT_TYPE_BANK});
accsel, {ACCT_TYPE_BANK}}};
GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
acc_guids += " ";
iss.str(acc_guids);
iss >> sel_option;
EXPECT_EQ(accsel, sel_option.get_value());
EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());
iss.clear(); //Reset the failedbit from the invalid selection type.
acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
EXPECT_NO_THROW({
iss.str(acc_guids);
iss >> sel_option;
});
EXPECT_EQ(acclist[1], sel_option.get_value()[0]);
EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
}
}
class GncOptionMultichoiceTest : public ::testing::Test
@ -710,6 +736,24 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
m_option.permissible_value_index("xyzzy"));
}
TEST_F(GncMultichoiceOption, test_multichoice_out)
{
std::ostringstream oss;
oss << m_option;
EXPECT_EQ(oss.str(), m_option.get_value<std::string>());
}
TEST_F(GncMultichoiceOption, test_multichoice_in)
{
std::istringstream iss{"grault"};
EXPECT_THROW({
iss >> m_option;
}, std::invalid_argument);
iss.clear(); //reset failedbit
iss.str("pork");
iss >> m_option;
EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
}
class GncOptionDateOptionTest : public ::testing::Test
{
protected: