/********************************************************************\ * gnc-option-impl.cpp -- Application options system * * Copyright (C) 2019 John Ralls * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * * * \********************************************************************/ //#include "options.h" #include "gnc-option-impl.hpp" #include #include #include #include extern "C" { #include "gnc-accounting-period.h" #include "gnc-ui-util.h" } static const QofLogModule log_module{"gnc.options"}; const std::string GncOptionMultichoiceValue::c_empty_string{""}; const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"}; using GncItem = std::pair; static GncItem make_gnc_item(const QofInstance* inst) { if (!inst) return std::make_pair("", guid_new_return()); auto type{qof_collection_get_type(qof_instance_get_collection(inst))}; auto guid{qof_instance_get_guid(inst)}; return std::make_pair(std::move(type), std::move(*const_cast(guid))); } static const QofInstance* qof_instance_from_gnc_item(const GncItem& item) { auto [type, guid] = item; auto book{gnc_get_current_book()}; auto coll{qof_book_get_collection(book, type)}; return static_cast(qof_collection_lookup_entity(coll, &guid)); } static bool operator!=(const GncItem& left, const GncItem& right) { auto [ltype, lguid]{left}; auto [rtype, rguid]{right}; return strcmp(rtype, ltype) && !guid_equal(&rguid, &lguid); } GncOptionQofInstanceValue::GncOptionQofInstanceValue( const char* section, const char* name, const char* key, const char* doc_string, const QofInstance* value, GncOptionUIType ui_type) : OptionClassifier{section, name, key, doc_string}, m_ui_type(ui_type), m_value{}, m_default_value{} { m_value = make_gnc_item(value); m_default_value = make_gnc_item(value); } GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) : OptionClassifier{from.m_section, from.m_name, from.m_sort_tag, from.m_doc_string}, m_ui_type(from.get_ui_type()), m_value{from.get_item()}, m_default_value{from.get_default_item()} { } void GncOptionQofInstanceValue::set_value(const QofInstance* new_value) { m_value = make_gnc_item(new_value); } void GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value) { m_value = m_default_value = make_gnc_item(new_value); } const QofInstance* GncOptionQofInstanceValue::get_value() const { return qof_instance_from_gnc_item(m_value); } const QofInstance* GncOptionQofInstanceValue::get_default_value() const { return qof_instance_from_gnc_item(m_default_value); } void GncOptionQofInstanceValue::reset_default_value() { m_value = m_default_value; } bool GncOptionQofInstanceValue::is_changed() const noexcept { return m_value != m_default_value; } bool GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept { QofInstance* inst{}; // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic try { auto guid{static_cast(gnc::GUID::from_string(str))}; inst = qof_instance_from_guid(&guid, m_ui_type); if (inst) { m_value = make_gnc_item(inst); return true; } } catch (const gnc::guid_syntax_exception& err) { PWARN("Failed to convert %s to a GUID", str.c_str()); } return false; } std::string GncOptionQofInstanceValue::serialize() const noexcept { auto inst{get_value()}; std::string retval; if (GNC_IS_COMMODITY(inst)) { auto commodity{GNC_COMMODITY(inst)}; if (!gnc_commodity_is_currency(commodity)) { auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))}; if (name_space && *name_space != '\0') { retval = name_space; retval += ":"; } } retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst)); return retval; } else { gnc::GUID guid{m_value.second}; retval = guid.to_string(); } return retval; } static gnc_commodity* gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space, std::string_view mnemonic) { auto book{gnc_get_current_book()}; auto table = gnc_commodity_table_get_table(book); return gnc_commodity_table_lookup(table, name_space.data(), mnemonic.data()); } gnc_commodity* GncOptionCommodityValue::get_value() const { return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic); } gnc_commodity* GncOptionCommodityValue::get_default_value() const { return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace, m_default_mnemonic); } void GncOptionCommodityValue::set_value(gnc_commodity* value) { if (!validate(value)) throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set."); m_mnemonic = gnc_commodity_get_mnemonic(value); m_namespace = gnc_commodity_get_namespace(value); } void GncOptionCommodityValue::set_default_value(gnc_commodity* value) { if (!validate(value)) throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set."); m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value); m_namespace = m_default_namespace = gnc_commodity_get_namespace(value); } void GncOptionCommodityValue::reset_default_value() { m_mnemonic = m_default_mnemonic; m_namespace = m_default_namespace; } bool GncOptionCommodityValue::is_changed() const noexcept { return m_namespace == m_default_namespace && m_mnemonic == m_default_mnemonic; } bool GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept { if (!GNC_IS_COMMODITY(comm)) return false; if (m_is_currency && !gnc_commodity_is_currency(comm)) return false; return true; } std::string GncOptionCommodityValue::serialize() const noexcept { if (m_is_currency) return m_mnemonic; else return m_namespace + ":" + m_mnemonic; } bool GncOptionCommodityValue::deserialize(const std::string& str) noexcept { auto sep{str.find(":")}; gnc_commodity* comm{}; std::string mnemonic, name_space; if (sep != std::string::npos) { name_space = str.substr(0, sep); mnemonic = str.substr(sep + 1, -1); } else { name_space = "CURRENCY"; mnemonic = str; } comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic); if (!validate(comm)) return false; m_namespace = std::move(name_space); m_mnemonic = std::move(mnemonic); return true; } bool GncOptionAccountListValue::validate(const GncOptionAccountList& values) const { if (values.empty()) return true; if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) && values.size() != 1) { std::cerr << "GncOptionAccountListValue::validate: Multiple values for a non-multiselect option." << std::endl; return false; } if (m_allowed.empty()) return true; auto book{gnc_get_current_book()}; for(auto& guid : values) { if (std::find(m_allowed.begin(), m_allowed.end(), xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end()) { std::cerr << "GncOptionAccountListValue::validate: Account " << gnc::GUID(guid).to_string() << " is not of an allowed type" << std::endl; return false; } } return true; } GncOptionAccountList GncOptionAccountListValue::get_value() const { return !m_value.empty() ? m_value : get_default_value(); } GncOptionAccountList GncOptionAccountListValue::get_default_value() const { if (!m_default_value.empty()) return m_default_value; /* If no default has been set and there's an allowed set then find the first * account that matches one of the allowed account types. */ GncOptionAccountList retval{}; if (m_allowed.empty()) return retval; auto root{gnc_get_current_root_account()}; auto account_list{gnc_account_get_descendants_sorted(root)}; if (!account_list) return retval; auto book{gnc_get_current_book()}; for (auto node = account_list; node; node = g_list_next (node)) { if (std::find(m_allowed.begin(), m_allowed.end(), xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end()) { retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data))); break; } } g_list_free(account_list); return retval; } static bool operator==(const GncGUID& l, const GncGUID& r) { return guid_equal(&l, &r); } bool GncOptionAccountListValue::is_changed() const noexcept { return m_value != m_default_value; } /** * Create a GList of account types to pass to gnc_account_sel_set_acct_filters. * gnc_account_sel_set_acct_filters copies the list so the intermediary caller * is responsible for freeing the list. * * @return an allocated GList* or nullptr if the list is empty. */ GList* GncOptionAccountListValue::account_type_list() const noexcept { if (m_allowed.empty()) return nullptr; GList* retval{nullptr}; for (auto type : m_allowed) retval = g_list_prepend(retval, GINT_TO_POINTER(type)); return g_list_reverse(retval); } bool GncOptionAccountSelValue::validate(const Account* value) const { if (m_allowed.empty() || !value) return true; if (std::find(m_allowed.begin(), m_allowed.end(), xaccAccountGetType(value)) == m_allowed.end()) return false; return true; } const Account* GncOptionAccountSelValue::get_value() const { auto book{gnc_get_current_book()}; return guid_equal(guid_null(), &m_value) ? get_default_value() : xaccAccountLookup(&m_value, book); } const Account* GncOptionAccountSelValue::get_default_value() const { if (!guid_equal(guid_null(), &m_default_value)) { auto book{gnc_get_current_book()}; return xaccAccountLookup(&m_default_value, book); } /* If no default has been set and there's an allowed set then find the first * account that matches one of the allowed account types. */ if (m_allowed.empty()) return nullptr; const Account* retval{nullptr}; auto root{gnc_get_current_root_account()}; auto account_list{gnc_account_get_descendants_sorted(root)}; if (!account_list) return nullptr; for (auto node = account_list; node; node = g_list_next (node)) if (std::find(m_allowed.begin(), m_allowed.end(), xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end()) { retval = GNC_ACCOUNT(node->data); break; } g_list_free(account_list); return retval; } /** * Create a GList of account types to pass to gnc_account_sel_set_acct_filters. * gnc_account_sel_set_acct_filters copies the list so the intermediary caller * is responsible for freeing the list. * * @return an allocated GList* or nullptr if the list is empty. */ GList* GncOptionAccountSelValue::account_type_list() const noexcept { if (m_allowed.empty()) return nullptr; GList* retval{nullptr}; for (auto type : m_allowed) retval = g_list_prepend(retval, GINT_TO_POINTER(type)); return g_list_reverse(retval); } bool GncOptionDateValue::validate(RelativeDatePeriod value) { if (m_period_set.empty()) return true; // No restrictions if (std::find(m_period_set.begin(), m_period_set.end(), value) != m_period_set.end()) return true; return false; } time64 GncOptionDateValue::get_value() const noexcept { if (m_period == RelativeDatePeriod::ABSOLUTE) return m_date; return gnc_relative_date_to_time64(m_period); } time64 GncOptionDateValue::get_default_value() const noexcept { if (m_default_period == RelativeDatePeriod::ABSOLUTE) return m_default_date; return gnc_relative_date_to_time64(m_default_period); } /* Use asserts for pre- and post-conditions to deliberately crash if they're not * met as the program design should prevent that from happening. */ size_t GncOptionDateValue::get_period_index() const noexcept { assert (m_period != RelativeDatePeriod::ABSOLUTE); assert(!m_period_set.empty()); auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)}; assert(item != m_period_set.end()); return item - m_period_set.begin(); } size_t GncOptionDateValue::get_default_period_index() const noexcept { assert(m_period != RelativeDatePeriod::ABSOLUTE); assert(!m_period_set.empty()); auto item{std::find(m_period_set.begin(), m_period_set.end(), m_default_period)}; assert (item != m_period_set.end()); return item - m_period_set.begin(); } void GncOptionDateValue::set_value(size_t index) noexcept { assert(!m_period_set.empty()); assert(index < m_period_set.size()); m_date = INT64_MAX; m_period = m_period_set[index]; } size_t GncOptionDateValue::permissible_value_index(const char* key) const noexcept { auto index = std::find_if(m_period_set.begin(), m_period_set.end(), [key](auto period) -> bool { return strcmp(gnc_relative_date_display_string(period), key) == 0; }); return index != m_period_set.end() ? index - m_period_set.begin() : 0; } static const char* date_type_str[] {"absolute", "relative"}; std::ostream& GncOptionDateValue::out_stream(std::ostream& oss) const noexcept { if (m_period == RelativeDatePeriod::ABSOLUTE) oss << date_type_str[0] << " . " << m_date; else oss << date_type_str[1] << " . " << gnc_relative_date_storage_string(m_period); return oss; } std::istream& GncOptionDateValue::in_stream(std::istream& iss) { char type_str[10]; //The length of both "absolute" and "relative" plus 1. iss.getline(type_str, sizeof(type_str), '.'); if(!iss) throw std::invalid_argument("Date Type separator missing"); /* strcmp is safe, istream::getline null terminates the buffer. */ if (strcmp(type_str, "absolute ") == 0) { time64 time; iss >> time; set_value(time); if (iss.get() != ')') iss.unget(); } else if (strcmp(type_str, "relative ") == 0) { std::string period_str; iss >> period_str; if (period_str.back() == ')') period_str.pop_back(); auto period = gnc_relative_date_from_storage_string(period_str.c_str()); if (period == RelativeDatePeriod::ABSOLUTE) { std::string err{"Unknown period string in date option: '"}; err += period_str; err += "'"; throw std::invalid_argument(err); } set_value(period); } else { std::string err{"Unknown date type string in date option: '"}; err += type_str; err += "'"; throw std::invalid_argument{err}; } return iss; } QofInstance* qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) { QofIdType qof_type; switch(type) { case GncOptionUIType::BUDGET: qof_type = "Budget"; break; case GncOptionUIType::JOB: qof_type = "gncJob"; break; case GncOptionUIType::CUSTOMER: qof_type = "gncCustomer"; break; case GncOptionUIType::VENDOR: qof_type = "gncVendor"; break; case GncOptionUIType::EMPLOYEE: qof_type = "gncEmployee"; break; case GncOptionUIType::INVOICE: qof_type = "gncInvoice"; break; case GncOptionUIType::TAX_TABLE: qof_type = "gncTaxtable"; break; case GncOptionUIType::ACCOUNT_LIST: case GncOptionUIType::ACCOUNT_SEL: default: qof_type = "Account"; break; } auto book{gnc_get_current_book()}; auto col{qof_book_get_collection(book, qof_type)}; return QOF_INSTANCE(qof_collection_lookup_entity(col, guid)); } QofInstance* qof_instance_from_string(const std::string& str, GncOptionUIType type) { QofInstance* retval{nullptr}; try { auto guid{static_cast(gnc::GUID::from_string(str))}; retval = qof_instance_from_guid(&guid, type); } catch (const gnc::guid_syntax_exception& err) { PWARN("Failed to convert %s to a GUID", str.c_str()); } return retval; } std::string qof_instance_to_string(const QofInstance* inst) { std::string retval; gnc::GUID guid{*qof_instance_get_guid(inst)}; retval = guid.to_string(); return retval; } template void GncOptionValue::set_value(ValueType new_value) { m_value = new_value; } template void GncOptionValue::set_default_value(ValueType new_value) { m_value = m_default_value = new_value; } template void GncOptionValue::reset_default_value() { m_value = m_default_value; } /* Missing on purpose: QofQuery because for current usage it's serialized with * gnc_query2scm. The future is to replace QofQuery with SQL queries so there's * not much point to spending the time to create a std::string serialization for * them. */ template std::string GncOptionValue::serialize() const noexcept { static const std::string no_value{"No Value"}; if constexpr(std::is_same_v) return m_value ? qof_instance_to_string(m_value) : no_value; if constexpr(std::is_same_v) { if (!m_value) return no_value; auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))}; auto type{qofOwnerGetType(m_value)}; std::ostringstream ostr{}; ostr << type << " " << guid; return ostr.str(); } else if constexpr(is_same_decayed_v) return m_value; else if constexpr(is_same_decayed_v) return m_value ? "True" : "False"; else if constexpr(std::is_arithmetic_v) return std::to_string(m_value); else return "Serialization not implemented"; } template bool GncOptionValue::deserialize(const std::string& str) noexcept { if constexpr(std::is_same_v) set_value(qof_instance_from_string(str, get_ui_type())); if constexpr(std::is_same_v) { std::istringstream istr{str}; std::string type, guid; istr >> type >> guid; auto inst{qof_instance_from_string(guid, get_ui_type())}; qofOwnerSetEntity(const_cast(m_value), inst); } else if constexpr(is_same_decayed_v) set_value(str); else if constexpr(is_same_decayed_v) set_value(str == "True"); else if constexpr(is_same_decayed_v) set_value(stoi(str)); else if constexpr(is_same_decayed_v) set_value(stoll(str)); else if constexpr(is_same_decayed_v) set_value(stod(str)); else return false; return true; } template <> void GncOptionValue::set_value(SCM new_value) { if (m_value) scm_gc_unprotect_object(m_value); m_value = new_value; scm_gc_protect_object(m_value); } template <> void GncOptionValue::set_default_value(SCM new_value) { if (m_value) scm_gc_unprotect_object(m_value); if (m_default_value) scm_gc_unprotect_object(m_default_value); m_value = m_default_value = new_value; scm_gc_protect_object(m_value); scm_gc_protect_object(m_default_value); } template <> void GncOptionValue::reset_default_value() { if (m_value) scm_gc_unprotect_object(m_value); m_value = m_default_value; scm_gc_protect_object(m_value); } std::string GncOptionAccountListValue::serialize() const noexcept { static const std::string no_value{"No Value"}; std::string retval; bool first = true; if (m_value.empty()) return no_value; for (auto val : m_value) { if (!first) retval += " "; first = false; retval += guid_to_string(&val); } return retval; } bool GncOptionAccountListValue::deserialize(const std::string& str) noexcept { if (str.empty() || str.size() < GUID_ENCODING_LENGTH) return false; m_value.clear(); m_value.reserve(str.size() / GUID_ENCODING_LENGTH); bool first = true; size_t pos{}; while (pos + GUID_ENCODING_LENGTH < str.size()) { if (!first) ++pos; first = false; GncGUID guid{}; string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid); m_value.push_back(guid); pos += GUID_ENCODING_LENGTH; } return true; } std::string GncOptionAccountSelValue::serialize() const noexcept { static const std::string no_value{"No Value"}; return guid_equal(guid_null(), &m_value) ? no_value : guid_to_string(&m_value); } bool GncOptionAccountSelValue::deserialize(const std::string& str) noexcept { set_value(reinterpret_cast(qof_instance_from_string(str, get_ui_type()))); return true; } std::string GncOptionMultichoiceValue::serialize() const noexcept { static const std::string no_value{"No Value"}; std::string retval; bool first = true; if (m_value.empty()) return no_value; for (auto index : m_value) { if (!first) retval += " "; first = false; retval += std::get<0>(m_choices[index]); } return retval; } bool GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept { static const auto size_t_max = std::numeric_limits::max(); if (str.empty()) return false; size_t pos{}; while (pos < str.size()) { auto endpos{str.find(' ', pos)}; if (endpos == std::string::npos) endpos = str.size(); //need a null-terminated char* to pass to permissible_value_index auto index{permissible_value_index(str.substr(pos, endpos).c_str())}; if (index == size_t_max) return false; m_value.push_back(index); pos = endpos + 1; } return true; } template std::string GncOptionRangeValue::serialize() const noexcept { if constexpr (std::is_arithmetic_v) return std::to_string(m_value); return ""; } template bool GncOptionRangeValue::deserialize(const std::string& str) noexcept { if constexpr(is_same_decayed_v) set_value(stoi(str)); else if constexpr(is_same_decayed_v) set_value(stod(str)); return true; } std::string GncOptionDateValue::serialize() const noexcept { std::string retval{"("}; if (m_period == RelativeDatePeriod::ABSOLUTE) { retval += date_type_str[0]; retval += " . "; retval += std::to_string(m_date); } else { retval += date_type_str[1]; retval += " . "; retval += gnc_relative_date_storage_string(m_period); } retval += ")"; return retval; } bool GncOptionDateValue::deserialize(const std::string& str) noexcept { //The length of both "absolute" and "relative". static constexpr size_t date_type_len{9}; // date_type_len plus the length of " . ". static constexpr size_t date_value_pos{12}; auto type_str{str.substr(0, date_type_len)}; auto period_str{str.substr(date_value_pos)}; if (type_str == "absolute") { // Need a cast to disambiguate from time64. set_value(static_cast(std::stoll(period_str))); return true; } else if (type_str == "relative ") { auto period = gnc_relative_date_from_storage_string(period_str.c_str()); if (period == RelativeDatePeriod::ABSOLUTE) { PWARN("Unknown period string in date option: '%s'", period_str.c_str()); return false; } set_value(period); return true; } else { PWARN("Unknown date type string in date option: '%s'", type_str.c_str()); return false; } } std::istream& operator>> (std::istream& iss, GncOptionCommodityValue& opt) { std::string instr; iss >> instr; if (!opt.deserialize(instr)) throw std::invalid_argument("Invalid commodity string in stream."); return iss; } template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template void GncOptionValue::set_value(bool); template void GncOptionValue::set_value(int); template void GncOptionValue::set_value(int64_t); template void GncOptionValue::set_value(double); template void GncOptionValue::set_value(char*); template void GncOptionValue::set_value(const char*); template void GncOptionValue::set_value(std::string); template void GncOptionValue::set_value(const QofQuery*); template void GncOptionValue::set_value(const GncOwner*); template void GncOptionValue::set_value(RelativeDatePeriod); template void GncOptionValue::set_value(size_t); template void GncOptionValue::set_value(GncOptionAccountList); template void GncOptionValue::set_value(GncMultichoiceOptionIndexVec); template void GncOptionValue::set_default_value(bool); template void GncOptionValue::set_default_value(int); template void GncOptionValue::set_default_value(int64_t); template void GncOptionValue::set_default_value(double); template void GncOptionValue::set_default_value(char*); template void GncOptionValue::set_default_value(const char*); template void GncOptionValue::set_default_value(std::string); template void GncOptionValue::set_default_value(const QofQuery*); template void GncOptionValue::set_default_value(const GncOwner*); template void GncOptionValue::set_default_value(RelativeDatePeriod); template void GncOptionValue::set_default_value(size_t); template void GncOptionValue::set_default_value(GncOptionAccountList); template void GncOptionValue::set_default_value(GncMultichoiceOptionIndexVec); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template void GncOptionValue::reset_default_value(); template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionValue::serialize() const noexcept; template std::string GncOptionRangeValue::serialize() const noexcept; template std::string GncOptionRangeValue::serialize() const noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionValue::deserialize(const std::string&) noexcept; template bool GncOptionRangeValue::deserialize(const std::string&) noexcept; template bool GncOptionRangeValue::deserialize(const std::string&) noexcept;