Skip to content

Commit

Permalink
Support different GncOptionMultichoiceValue key types.
Browse files Browse the repository at this point in the history
Scheme can use strings, symbols, or ints as keys in multichoice options,
but C++ can handle only strings. Add conversion and tracking so that the
right key type gets sent back to Scheme.
  • Loading branch information
jralls committed Aug 1, 2021
1 parent 55a2ed1 commit fba0248
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 5 deletions.
7 changes: 5 additions & 2 deletions libgnucash/app-utils/gnc-option-impl.hpp
Expand Up @@ -500,7 +500,8 @@ operator>> (std::istream& iss, OptType& opt)

using GncMultichoiceOptionEntry = std::tuple<const std::string,
const std::string,
const std::string>;
const std::string,
GncOptionMultichoiceKeyType>;
using GncMultichoiceOptionIndexVec = std::vector<std::size_t>;
using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;

Expand Down Expand Up @@ -575,7 +576,8 @@ class GncOptionMultichoiceValue : public OptionClassifier
if (vec.size() == 1)
return std::get<0>(m_choices.at(vec[0]));
else
return c_list_string;
throw std::length_error("Retrieving multiple values from a multichoice isn't implemented.");

}
const std::string& get_default_value() const
{
Expand Down Expand Up @@ -671,6 +673,7 @@ class GncOptionMultichoiceValue : public OptionClassifier
bool is_changed() const noexcept { return m_value != m_default_value; }
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<3>(m_choices.at(i)); }
private:
std::size_t find_key (const std::string& key) const noexcept
{
Expand Down
7 changes: 7 additions & 0 deletions libgnucash/app-utils/gnc-option.hpp
Expand Up @@ -63,6 +63,13 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,

using GncOptionVariantPtr = std::unique_ptr<GncOptionVariant>;

enum class GncOptionMultichoiceKeyType
{
SYMBOL,
STRING,
NUMBER,
};

class GncOption
{
public:
Expand Down
3 changes: 2 additions & 1 deletion libgnucash/app-utils/gnc-optiondb.hpp
Expand Up @@ -51,7 +51,8 @@ using GncOptionAccountList = std::vector<const Account*>;
using GncOptionAccountTypeList = std::vector<GNCAccountType>;
using GncMultichoiceOptionEntry = std::tuple<const std::string,
const std::string,
const std::string>;
const std::string,
GncOptionMultichoiceKeyType>;
using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;

/**
Expand Down
66 changes: 64 additions & 2 deletions libgnucash/app-utils/gnc-optiondb.i
Expand Up @@ -256,14 +256,34 @@ gnc_option_test_book_destroy(QofBook* book)

%typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices)
{
using KeyType = GncOptionMultichoiceKeyType;
auto len = scm_to_size_t(scm_length($input));
for (std::size_t i = 0; i < len; ++i)
{
SCM vec = scm_list_ref($input, scm_from_size_t(i));
std::string key{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 0))};
SCM keyval, v_ref_0 = SCM_SIMPLE_VECTOR_REF(vec, 0);
GncOptionMultichoiceKeyType keytype;
if (scm_is_symbol(v_ref_0))
{
keyval = scm_symbol_to_string(SCM_SIMPLE_VECTOR_REF(vec, 0));
keytype = KeyType::SYMBOL;
}
else if (scm_is_string(v_ref_0))
{
keyval = SCM_SIMPLE_VECTOR_REF(vec, 0);
keytype = KeyType::STRING;
}
else if (scm_is_integer(v_ref_0))
{
keyval = scm_number_to_string(v_ref_0, scm_from_uint(10u));
keytype = KeyType::NUMBER;
}
else
throw std::invalid_argument("Unsupported key type in multichoice option.");
std::string key{scm_to_utf8_string(keyval)};
std::string name{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 1))};
std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))};
choices.push_back({std::move(key), std::move(name), std::move(desc)});
choices.push_back({std::move(key), std::move(name), std::move(desc), keytype});
}
$1 = &choices;
}
Expand Down Expand Up @@ -404,6 +424,45 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
%ignore gnc_register_start_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
%ignore gnc_register_end_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);


%ignore GncOptionMultichoiceKeyType;
/* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */
%inline %{
SCM get_scm_value(const GncOptionMultichoiceValue& option)
{
using KeyType = GncOptionMultichoiceKeyType;
auto scm_value = [](const char* value, KeyType keytype) -> SCM {
auto scm_str{scm_from_utf8_string(value)};
switch (keytype)
{
case KeyType::SYMBOL:
return scm_string_to_symbol(scm_str);
case KeyType::STRING:
return scm_str;
case KeyType::NUMBER:
return scm_string_to_number(scm_str, scm_from_int(10));
};
};

auto indexes = option.get_multiple();
if (indexes.empty())
indexes = option.get_default_multiple();
if (indexes.empty())
return SCM_BOOL_F;
if (indexes.size() == 1) // FIXME: Should use bool member to decide
return scm_value(option.permissible_value(indexes[0]),
option.get_keytype(indexes[0]));
auto values{scm_list_1(SCM_UNDEFINED)};
for(auto index : indexes)
{
auto val{scm_list_1(scm_value(option.permissible_value(index),
option.get_keytype(index)))};
values = scm_append(scm_list_2(val, values));
}
return scm_reverse(values);
}
%}

%include "gnc-option-date.hpp"
%include "gnc-option.hpp"
%include "gnc-option-impl.hpp"
Expand All @@ -421,6 +480,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
SCM get_scm_value()
{
return std::visit([](const auto& option)->SCM {
if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>)
return get_scm_value(option);
auto value{option.get_value()};
if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
SCM>)
Expand Down

0 comments on commit fba0248

Please sign in to comment.