[c++options] Implement GncOptionGncOwnerValue class.

GncOwners aren't QofInstances and have limited lifetimes so an option
must hold its own, wrapped in std::unique_ptr for memory management.
This commit is contained in:
John Ralls 2023-03-14 15:50:34 -07:00
parent 8db8105850
commit a44b3664e2
8 changed files with 249 additions and 13 deletions

View File

@ -740,6 +740,10 @@ gnc_option_test_book_destroy(QofBook* book)
wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
%ignore swig_get_option(GncOption&); %ignore swig_get_option(GncOption&);
%ignore GncOwnerDeleter;
%ignore
GncOptionGncOwnerValue::GncOptionGncOwnerValue(GncOptionGncOwnerValue &&);
%inline %{ %inline %{
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
@ -1151,7 +1155,6 @@ inline SCM return_scm_value(ValueType value)
%template(gnc_make_bool_option) gnc_make_option<bool>; %template(gnc_make_bool_option) gnc_make_option<bool>;
%template(gnc_make_int64_option) gnc_make_option<int64_t>; %template(gnc_make_int64_option) gnc_make_option<int64_t>;
%template(gnc_make_query_option) gnc_make_option<const QofQuery*>; %template(gnc_make_query_option) gnc_make_option<const QofQuery*>;
%template(gnc_make_owner_option) gnc_make_option<const GncOwner*>;
%rename (get_value) GncOption::get_scm_value; %rename (get_value) GncOption::get_scm_value;
%rename (get_default_value) GncOption::get_scm_default_value; %rename (get_default_value) GncOption::get_scm_default_value;
@ -1247,13 +1250,12 @@ inline SCM return_scm_value(ValueType value)
return scm_simple_format(SCM_BOOL_F, date_fmt, value); return scm_simple_format(SCM_BOOL_F, date_fmt, value);
} }
if constexpr (is_same_decayed_v<decltype(option), if constexpr (is_GncOwnerValue_v<decltype(option)>)
GncOptionValue<const GncOwner*>>)
{ {
auto value{option.get_value()}; auto value{option.get_value()};
auto guid{scm_from_utf8_string(qof_instance_to_string(qofOwnerGetOwner(value)).c_str())}; auto guid{scm_from_utf8_string(qof_instance_to_string(qofOwnerGetOwner(value)).c_str())};
auto type{scm_from_long(gncOwnerGetType(value))}; auto type{scm_from_long(gncOwnerGetType(value))};
return scm_simple_format(SCM_BOOL_F, ticked_format_str, return scm_simple_format(SCM_BOOL_F, list_format_str,
scm_list_1(scm_cons(type, guid))); scm_list_1(scm_cons(type, guid)));
} }
if constexpr (is_QofQueryValue_v<decltype(option)>) if constexpr (is_QofQueryValue_v<decltype(option)>)
@ -1420,6 +1422,24 @@ inline SCM return_scm_value(ValueType value)
} }
return; return;
} }
if constexpr (is_GncOwnerValue_v<decltype(option)>)
{
if (scm_is_pair(new_value))
{
GncOwner owner{};
owner.type = static_cast<GncOwnerType>(scm_to_int(scm_car(new_value)));
auto strval{scm_to_utf8_string(scm_cdr(new_value))};
owner.owner.undefined = qof_instance_from_string(strval, option.get_ui_type());
option.set_value(&owner);
}
else
{
auto val{scm_to_value<const GncOwner*>(new_value)};
option.set_value(val);
}
return;
}
if constexpr (is_QofQueryValue_v<decltype(option)>) if constexpr (is_QofQueryValue_v<decltype(option)>)
{ {
if (scm_is_pair(new_value)) if (scm_is_pair(new_value))
@ -1516,6 +1536,37 @@ inline SCM return_scm_value(ValueType value)
} }
return; return;
} }
if constexpr (is_GncOwnerValue_v<decltype(option)>)
{
if (scm_is_pair(new_value))
{
GncOwner owner{};
owner.type = static_cast<GncOwnerType>(scm_to_int(scm_car(new_value)));
auto strval{scm_to_utf8_string(scm_cdr(new_value))};
owner.owner.undefined = qof_instance_from_string(strval, option.get_ui_type());
option.set_default_value(&owner);
}
else
{
auto val{scm_to_value<const GncOwner*>(new_value)};
option.set_default_value(val);
}
return;
}
if constexpr (is_QofQueryValue_v<decltype(option)>)
{
if (scm_is_pair(new_value))
{
auto val{gnc_scm2query(new_value)};
option.set_default_value(val);
}
else
{
auto val{scm_to_value<const QofQuery*>(new_value)};
option.set_default_value(val);
}
return;
}
if constexpr (is_same_decayed_v<decltype(option), if constexpr (is_same_decayed_v<decltype(option),
GncOptionAccountSelValue>) GncOptionAccountSelValue>)
{ {
@ -1674,6 +1725,26 @@ gnc_register_multichoice_callback_option(GncOptionDBPtr& db,
} }
} }
static GncOption*
gnc_make_gncowner_option(const char* section,
const char* name, const char* key,
const char* doc_string,
const GncOwner* value,
GncOptionUIType ui_type)
{
try {
return new GncOption(GncOptionGncOwnerValue{section, name, key,
doc_string,
value, ui_type});
}
catch (const std::exception& err)
{
std::cerr << "Make GncOwner option threw unexpected exception"
<< err.what() << ", option not created." << std::endl;
return nullptr;
}
}
static GncOption* static GncOption*
gnc_make_account_list_option(const char* section, gnc_make_account_list_option(const char* section,
const char* name, const char* key, const char* name, const char* key,

View File

@ -297,7 +297,7 @@
((eqv? owner-type GNC-OWNER-EMPLOYEE) (gncEmployeeLookupFlip guid book)) ((eqv? owner-type GNC-OWNER-EMPLOYEE) (gncEmployeeLookupFlip guid book))
((eqv? owner-type GNC-OWNER-JOB) (gncJobLookupFlip guid book))))) ((eqv? owner-type GNC-OWNER-JOB) (gncJobLookupFlip guid book)))))
(gnc-make-owner-option section name key docstring defval ui-type))) (gnc-make-gncowner-option section name key docstring defval ui-type)))
(define-public (gnc:make-invoice-option section name key docstring getter validator) (define-public (gnc:make-invoice-option section name key docstring getter validator)
(issue-deprecation-warning "gnc:make-invoice-option is deprecated. Make and register the option in one command with gnc-register-ionvoice-option.") (issue-deprecation-warning "gnc:make-invoice-option is deprecated. Make and register the option in one command with gnc-register-ionvoice-option.")
(let ((defval (if getter (getter) #f))) (let ((defval (if getter (getter) #f)))

View File

@ -588,7 +588,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
(test-equal "owner unchanged" test-unchanged-section-output-template (test-equal "owner unchanged" test-unchanged-section-output-template
(gnc:generate-restore-forms odb "options")) (gnc:generate-restore-forms odb "options"))
(let* ((option (gnc:lookup-option odb "foo" "bar")) (let* ((option (gnc:lookup-option odb "foo" "bar"))
(test-template test-literal-output-template) (test-template test-list-output-template)
(book (gnc-get-current-book)) (book (gnc-get-current-book))
(owner (gncOwnerNew))) (owner (gncOwnerNew)))
(gncOwnerInitCustomer owner (gncCustomerCreate book)) (gncOwnerInitCustomer owner (gncCustomerCreate book))

View File

@ -37,6 +37,112 @@ static const QofLogModule log_module{"gnc.options"};
const std::string GncOptionMultichoiceValue::c_empty_string{""}; const std::string GncOptionMultichoiceValue::c_empty_string{""};
const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"}; const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
static inline GncOwnerType
ui_type_to_owner_type(GncOptionUIType ui_type)
{
if (ui_type == GncOptionUIType::CUSTOMER)
return GNC_OWNER_CUSTOMER;
if (ui_type == GncOptionUIType::VENDOR)
return GNC_OWNER_VENDOR;
if (ui_type == GncOptionUIType::EMPLOYEE)
return GNC_OWNER_EMPLOYEE;
return GNC_OWNER_NONE;
}
static GncOwner*
make_owner_ptr(const GncOwner* owner)
{
if (!owner)
return nullptr;
auto rv{gncOwnerNew()};
gncOwnerCopy(owner, rv);
return rv;
}
GncOptionGncOwnerValue::GncOptionGncOwnerValue(
const char* section, const char* name,
const char* key, const char* doc_string,
const GncOwner* value, GncOptionUIType ui_type) :
OptionClassifier{section, name, key, doc_string},
m_ui_type(ui_type), m_value{make_owner_ptr(value)},
m_default_value{make_owner_ptr(value)} {}
GncOptionGncOwnerValue::GncOptionGncOwnerValue(const GncOptionGncOwnerValue& 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{make_owner_ptr(from.get_value())},
m_default_value{make_owner_ptr(from.get_default_value())} {}
void
GncOptionGncOwnerValue::set_value(const GncOwner* new_value)
{
m_value.reset(make_owner_ptr(new_value));
}
void
GncOptionGncOwnerValue::set_default_value(const GncOwner *new_value)
{
m_value.reset(make_owner_ptr(new_value));
m_default_value.reset(make_owner_ptr(new_value));
}
const GncOwner*
GncOptionGncOwnerValue::get_value() const
{
return m_value.get();
}
const GncOwner*
GncOptionGncOwnerValue::get_default_value() const
{
return m_default_value.get();
}
void
GncOptionGncOwnerValue::reset_default_value()
{
gncOwnerCopy(m_default_value.get(), m_value.get());
}
bool
GncOptionGncOwnerValue::is_changed() const noexcept
{
return gncOwnerEqual(m_value.get(), m_default_value.get());
}
bool
GncOptionGncOwnerValue::deserialize(const std::string& str) noexcept
{
try {
auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
auto inst = qof_instance_from_guid(&guid, m_ui_type);
if (inst)
{
GncOwner owner{};
owner.type = ui_type_to_owner_type(m_ui_type);
owner.owner.undefined = inst;
set_default_value(&owner);
return true;
}
}
catch (const gnc::guid_syntax_exception& err)
{
PWARN("Failed to convert %s to a GUID", str.c_str());
}
return false;
}
std::string
GncOptionGncOwnerValue::serialize() const noexcept
{
auto owner{m_value.get()};
gnc::GUID guid{*qof_instance_get_guid(static_cast<QofInstance*>(owner->owner.undefined))};
std::string retval{guid.to_string()};
return retval;
}
using GncItem = std::pair<QofIdTypeConst, GncGUID>; using GncItem = std::pair<QofIdTypeConst, GncGUID>;
static GncItem static GncItem
@ -60,6 +166,7 @@ get_current_root_account(void)
{ {
return gnc_book_get_root_account(get_current_book()); return gnc_book_get_root_account(get_current_book());
} }
static const QofInstance* static const QofInstance*
qof_instance_from_gnc_item(const GncItem& item) qof_instance_from_gnc_item(const GncItem& item)
{ {

View File

@ -113,6 +113,54 @@ private:
ValueType m_default_value; ValueType m_default_value;
}; };
/** class GncOptionGncOwnerValue
*
* Unlike QofInstance based classes GncOwners are created on the fly, aren't
* placed in QofCollection, and therefore their lifetimes have to be managed.
* We use GncOwnerPtr for the purpose.
*/
struct GncOwnerDeleter
{
void operator()(GncOwner* o) {
g_free(o);
}
};
using GncOwnerPtr = std::unique_ptr<GncOwner, GncOwnerDeleter>;
class GncOptionGncOwnerValue: public OptionClassifier {
public:
GncOptionGncOwnerValue(
const char* section, const char* name,
const char* key, const char* doc_string,
const GncOwner* value,
GncOptionUIType ui_type = GncOptionUIType::INTERNAL);
GncOptionGncOwnerValue(const GncOptionGncOwnerValue& from);
GncOptionGncOwnerValue(GncOptionGncOwnerValue&&) = default;
~GncOptionGncOwnerValue() = default;
const GncOwner* get_value() const;
const GncOwner* get_default_value() const;
void set_value(const GncOwner* new_value);
void set_default_value(const GncOwner* new_value);
void reset_default_value();
bool is_changed() const noexcept;
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
std::string serialize() const noexcept;
bool deserialize(const std::string& str) noexcept;
private:
GncOptionUIType m_ui_type;
GncOwnerPtr m_value;
GncOwnerPtr m_default_value;
};
/** class GncOptionQofinstanceValue
*
* QofInstances know what type they are but getting them to tell you is a pain
* so we put them in a pair with a type identifier.
*/
using GncItem = std::pair<QofIdTypeConst, GncGUID>; using GncItem = std::pair<QofIdTypeConst, GncGUID>;
class GncOptionQofInstanceValue: public OptionClassifier { class GncOptionQofInstanceValue: public OptionClassifier {
@ -201,6 +249,16 @@ QofInstance* qof_instance_from_string(const std::string& str,
QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type); QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type);
std::string qof_instance_to_string(const QofInstance* inst); std::string qof_instance_to_string(const QofInstance* inst);
template <typename T>
struct is_GncOwnerValue
{
static constexpr bool value =
std::is_same_v<std::decay_t<T>, GncOptionGncOwnerValue>;
};
template <typename T> inline constexpr bool
is_GncOwnerValue_v = is_GncOwnerValue<T>::value;
template <typename T> template <typename T>
struct is_QofInstanceValue struct is_QofInstanceValue
{ {

View File

@ -25,6 +25,7 @@
#include "gnc-option-impl.hpp" #include "gnc-option-impl.hpp"
#include "gnc-option-uitype.hpp" #include "gnc-option-uitype.hpp"
#include "gnc-option-ui.hpp" #include "gnc-option-ui.hpp"
#include "gncOwner.h"
static const char* log_module{"gnc.app-utils.gnc-option"}; static const char* log_module{"gnc.app-utils.gnc-option"};
@ -466,8 +467,6 @@ template GncOption::GncOption(const char*, const char*, const char*,
const char*, std::string, GncOptionUIType); const char*, std::string, GncOptionUIType);
template GncOption::GncOption(const char*, const char*, const char*, template GncOption::GncOption(const char*, const char*, const char*,
const char*, const QofQuery*, GncOptionUIType); const char*, const QofQuery*, GncOptionUIType);
template GncOption::GncOption(const char*, const char*, const char*,
const char*, const GncOwner*, GncOptionUIType);
template bool GncOption::get_value<bool>() const; template bool GncOption::get_value<bool>() const;
template int GncOption::get_value<int>() const; template int GncOption::get_value<int>() const;
@ -477,6 +476,7 @@ template uint16_t GncOption::get_value<uint16_t>() const;
template const char* GncOption::get_value<const char*>() const; template const char* GncOption::get_value<const char*>() const;
template std::string GncOption::get_value<std::string>() const; template std::string GncOption::get_value<std::string>() const;
template const QofInstance* GncOption::get_value<const QofInstance*>() const; template const QofInstance* GncOption::get_value<const QofInstance*>() const;
template const GncOwner* GncOption::get_value<const GncOwner*>() const;
template gnc_commodity* GncOption::get_value<gnc_commodity*>() const; template gnc_commodity* GncOption::get_value<gnc_commodity*>() const;
template const Account* GncOption::get_value<const Account*>() const; template const Account* GncOption::get_value<const Account*>() const;
template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const; template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
@ -506,6 +506,7 @@ template void GncOption::set_value(char*);
template void GncOption::set_value(const char*); template void GncOption::set_value(const char*);
template void GncOption::set_value(std::string); template void GncOption::set_value(std::string);
template void GncOption::set_value(const QofInstance*); template void GncOption::set_value(const QofInstance*);
template void GncOption::set_value(const GncOwner*);
template void GncOption::set_value(gnc_commodity*); template void GncOption::set_value(gnc_commodity*);
template void GncOption::set_value(const Account*); template void GncOption::set_value(const Account*);
template void GncOption::set_value(RelativeDatePeriod); template void GncOption::set_value(RelativeDatePeriod);
@ -556,5 +557,3 @@ template GncOption* gnc_make_option<bool>(const char*, const char*, const char*,
template GncOption* gnc_make_option<int64_t>(const char*, const char*, template GncOption* gnc_make_option<int64_t>(const char*, const char*,
const char*, const char*, int64_t, const char*, const char*, int64_t,
GncOptionUIType); GncOptionUIType);

View File

@ -56,6 +56,7 @@ using QofQuery = _QofQuery;
struct QofInstance_s; struct QofInstance_s;
using QofInstance = QofInstance_s; using QofInstance = QofInstance_s;
template <typename ValueType> class GncOptionValue; template <typename ValueType> class GncOptionValue;
class GncOptionGncOwnerValue;
class GncOptionQofInstanceValue; class GncOptionQofInstanceValue;
class GncOptionAccountListValue; class GncOptionAccountListValue;
class GncOptionAccountSelValue; class GncOptionAccountSelValue;
@ -101,8 +102,8 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
GncOptionValue<bool>, GncOptionValue<bool>,
GncOptionValue<int64_t>, GncOptionValue<int64_t>,
GncOptionQofInstanceValue, GncOptionQofInstanceValue,
GncOptionGncOwnerValue,
GncOptionValue<const QofQuery*>, GncOptionValue<const QofQuery*>,
GncOptionValue<const GncOwner*>,
GncOptionValue<GncOptionReportPlacementVec>, GncOptionValue<GncOptionReportPlacementVec>,
GncOptionAccountListValue, GncOptionAccountListValue,
GncOptionAccountSelValue, GncOptionAccountSelValue,

View File

@ -809,8 +809,8 @@ gnc_register_owner_option(GncOptionDB* db, const char* section,
default: default:
uitype = GncOptionUIType::INTERNAL; uitype = GncOptionUIType::INTERNAL;
}; };
GncOption option{section, name, key, doc_string, value, GncOption option{GncOptionGncOwnerValue{section, name, key, doc_string,
uitype}; value, uitype}};
db->register_option(section, std::move(option)); db->register_option(section, std::move(option));
} }