gnucash stable: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Mon Apr 15 19:31:05 EDT 2024


Updated	 via  https://github.com/Gnucash/gnucash/commit/3d173681 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2b712197 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1b50c626 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/606da28d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f70ee754 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f7a5a82 (commit)
	from  https://github.com/Gnucash/gnucash/commit/6506e485 (commit)



commit 3d173681cc5cfa311caa301d2250d5808f536486
Merge: 6506e48542 2b71219766
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Apr 16 07:30:27 2024 +0800

    Merge branch 'account-splits-vector' into stable #1917


commit 2b712197662da8b0e13fb8df867e444f063b8b72
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Apr 16 07:30:15 2024 +0800

    [Account.cpp] remove obsolete comment

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index d25d01aeca..504e1794e2 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -3985,17 +3985,6 @@ xaccAccountGetSplits (const Account *account)
     return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits : SplitsVec{};
 }
 
-/* THIS API NEEDS TO CHANGE.
- *
- * This code exposes the internal structure of the account object to
- * external callers by returning the actual list used by the object.
- * It should instead return a copy of the split list that the caller
- * is required to free.  That change would provide the freedom of
- * allowing the internal organization to change data structures if
- * necessary for whatever reason, while leaving the external API
- * unchanged. */
-/* XXX: violates the const'ness by forcing a sort before returning
- * the splitlist */
 SplitList *
 xaccAccountGetSplitList (const Account *acc)
 {

commit 1b50c6261acfd6e21f406ca7260143977d43f06d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Apr 15 22:45:13 2024 +0800

    g_list_free xaccAccountGetSplitList

diff --git a/gnucash/gnome-utils/gnc-autoclear.c b/gnucash/gnome-utils/gnc-autoclear.c
index 0b4ff9249d..b6e4120bad 100644
--- a/gnucash/gnome-utils/gnc-autoclear.c
+++ b/gnucash/gnome-utils/gnc-autoclear.c
@@ -143,6 +143,7 @@ gnc_autoclear_get_splits (Account *account, gnc_numeric toclear_value,
         else
             nc_list = g_list_prepend (nc_list, split);
     }
+    g_list_free (acc_splits);
 
     if (gnc_numeric_zero_p (toclear_value))
     {
diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index 71e66165ae..cea2cc7f44 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -1094,6 +1094,7 @@ RESTART:
                     GList *splits = xaccAccountGetSplitList (acc);
                     g_list_foreach (splits,
                                     (GFunc)gnc_sx_scrub_split_numerics, NULL);
+                    g_list_free (splits);
                 }
                 g_list_free (children);
             }
diff --git a/gnucash/gnome/dialog-lot-viewer.c b/gnucash/gnome/dialog-lot-viewer.c
index 2856186731..cf1d4a69ca 100644
--- a/gnucash/gnome/dialog-lot-viewer.c
+++ b/gnucash/gnome/dialog-lot-viewer.c
@@ -236,6 +236,7 @@ lv_show_splits_free (GNCLotViewer *lv)
     /* display list */
     gnc_split_viewer_fill(lv, lv->split_free_store, filtered_list);
     g_list_free (filtered_list);
+    g_list_free (split_list);
 }
 
 /* ======================================================================== */
diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c
index a85b84ab4c..4c1a54ee91 100644
--- a/gnucash/gnome/dialog-sx-editor.c
+++ b/gnucash/gnome/dialog-sx-editor.c
@@ -847,6 +847,7 @@ gnc_sxed_check_consistent (GncSxEditorDialog *sxed)
         g_hash_table_foreach (txns, set_sums_to_zero, NULL);
 
         splitCount += g_list_length (splitList);
+        g_list_free (splitList);
 
         xaccAccountForEachTransaction (tmpl_acct, check_transaction_splits, &sd);
 
@@ -1510,6 +1511,7 @@ schedXact_editor_populate (GncSxEditorDialog *sxed)
             splitReg = gnc_ledger_display_get_split_register (sxed->ledger);
             gnc_split_register_load (splitReg, splitList, NULL);
         } /* otherwise, use the existing stuff. */
+        g_list_free (splitList);
     }
 
     /* Update the example cal */
diff --git a/gnucash/register/ledger-core/split-register-control.cpp b/gnucash/register/ledger-core/split-register-control.cpp
index a1578868cd..c0a8c6519f 100644
--- a/gnucash/register/ledger-core/split-register-control.cpp
+++ b/gnucash/register/ledger-core/split-register-control.cpp
@@ -683,6 +683,7 @@ gnc_find_split_in_account_by_memo (Account *account, const char *memo,
             rv = split;
     }
 
+    g_list_free (splits);
     return rv;
 }
 
diff --git a/libgnucash/engine/SX-book.c b/libgnucash/engine/SX-book.c
index b784bab150..1aff588358 100644
--- a/libgnucash/engine/SX-book.c
+++ b/libgnucash/engine/SX-book.c
@@ -385,6 +385,7 @@ gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct)
 
             guid_free (guid);
         }
+        g_list_free (splits);
     }
     return g_list_reverse (rtn);
 }
diff --git a/libgnucash/engine/SchedXaction.c b/libgnucash/engine/SchedXaction.c
index 5ad3480b9b..266bbd99f3 100644
--- a/libgnucash/engine/SchedXaction.c
+++ b/libgnucash/engine/SchedXaction.c
@@ -454,6 +454,7 @@ delete_template_trans(SchedXaction *sx)
                    sxprivTransMapDelete,
                    NULL);
 
+    g_list_free (templ_acct_splits);
     g_list_free (templ_acct_transactions);
     return;
 }
diff --git a/libgnucash/engine/SchedXaction.h b/libgnucash/engine/SchedXaction.h
index e58b6d2042..9e1f65719a 100644
--- a/libgnucash/engine/SchedXaction.h
+++ b/libgnucash/engine/SchedXaction.h
@@ -217,6 +217,7 @@ gint gnc_sx_get_instance_count( const SchedXaction *sx, /*@ null @*/ SXTmpStateD
 */
 void gnc_sx_set_instance_count( SchedXaction *sx, gint instanceNum );
 
+/* must be g_list_freed */
 GList *xaccSchedXactionGetSplits( const SchedXaction *sx );
 void xaccSchedXactionSetSplits( SchedXaction *sx, GList *newSplits );
 
diff --git a/libgnucash/engine/ScrubBusiness.c b/libgnucash/engine/ScrubBusiness.c
index 4af048656c..f2b765beed 100644
--- a/libgnucash/engine/ScrubBusiness.c
+++ b/libgnucash/engine/ScrubBusiness.c
@@ -410,6 +410,7 @@ gncScrubLotDanglingPayments (GNCLot *lot)
 
         filtered_list = g_list_prepend (filtered_list, free_split);
     }
+    g_list_free (split_list);
 
     filtered_list = g_list_reverse (filtered_list);
     match_list = gncSLFindOffsSplits (filtered_list, ll_val);
@@ -723,6 +724,7 @@ restart:
               curr_split_no + 1, split_count);
         curr_split_no++;
     }
+    g_list_free (splits);
     xaccAccountCommitEdit(acc);
     (percentagefunc)(NULL, -1.0);
     LEAVE ("(acc=%s)", str);

commit 606da28d25f8ed1cbd6b9ee0cedbbecd3de8a40c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Apr 14 16:20:37 2024 +0800

    use xaccAccountGetSplitsSize

diff --git a/gnucash/gnome-utils/dialog-account.c b/gnucash/gnome-utils/dialog-account.c
index 41b2832f22..bb7e0746b2 100644
--- a/gnucash/gnome-utils/dialog-account.c
+++ b/gnucash/gnome-utils/dialog-account.c
@@ -247,7 +247,7 @@ gnc_account_opening_balance_button_update (AccountWindow *aw, gnc_commodity *com
 {
     Account *account = aw_get_account (aw);
     Account *ob_account = gnc_account_lookup_by_opening_balance (gnc_book_get_root_account (aw->book), commodity);
-    gboolean has_splits = (xaccAccountGetSplitList (account) != NULL);
+    gboolean has_splits = (xaccAccountGetSplitsSize (account) != 0);
 
     if (aw->type != ACCT_TYPE_EQUITY)
     {
@@ -1614,7 +1614,7 @@ gnc_account_window_create (GtkWindow *parent, AccountWindow *aw)
     gtk_box_pack_start (GTK_BOX(box), aw->commodity_edit, TRUE, TRUE, 0);
     gtk_widget_show (aw->commodity_edit);
     // If the account has transactions, prevent changes by displaying a label and tooltip
-    if (xaccAccountGetSplitList (aw_get_account (aw)) != NULL)
+    if (xaccAccountGetSplitsSize (aw_get_account (aw)) != 0)
     {
         gtk_widget_set_tooltip_text (aw->commodity_edit, tt);
         gtk_widget_set_sensitive (aw->commodity_edit, FALSE);
@@ -1714,7 +1714,7 @@ gnc_account_window_create (GtkWindow *parent, AccountWindow *aw)
     //   immutable if gnucash depends on details that would be lost/missing
     //   if changing from/to such a type. At the time of this writing the
     //   immutable types are AR, AP and trading types.
-    if (xaccAccountGetSplitList (aw_get_account (aw)) != NULL)
+    if (xaccAccountGetSplitsSize (aw_get_account (aw)) != 0)
     {
         GNCAccountType atype = xaccAccountGetType (aw_get_account (aw));
         compat_types = xaccAccountTypesCompatibleWith (atype);
@@ -2128,7 +2128,7 @@ gnc_ui_edit_account_window (GtkWindow *parent, Account *account)
     gnc_resume_gui_refresh ();
 
     gtk_widget_show_all (aw->dialog);
-    if (xaccAccountGetSplitList (account) != NULL)
+    if (xaccAccountGetSplitsSize (account) != 0)
         gtk_widget_hide (aw->opening_balance_page);
 
     parent_acct = gnc_account_get_parent (account);
diff --git a/libgnucash/engine/ScrubBusiness.c b/libgnucash/engine/ScrubBusiness.c
index 0a68fc9531..4af048656c 100644
--- a/libgnucash/engine/ScrubBusiness.c
+++ b/libgnucash/engine/ScrubBusiness.c
@@ -695,7 +695,7 @@ gncScrubBusinessAccountSplits (Account *acc, QofPercentageFunc percentagefunc)
 restart:
     curr_split_no = 0;
     splits = xaccAccountGetSplitList(acc);
-    split_count = g_list_length (splits);
+    split_count = xaccAccountGetSplitsSize (acc);
     for (node = splits; node; node = node->next)
     {
         Split *split = node->data;
diff --git a/libgnucash/engine/test/utest-Invoice.c b/libgnucash/engine/test/utest-Invoice.c
index a069c56f89..fbb4b7db78 100644
--- a/libgnucash/engine/test/utest-Invoice.c
+++ b/libgnucash/engine/test/utest-Invoice.c
@@ -329,8 +329,7 @@ test_invoice_doclink ( Fixture *fixture, gconstpointer pData )
 
 static gboolean account_has_one_split (const Account *acc)
 {
-    GList *splits = xaccAccountGetSplitList (acc);
-    return (splits && !(splits->next));
+    return (xaccAccountGetSplitsSize (acc) == 1);
 }
 
 static void

commit f70ee754fc635753ac9769dbf6c4fff6974118ca
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Apr 14 10:35:04 2024 +0800

    use xaccAccountGetSplits and gnc_account_find_split

diff --git a/gnucash/gnome-utils/dialog-transfer.cpp b/gnucash/gnome-utils/dialog-transfer.cpp
index f1d3acf048..8ed87051bf 100644
--- a/gnucash/gnome-utils/dialog-transfer.cpp
+++ b/gnucash/gnome-utils/dialog-transfer.cpp
@@ -44,6 +44,7 @@
 #include "gnc-ui.h"
 #include "Transaction.h"
 #include "Account.h"
+#include "Account.hpp"
 #include "engine-helpers.h"
 #include "QuickFill.h"
 #include <gnc-commodity.h>
@@ -472,10 +473,8 @@ gnc_xfer_dialog_reload_quickfill( XferDialog *xferData )
     gnc_quickfill_destroy( xferData->qf );
     xferData->qf = gnc_quickfill_new();
 
-    auto splitlist = xaccAccountGetSplitList( account );
-    for ( GList *node = splitlist; node; node = node->next )
+    for (auto split : xaccAccountGetSplits (account))
     {
-        auto split = static_cast<Split *> (node->data);
         auto trans = xaccSplitGetParent (split);
         gnc_quickfill_insert( xferData->qf,
                               xaccTransGetDescription (trans), QUICKFILL_LIFO);
diff --git a/gnucash/gnome/assistant-stock-transaction.cpp b/gnucash/gnome/assistant-stock-transaction.cpp
index 1b26ac18ab..5f7de1d349 100644
--- a/gnucash/gnome/assistant-stock-transaction.cpp
+++ b/gnucash/gnome/assistant-stock-transaction.cpp
@@ -37,6 +37,7 @@
 #include <sstream>
 
 #include "Account.h"
+#include "Account.hpp"
 #include "Transaction.h"
 #include "engine-helpers.h"
 #include "dialog-utils.h"
@@ -1262,9 +1263,8 @@ StockAssistantModel::set_txn_type (guint type_idx)
 };
 
 static void
-check_txn_date(GList* last_split_node, time64 txn_date, Logger& logger)
+check_txn_date(Split* last_split, time64 txn_date, Logger& logger)
 {
-    auto last_split = static_cast<const Split *>(last_split_node->data);
     auto last_split_date = xaccTransGetDate(xaccSplitGetParent(last_split));
     if (txn_date <= last_split_date) {
         auto last_split_date_str = qof_print_date(last_split_date);
@@ -1301,9 +1301,9 @@ StockAssistantModel::generate_list_of_splits() {
     // transactions dated after the date specified, it is very likely
     // the later stock transactions will be invalidated. warn the user
     // to review them.
-    auto last_split_node = g_list_last (xaccAccountGetSplitList (m_acct));
-    if (last_split_node)
-        check_txn_date(last_split_node, m_transaction_date, m_logger);
+    auto splits{xaccAccountGetSplits (m_acct)};
+    if (!splits.empty())
+        check_txn_date(splits.back(), m_transaction_date, m_logger);
 
     if (m_stock_entry->enabled()  || m_stock_entry->has_amount())
     {
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.cpp b/gnucash/gnome/gnc-plugin-page-account-tree.cpp
index ffb3bc2f15..15f764104b 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.cpp
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.cpp
@@ -39,6 +39,7 @@
 #include "gnc-plugin-page-account-tree.h"
 #include "gnc-plugin-page-register.h"
 
+#include "Account.hpp"
 #include "Scrub.h"
 #include "Scrub3.h"
 #include "ScrubBusiness.h"
@@ -1145,22 +1146,19 @@ static gpointer
 delete_account_helper (Account * account, gpointer data)
 {
     auto helper_res = static_cast<delete_helper_t*>(data);
-    GList *splits;
+    auto splits{xaccAccountGetSplits (account)};
 
-    splits = xaccAccountGetSplitList (account);
-    if (splits)
+    if (!splits.empty())
     {
         helper_res->has_splits = TRUE;
-        while (splits)
+        for (auto s : splits)
         {
-            auto s = GNC_SPLIT(splits->data);
             Transaction *txn = xaccSplitGetParent (s);
             if (xaccTransGetReadOnly (txn))
             {
                 helper_res->has_ro_splits = TRUE;
                 break;
             }
-            splits = splits->next;
         }
     }
 
@@ -1383,7 +1381,6 @@ account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt)
     gchar *title = NULL;
     GtkBuilder *builder = gtk_builder_new();
     gchar *acct_name = gnc_account_get_full_name(account);
-    GList* splits = xaccAccountGetSplitList(account);
     GList* filter = g_list_prepend(NULL, (gpointer)xaccAccountGetType(account));
 
     if (!acct_name)
@@ -1416,7 +1413,7 @@ account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt)
                   account, FALSE);
 
     // Does the selected account have splits
-    if (splits)
+    if (!xaccAccountGetSplits(account).empty())
     {
         delete_helper_t delete_res2 = { FALSE, FALSE };
 
@@ -1537,8 +1534,7 @@ gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple,
     }
 
     // If no transaction or children just delete it.
-    if (!(xaccAccountGetSplitList (account) != NULL ||
-          gnc_account_n_children (account)))
+    if (xaccAccountGetSplits (account).empty() && gnc_account_n_children (account) == 0)
     {
         do_delete_account (account, NULL, NULL, NULL);
         return;
@@ -1581,7 +1577,6 @@ confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page,
                         delete_helper_t delete_res)
 {
     Account *account = gnc_plugin_page_account_tree_get_current_account (page);
-    GList* splits = xaccAccountGetSplitList(account);
     GtkWidget* window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
     gint response;
 
@@ -1595,7 +1590,7 @@ confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page,
                                 acct_name);
     g_free(acct_name);
 
-    if (splits)
+    if (!xaccAccountGetSplits (account).empty())
     {
         if (ta)
         {
diff --git a/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp b/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp
index f328a324d1..3758ccba32 100644
--- a/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp
+++ b/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp
@@ -231,9 +231,8 @@ static void dump_acct (Account *acct)
     std::cout << '\n' << std::setw(20) << std::right << xaccAccountGetName (acct)
               << " Bal=" << std::setw(10) << std::right << GncNumeric (xaccAccountGetBalance (acct))
               << std::endl;
-    for (auto n = xaccAccountGetSplitList (acct); n; n = n->next)
+    for (auto s : xaccAccountGetSplits (acct))
     {
-        auto s = static_cast<Split*>(n->data);
         bal += xaccSplitGetAmount (s);
         std::cout << std::setw(20) << std::right << GncDateTime (xaccTransGetDate (xaccSplitGetParent (s))).format_iso8601()
                   << " amt=" << std::setw(10) << std::right << GncNumeric (xaccSplitGetAmount (s))
diff --git a/gnucash/gnome/window-reconcile.cpp b/gnucash/gnome/window-reconcile.cpp
index 1d352f05d8..8b4a96b0d7 100644
--- a/gnucash/gnome/window-reconcile.cpp
+++ b/gnucash/gnome/window-reconcile.cpp
@@ -37,6 +37,7 @@
 #endif
 #include <gdk/gdkkeysyms.h>
 
+#include "Account.hpp"
 #include "Scrub.h"
 #include "Scrub3.h"
 #include "dialog-account.h"
@@ -1621,10 +1622,8 @@ recn_set_watches_one_account (gpointer data, gpointer user_data)
                                     QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
 
     /* add a watch on each unreconciled or cleared split for the account */
-    GList *splits = xaccAccountGetSplitList (account);
-    for (GList *node = splits; node; node = node->next)
+    for (auto split : xaccAccountGetSplits (account))
     {
-        auto split = GNC_SPLIT(node->data);
         Transaction *trans;
         char recn;
 
@@ -1942,22 +1941,16 @@ recnWindowWithBalance (GtkWidget *parent, Account *account, gnc_numeric new_endi
         GtkWidget *box = gtk_statusbar_get_message_area (bar);
         GtkWidget *image = gtk_image_new_from_icon_name
             ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
-        GList *splits = xaccAccountGetSplitList (account);
 
-        for (GList *n = splits; n; n = n->next)
-        {
-            auto split = GNC_SPLIT(n->data);
-            time64 recn_date = xaccSplitGetDateReconciled (split);
-            gchar *datestr, *recnstr;
-            if ((xaccSplitGetReconcile (split) != YREC) ||
-                (recn_date <= statement_date))
-                continue;
+        auto find_split = [statement_date](const Split *split)
+        { return (xaccSplitGetReconcile (split) == YREC &&
+                  xaccSplitGetDateReconciled (split) > statement_date); };
 
-            datestr = qof_print_date (xaccTransGetDate (xaccSplitGetParent (split)));
-            recnstr = qof_print_date (recn_date);
+        if (auto split = gnc_account_find_split (account, find_split, true))
+        {
+            auto datestr = qof_print_date (xaccTransGetDate (xaccSplitGetParent (split)));
+            auto recnstr = qof_print_date (xaccSplitGetDateReconciled (split));
             PWARN ("split posting_date=%s, recn_date=%s", datestr, recnstr);
-            g_free (datestr);
-            g_free (recnstr);
 
             gtk_statusbar_push (bar, context, _("WARNING! Account contains \
 splits whose reconcile date is after statement date. Reconciliation may be \
@@ -1970,7 +1963,9 @@ use Find Transactions to find them, unreconcile, and re-reconcile."));
 
             gtk_box_pack_start (GTK_BOX(box), image, FALSE, FALSE, 0);
             gtk_box_reorder_child (GTK_BOX(box), image, 0);
-            break;
+
+            g_free (datestr);
+            g_free (recnstr);
         }
     }
 
@@ -2305,6 +2300,7 @@ find_payment_account(Account *account)
         }
     }
 
+    g_list_free (list);
     return rv;
 }
 
diff --git a/gnucash/import-export/import-backend.cpp b/gnucash/import-export/import-backend.cpp
index 5fbf9a082a..d333d7bfbb 100644
--- a/gnucash/import-export/import-backend.cpp
+++ b/gnucash/import-export/import-backend.cpp
@@ -38,6 +38,7 @@
 #include "import-backend.h"
 #include "import-utilities.h"
 #include "Account.h"
+#include "Account.hpp"
 #include "Query.h"
 #include "gnc-engine.h"
 #include "engine-helpers.h"
@@ -996,9 +997,9 @@ hash_account_online_ids (Account *account)
 {
      auto acct_hash = g_hash_table_new_full
           (g_str_hash, g_str_equal, g_free, nullptr);
-     for (GList *n = xaccAccountGetSplitList (account) ; n; n = n->next)
+     for (auto split : xaccAccountGetSplits (account))
      {
-        auto id = gnc_import_get_split_online_id (static_cast<Split *>(n->data));
+        auto id = gnc_import_get_split_online_id (split);
         if (id && *id)
             g_hash_table_insert (acct_hash, (void*) id, GINT_TO_POINTER (1));
      }
diff --git a/gnucash/import-export/import-main-matcher.cpp b/gnucash/import-export/import-main-matcher.cpp
index 8f7c18b751..5acc3ba474 100644
--- a/gnucash/import-export/import-main-matcher.cpp
+++ b/gnucash/import-export/import-main-matcher.cpp
@@ -44,6 +44,7 @@
 
 #include "import-main-matcher.h"
 
+#include "Account.hpp"
 #include "dialog-transfer.h"
 #include "dialog-utils.h"
 #include "gnc-glib-utils.h"
@@ -468,9 +469,8 @@ load_hash_tables (GNCImportMainMatcher *info)
     }
     for (GList *m = accounts_list; m; m = m->next)
     {
-        for (GList *n = xaccAccountGetSplitList (static_cast<Account*>(m->data)); n; n = n->next)
+        for (auto s : xaccAccountGetSplits (static_cast<Account*>(m->data)))
         {
-            auto s = static_cast<const Split*>(n->data);
             const Transaction *t = xaccSplitGetParent (s);
 
             const gchar *key = xaccTransGetDescription (t);
diff --git a/libgnucash/engine/Scrub.cpp b/libgnucash/engine/Scrub.cpp
index 0530325dfc..f468f13f76 100644
--- a/libgnucash/engine/Scrub.cpp
+++ b/libgnucash/engine/Scrub.cpp
@@ -47,6 +47,7 @@
 
 #include "Account.h"
 #include "AccountP.hpp"
+#include "Account.hpp"
 #include "Scrub.h"
 #include "Transaction.h"
 #include "TransactionP.h"
@@ -91,8 +92,8 @@ gnc_get_ongoing_scrub (void)
 
 static void add_transactions (const Account *account, GHashTable **ht)
 {
-    for (GList *m = xaccAccountGetSplitList (account); m; m = g_list_next (m))
-        g_hash_table_add (*ht, xaccSplitGetParent (GNC_SPLIT(m->data)));
+    for (auto s : xaccAccountGetSplits (account))
+        g_hash_table_add (*ht, xaccSplitGetParent (s));
 }
 
 static GList*
@@ -226,12 +227,11 @@ xaccAccountTreeScrubSplits (Account *account)
 void
 xaccAccountScrubSplits (Account *account)
 {
-    GList *node;
     scrub_depth++;
-    for (node = xaccAccountGetSplitList (account); node; node = node->next)
+    for (auto s : xaccAccountGetSplits (account))
     {
         if (abort_now) break;
-        xaccSplitScrub (GNC_SPLIT(node->data));
+        xaccSplitScrub (s);
     }
     scrub_depth--;
 }
diff --git a/libgnucash/engine/Scrub2.cpp b/libgnucash/engine/Scrub2.cpp
index 6743895177..265492f738 100644
--- a/libgnucash/engine/Scrub2.cpp
+++ b/libgnucash/engine/Scrub2.cpp
@@ -37,6 +37,7 @@
 #include "qof.h"
 #include "Account.h"
 #include "AccountP.hpp"
+#include "Account.hpp"
 #include "Transaction.h"
 #include "TransactionP.h"
 #include "Scrub2.h"
@@ -57,19 +58,14 @@ static QofLogModule log_module = GNC_MOD_LOT;
 void
 xaccAccountAssignLots (Account *acc)
 {
-    SplitList *splits, *node;
-
     if (!acc) return;
 
     ENTER ("acc=%s", xaccAccountGetName(acc));
     xaccAccountBeginEdit (acc);
 
 restart_loop:
-    splits = xaccAccountGetSplitList(acc);
-    for (node = splits; node; node = node->next)
+    for (auto split : xaccAccountGetSplits (acc))
     {
-        Split * split = GNC_SPLIT(node->data);
-
         /* If already in lot, then no-op */
         if (split->lot) continue;
 
diff --git a/libgnucash/engine/cap-gains.cpp b/libgnucash/engine/cap-gains.cpp
index 0754005fc7..3c004c552b 100644
--- a/libgnucash/engine/cap-gains.cpp
+++ b/libgnucash/engine/cap-gains.cpp
@@ -58,6 +58,7 @@ ToDo:
 #include <glib.h>
 #include <glib/gi18n.h>
 
+#include "Account.hpp"
 #include "AccountP.hpp"
 #include "Scrub2.h"
 #include "Scrub3.h"
@@ -79,7 +80,6 @@ gboolean
 xaccAccountHasTrades (const Account *acc)
 {
     gnc_commodity *acc_comm;
-    SplitList *splits, *node;
 
     if (!acc) return FALSE;
 
@@ -88,10 +88,8 @@ xaccAccountHasTrades (const Account *acc)
 
     acc_comm = xaccAccountGetCommodity(acc);
 
-    splits = xaccAccountGetSplitList(acc);
-    for (node = splits; node; node = node->next)
+    for (auto s : xaccAccountGetSplits (acc))
     {
-        Split *s = GNC_SPLIT(node->data);
         Transaction *t = s->parent;
 	if (s->gains == GAINS_STATUS_GAINS) continue;
         if (acc_comm != t->common_currency) return TRUE;
diff --git a/libgnucash/engine/policy.cpp b/libgnucash/engine/policy.cpp
index ce89264219..35febca6f4 100644
--- a/libgnucash/engine/policy.cpp
+++ b/libgnucash/engine/policy.cpp
@@ -34,6 +34,7 @@
 #include <glib.h>
 
 #include "Account.h"
+#include "Account.hpp"
 #include "Transaction.h"
 #include "TransactionP.h"
 #include "cap-gains.h"
@@ -100,38 +101,16 @@ DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse)
      * hasn't been assigned to a lot.  Return that split.
      * Make use of the fact that the splits in an account are
      * already in date order; so we don't have to sort. */
-    auto splits = xaccAccountGetSplitList (lot_account);
-
-    Split *rv = nullptr;
-
-    for (auto node = reverse ? g_list_last (splits) : splits; !rv && node;
-         node = reverse ? node->prev : node->next)
+    auto find_split = [open_time, common_currency, want_positive](const Split* split)
     {
-        split = GNC_SPLIT(node->data);
-        if (split->lot)
-            continue;
-
-        /* Skip it if it's too early */
-        if (xaccTransRetDatePosted (xaccSplitGetParent (split)) < open_time)
-        {
-            if (reverse)
-                /* Going backwards, no point in looking further */
-                break;
-            continue;
-        }
-
-        /* Allow equiv currencies */
-        if (!gnc_commodity_equiv (common_currency, split->parent->common_currency))
-            continue;
-
-        /* Disallow zero-amount splits in general. */
-        if (gnc_numeric_zero_p(split->amount))
-            continue;
-
-        if (want_positive == gnc_numeric_positive_p (split->amount))
-            rv = split;
-    }
-    return rv;
+        return (!split->lot &&
+                xaccTransRetDatePosted (xaccSplitGetParent (split)) >= open_time &&
+                gnc_commodity_equiv (common_currency, split->parent->common_currency) &&
+                !gnc_numeric_zero_p (split->amount) &&
+                want_positive == gnc_numeric_positive_p (split->amount));
+    };
+
+    return gnc_account_find_split (lot_account, find_split, reverse);
 }
 
 /* ============================================================== */
diff --git a/libgnucash/engine/test-core/test-engine-stuff.cpp b/libgnucash/engine/test-core/test-engine-stuff.cpp
index 3eb0479e36..2024f8fb9a 100644
--- a/libgnucash/engine/test-core/test-engine-stuff.cpp
+++ b/libgnucash/engine/test-core/test-engine-stuff.cpp
@@ -1074,7 +1074,6 @@ make_random_changes_to_level (QofBook *book, Account *parent)
     account = static_cast<Account*>(get_random_list_element (accounts));
 
     splits = xaccAccountGetSplitList (account);
-    splits = g_list_copy (splits);
 
     for (node = splits; node; node = node->next)
     {

commit 3f7a5a8267d19538869b474ef92d4550cc0e37ee
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Apr 13 21:35:35 2024 +0800

    [Account.hpp][API] xaccAccountGetSplits and gnc_account_find_split

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index b4dbcb4b34..d25d01aeca 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -34,6 +34,7 @@
 #include <string.h>
 
 #include "AccountP.hpp"
+#include "Account.hpp"
 #include "Split.h"
 #include "Transaction.h"
 #include "TransactionP.h"
@@ -331,7 +332,7 @@ gnc_account_init(Account* acc)
     priv->lower_balance_limit = {};
     priv->include_sub_account_balances = {};
 
-    priv->splits = nullptr;
+    priv->splits.clear();
     priv->sort_dirty = FALSE;
 }
 
@@ -1136,6 +1137,29 @@ xaccInitAccount (Account * acc, QofBook *book)
 /********************************************************************\
 \********************************************************************/
 
+Split*
+gnc_account_find_split (const Account *acc, std::function<bool(const Split*)> predicate,
+                        bool reverse)
+{
+    if (!GNC_IS_ACCOUNT (acc))
+        return nullptr;
+
+    auto splits{GET_PRIVATE(acc)->splits};
+    if (reverse)
+    {
+        auto latest = std::find_if(splits.rbegin(), splits.rend(), predicate);
+        return (latest == splits.rend()) ? nullptr : *latest;
+    }
+    else
+    {
+        auto earliest = std::find_if(splits.begin(), splits.end(), predicate);
+        return (earliest == splits.end()) ? nullptr : *earliest;
+    }
+}
+
+/********************************************************************\
+\********************************************************************/
+
 QofBook *
 gnc_account_get_book(const Account *account)
 {
@@ -1368,22 +1392,18 @@ xaccFreeAccount (Account *acc)
     /* NB there shouldn't be any splits by now ... they should
      * have been all been freed by CommitEdit().  We can remove this
      * check once we know the warning isn't occurring any more. */
-    if (priv->splits)
+    if (!priv->splits.empty())
     {
-        GList *slist;
         PERR (" instead of calling xaccFreeAccount(), please call\n"
               " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
 
         qof_instance_reset_editlevel(acc);
 
-        slist = g_list_copy(priv->splits);
-        for (lp = slist; lp; lp = lp->next)
+        for (auto s : priv->splits)
         {
-            Split *s = (Split *) lp->data;
             g_assert(xaccSplitGetAccount(s) == acc);
             xaccSplitDestroy (s);
         }
-        g_list_free(slist);
 /* Nothing here (or in xaccAccountCommitEdit) nullptrs priv->splits, so this asserts every time.
         g_assert(priv->splits == nullptr);
 */
@@ -1416,6 +1436,7 @@ xaccFreeAccount (Account *acc)
     priv->type = ACCT_TYPE_NONE;
     gnc_commodity_decrement_usage_count(priv->commodity);
     priv->commodity = nullptr;
+    priv->splits.clear();
 
     priv->balance_dirty = FALSE;
     priv->sort_dirty = FALSE;
@@ -1483,7 +1504,6 @@ xaccAccountCommitEdit (Account *acc)
     priv = GET_PRIVATE(acc);
     if (qof_instance_get_destroying(acc))
     {
-        GList *lp, *slist;
         QofCollection *col;
 
         qof_instance_increase_editlevel(acc);
@@ -1500,18 +1520,12 @@ xaccAccountCommitEdit (Account *acc)
            themselves will be destroyed by the transaction code */
         if (!qof_book_shutting_down(book))
         {
-            slist = g_list_copy(priv->splits);
-            for (lp = slist; lp; lp = lp->next)
-            {
-                Split *s = static_cast<Split *>(lp->data);
+            for (auto s : priv->splits)
                 xaccSplitDestroy (s);
-            }
-            g_list_free(slist);
         }
         else
         {
-            g_list_free(priv->splits);
-            priv->splits = nullptr;
+            priv->splits.clear();
         }
 
         /* It turns out there's a case where this assertion does not hold:
@@ -1528,7 +1542,7 @@ xaccAccountCommitEdit (Account *acc)
             qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
 
             /* the lots should be empty by now */
-            for (lp = priv->lots; lp; lp = lp->next)
+            for (auto lp = priv->lots; lp; lp = lp->next)
             {
                 GNCLot *lot = static_cast<GNCLot*>(lp->data);
                 gnc_lot_destroy (lot);
@@ -1822,37 +1836,23 @@ xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
     /* no parent; always compare downwards. */
 
     {
-        GList *la = priv_aa->splits;
-        GList *lb = priv_ab->splits;
-
-        if ((la && !lb) || (!la && lb))
+        if (priv_aa->splits.size() != priv_ab->splits.size())
         {
-            PWARN ("only one has splits");
-            return FALSE;
+            PWARN ("number of splits differs");
+            return false;
         }
 
-        if (la && lb)
+        for (auto it_a = priv_aa->splits.begin(), it_b = priv_ab->splits.begin();
+             it_a != priv_aa->splits.end() && it_b != priv_ab->splits.end();
+             ++it_a, ++it_b)
         {
-            /* presume that the splits are in the same order */
-            while (la && lb)
-            {
-                Split *sa = (Split *) la->data;
-                Split *sb = (Split *) lb->data;
+            Split *sa = *it_a;
+            Split *sb = *it_b;
 
-                if (!xaccSplitEqual(sa, sb, check_guids, TRUE, FALSE))
-                {
-                    PWARN ("splits differ");
-                    return(FALSE);
-                }
-
-                la = la->next;
-                lb = lb->next;
-            }
-
-            if ((la != nullptr) || (lb != nullptr))
+            if (!xaccSplitEqual(sa, sb, check_guids, TRUE, FALSE))
             {
-                PWARN ("number of splits differs");
-                return(FALSE);
+                PWARN ("splits differ");
+                return false;
             }
         }
     }
@@ -1922,30 +1922,29 @@ gboolean gnc_account_get_defer_bal_computation (Account *acc)
 /********************************************************************\
 \********************************************************************/
 
+static bool split_cmp_less (const Split* a, const Split* b)
+{
+    return xaccSplitOrder (a, b) < 0;
+}
+
 gboolean
 gnc_account_insert_split (Account *acc, Split *s)
 {
     AccountPrivate *priv;
-    GList *node;
 
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
     g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
 
     priv = GET_PRIVATE(acc);
-    node = g_list_find(priv->splits, s);
-    if (node)
+    if (std::find (priv->splits.begin(), priv->splits.end(), s) != priv->splits.end())
         return FALSE;
 
+    priv->splits.push_back (s);
+
     if (qof_instance_get_editlevel(acc) == 0)
-    {
-        priv->splits = g_list_insert_sorted(priv->splits, s,
-                                            (GCompareFunc)xaccSplitOrder);
-    }
+        std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
     else
-    {
-        priv->splits = g_list_prepend(priv->splits, s);
-        priv->sort_dirty = TRUE;
-    }
+        priv->sort_dirty = true;
 
     //FIXME: find better event
     qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
@@ -1962,17 +1961,17 @@ gboolean
 gnc_account_remove_split (Account *acc, Split *s)
 {
     AccountPrivate *priv;
-    GList *node;
 
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
     g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
 
     priv = GET_PRIVATE(acc);
-    node = g_list_find(priv->splits, s);
-    if (nullptr == node)
+
+    auto it = std::remove (priv->splits.begin(), priv->splits.end(), s);
+    if (it == priv->splits.end())
         return FALSE;
 
-    priv->splits = g_list_delete_link(priv->splits, node);
+    priv->splits.erase (it, priv->splits.end());
     //FIXME: find better event type
     qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, nullptr);
     // And send the account-based event, too
@@ -1993,7 +1992,7 @@ xaccAccountSortSplits (Account *acc, gboolean force)
     priv = GET_PRIVATE(acc);
     if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0))
         return;
-    priv->splits = g_list_sort(priv->splits, (GCompareFunc)xaccSplitOrder);
+    std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
     priv->sort_dirty = FALSE;
     priv->balance_dirty = TRUE;
 }
@@ -2166,7 +2165,7 @@ xaccAccountInsertLot (Account *acc, GNCLot *lot)
 /********************************************************************\
 \********************************************************************/
 static void
-xaccPreSplitMove (Split *split, gpointer dummy)
+xaccPreSplitMove (Split *split)
 {
     xaccTransBeginEdit (xaccSplitGetParent (split));
 }
@@ -2193,7 +2192,7 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto)
 
     /* optimizations */
     from_priv = GET_PRIVATE(accfrom);
-    if (!from_priv->splits || accfrom == accto)
+    if (from_priv->splits.empty() || accfrom == accto)
         return;
 
     /* check for book mix-up */
@@ -2203,7 +2202,7 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto)
     xaccAccountBeginEdit(accfrom);
     xaccAccountBeginEdit(accto);
     /* Begin editing both accounts and all transactions in accfrom. */
-    g_list_foreach(from_priv->splits, (GFunc)xaccPreSplitMove, nullptr);
+    std::for_each (from_priv->splits.begin(), from_priv->splits.end(), xaccPreSplitMove);
 
     /* Concatenate accfrom's lists of splits and lots to accto's lists. */
     //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits);
@@ -2220,10 +2219,11 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto)
      * Convert each split's amount to accto's commodity.
      * Commit to editing each transaction.
      */
-    g_list_foreach(from_priv->splits, (GFunc)xaccPostSplitMove, (gpointer)accto);
+    std::for_each (from_priv->splits.begin(), from_priv->splits.end(),
+                   [accto](Split *s){ xaccPostSplitMove (s, accto); });
 
     /* Finally empty accfrom. */
-    g_assert(from_priv->splits == nullptr);
+    g_assert(from_priv->splits.empty());
     g_assert(from_priv->lots == nullptr);
     xaccAccountCommitEdit(accfrom);
     xaccAccountCommitEdit(accto);
@@ -2268,7 +2268,6 @@ xaccAccountRecomputeBalance (Account * acc)
     gnc_numeric  noclosing_balance;
     gnc_numeric  cleared_balance;
     gnc_numeric  reconciled_balance;
-    GList *lp;
 
     if (nullptr == acc) return;
 
@@ -2285,9 +2284,8 @@ xaccAccountRecomputeBalance (Account * acc)
 
     PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
            priv->accountName, balance.num, balance.denom);
-    for (lp = priv->splits; lp; lp = lp->next)
+    for (auto split : priv->splits)
     {
-        Split *split = (Split *) lp->data;
         gnc_numeric amt = xaccSplitGetAmount (split);
 
         balance = gnc_numeric_add_fixed(balance, amt);
@@ -2607,7 +2605,6 @@ void
 xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
 {
     AccountPrivate *priv;
-    GList *lp;
 
     /* errors */
     g_return_if_fail(GNC_IS_ACCOUNT(acc));
@@ -2626,9 +2623,8 @@ xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
     priv->non_standard_scu = FALSE;
 
     /* iterate over splits */
-    for (lp = priv->splits; lp; lp = lp->next)
+    for (auto s : priv->splits)
     {
-        Split *s = (Split *) lp->data;
         Transaction *trans = xaccSplitGetParent (s);
 
         xaccTransBeginEdit (trans);
@@ -3540,35 +3536,20 @@ xaccAccountGetReconciledBalance (const Account *acc)
 gnc_numeric
 xaccAccountGetProjectedMinimumBalance (const Account *acc)
 {
-    AccountPrivate *priv;
-    GList *node;
-    time64 today;
-    gnc_numeric lowest = gnc_numeric_zero ();
-    int seen_a_transaction = 0;
+    auto today{gnc_time64_get_today_end()};
+    std::optional<gnc_numeric> minimum;
 
-    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
-
-    priv = GET_PRIVATE(acc);
-    today = gnc_time64_get_today_end();
-    for (node = g_list_last(priv->splits); node; node = node->prev)
+    auto before_today_end = [&minimum, today](const Split *s) -> bool
     {
-        Split *split = static_cast<Split*>(node->data);
-
-        if (!seen_a_transaction)
-        {
-            lowest = xaccSplitGetBalance (split);
-            seen_a_transaction = 1;
-        }
-        else if (gnc_numeric_compare(xaccSplitGetBalance (split), lowest) < 0)
-        {
-            lowest = xaccSplitGetBalance (split);
-        }
-
-        if (xaccTransGetDate (xaccSplitGetParent (split)) <= today)
-            return lowest;
-    }
-
-    return lowest;
+        auto bal{xaccSplitGetBalance(s)};
+        if (!minimum || gnc_numeric_compare (bal, *minimum) < 0)
+            minimum = bal;
+        return (xaccTransGetDate(xaccSplitGetParent(s)) < today);
+    };
+    // scan to find today's split, but we're really interested in the
+    // minimum balance
+    [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true);
+    return minimum ? *minimum : gnc_numeric_zero();
 }
 
 
@@ -3578,27 +3559,16 @@ xaccAccountGetProjectedMinimumBalance (const Account *acc)
 static gnc_numeric
 GetBalanceAsOfDate (Account *acc, time64 date, std::function<gnc_numeric(Split*)> split_to_numeric)
 {
-    /* Ideally this could use xaccAccountForEachSplit, but
-     * it doesn't exist yet and I'm uncertain of exactly how
-     * it would work at this time, since it differs from
-     * xaccAccountForEachTransaction by using gpointer return
-     * values rather than gints.
-     */
-    Split *latest = nullptr;
-
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
     xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
     xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
 
-    for (GList *lp = GET_PRIVATE(acc)->splits; lp; lp = lp->next)
-    {
-        if (xaccTransGetDate (xaccSplitGetParent ((Split *)lp->data)) >= date)
-            break;
-        latest = (Split *)lp->data;
-    }
+    auto is_before_date = [date](auto s) -> bool
+    { return xaccTransGetDate(xaccSplitGetParent(s)) < date; };
 
-    return latest ? split_to_numeric (latest) : gnc_numeric_zero();
+    auto latest_split{gnc_account_find_split (acc, is_before_date, true)};
+    return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero();
 }
 
 gnc_numeric
@@ -3616,19 +3586,7 @@ xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date)
 gnc_numeric
 xaccAccountGetReconciledBalanceAsOfDate (Account *acc, time64 date)
 {
-    gnc_numeric balance = gnc_numeric_zero();
-
-    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
-
-    for (GList *node = GET_PRIVATE(acc)->splits; node; node = node->next)
-    {
-        Split *split = (Split*) node->data;
-        if ((xaccSplitGetReconcile (split) == YREC) &&
-            (xaccSplitGetDateReconciled (split) <= date))
-            balance = gnc_numeric_add_fixed (balance, xaccSplitGetAmount (split));
-    };
-
-    return balance;
+    return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance);
 }
 
 /*
@@ -4021,6 +3979,12 @@ xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1
 /********************************************************************\
 \********************************************************************/
 
+const SplitsVec
+xaccAccountGetSplits (const Account *account)
+{
+    return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits : SplitsVec{};
+}
+
 /* THIS API NEEDS TO CHANGE.
  *
  * This code exposes the internal structure of the account object to
@@ -4036,21 +4000,22 @@ SplitList *
 xaccAccountGetSplitList (const Account *acc)
 {
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
-    xaccAccountSortSplits((Account*)acc, FALSE);  // normally a noop
-    return GET_PRIVATE(acc)->splits;
+    auto priv{GET_PRIVATE(acc)};
+    return std::accumulate (priv->splits.rbegin(), priv->splits.rend(),
+                            static_cast<GList*>(nullptr), g_list_prepend);
 }
 
 size_t
 xaccAccountGetSplitsSize (const Account *account)
 {
-    return GNC_IS_ACCOUNT(account) ? g_list_length (GET_PRIVATE(account)->splits) : 0;
+    return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0;
 }
 
 gboolean gnc_account_and_descendants_empty (Account *acc)
 {
     g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
     auto priv = GET_PRIVATE (acc);
-    if (priv->splits != nullptr) return FALSE;
+    if (!priv->splits.empty()) return FALSE;
     for (auto *n = priv->children; n; n = n->next)
     {
 	if (!gnc_account_and_descendants_empty (static_cast<Account*>(n->data)))
@@ -5377,51 +5342,12 @@ xaccAccountGetReconcileChildrenStatus(const Account *acc)
 /********************************************************************\
 \********************************************************************/
 
-/* The caller of this function can get back one or both of the
- * matching split and transaction pointers, depending on whether
- * a valid pointer to the location to store those pointers is
- * passed.
- */
-static void
-finder_help_function(const Account *acc, const char *description,
-                     Split **split, Transaction **trans )
-{
-    AccountPrivate *priv;
-    GList *slp;
-
-    /* First, make sure we set the data to nullptr BEFORE we start */
-    if (split) *split = nullptr;
-    if (trans) *trans = nullptr;
-
-    /* Then see if we have any work to do */
-    if (acc == nullptr) return;
-
-    /* Why is this loop iterated backwards ?? Presumably because the split
-     * list is in date order, and the most recent matches should be
-     * returned!?  */
-    priv = GET_PRIVATE(acc);
-    for (slp = g_list_last(priv->splits); slp; slp = slp->prev)
-    {
-        Split *lsplit = static_cast<Split*>(slp->data);
-        Transaction *ltrans = xaccSplitGetParent(lsplit);
-
-        if (g_strcmp0 (description, xaccTransGetDescription (ltrans)) == 0)
-        {
-            if (split) *split = lsplit;
-            if (trans) *trans = ltrans;
-            return;
-        }
-    }
-}
-
 Split *
 xaccAccountFindSplitByDesc(const Account *acc, const char *description)
 {
-    Split *split;
-
-    /* Get the split which has a transaction matching the description. */
-    finder_help_function(acc, description, &split, nullptr);
-    return split;
+    auto has_description = [description](const Split* s) -> bool
+    { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); };
+    return gnc_account_find_split (acc, has_description, true);
 }
 
 /* This routine is for finding a matching transaction in an account by
@@ -5432,11 +5358,8 @@ xaccAccountFindSplitByDesc(const Account *acc, const char *description)
 Transaction *
 xaccAccountFindTransByDesc(const Account *acc, const char *description)
 {
-    Transaction *trans;
-
-    /* Get the translation matching the description. */
-    finder_help_function(acc, description, nullptr, &trans);
-    return trans;
+    auto split = xaccAccountFindSplitByDesc (acc, description);
+    return split ? xaccSplitGetParent (split) : nullptr;
 }
 
 /* ================================================================ */
@@ -5519,8 +5442,8 @@ gnc_account_merge_children (Account *parent)
             gnc_account_merge_children (acc_a);
 
             /* consolidate transactions */
-            while (priv_b->splits)
-                xaccSplitSetAccount (static_cast <Split*> (priv_b->splits->data), acc_a);
+            while (!priv_b->splits.empty())
+                xaccSplitSetAccount (priv_b->splits.front(), acc_a);
 
             /* move back one before removal. next iteration around the loop
              * will get the node after node_b */
@@ -5538,14 +5461,11 @@ gnc_account_merge_children (Account *parent)
 /* Transaction Traversal functions                                  */
 
 
-void
-xaccSplitsBeginStagedTransactionTraversals (GList *splits)
+static void
+xaccSplitsBeginStagedTransactionTraversals (SplitsVec splits)
 {
-    GList *lp;
-
-    for (lp = splits; lp; lp = lp->next)
+    for (auto s : splits)
     {
-        Split *s = static_cast <Split*> (lp->data);
         Transaction *trans = s->parent;
 
         if (trans)
@@ -5557,12 +5477,9 @@ xaccSplitsBeginStagedTransactionTraversals (GList *splits)
 void
 xaccAccountBeginStagedTransactionTraversals (const Account *account)
 {
-    AccountPrivate *priv;
-
     if (!account)
         return;
-    priv = GET_PRIVATE(account);
-    xaccSplitsBeginStagedTransactionTraversals(priv->splits);
+    xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits);
 }
 
 gboolean
@@ -5579,16 +5496,11 @@ xaccTransactionTraverse (Transaction *trans, int stage)
     return FALSE;
 }
 
-static void do_one_split (Split *s, gpointer data)
-{
-    Transaction *trans = s->parent;
-    trans->marker = 0;
-}
-
 static void do_one_account (Account *account, gpointer data)
 {
     AccountPrivate *priv = GET_PRIVATE(account);
-    g_list_foreach(priv->splits, (GFunc)do_one_split, nullptr);
+    std::for_each (priv->splits.begin(), priv->splits.end(),
+                   [](auto s){ s->parent->marker = 0; });
 }
 
 /* Replacement for xaccGroupBeginStagedTransactionTraversals */
@@ -5608,32 +5520,18 @@ xaccAccountStagedTransactionTraversal (const Account *acc,
                                        TransactionCallback thunk,
                                        void *cb_data)
 {
-    AccountPrivate *priv;
-    GList *split_p;
-    GList *next;
-    Transaction *trans;
-    Split *s;
-    int retval;
-
     if (!acc) return 0;
 
-    priv = GET_PRIVATE(acc);
-    for (split_p = priv->splits; split_p; split_p = next)
+    auto splits = GET_PRIVATE(acc)->splits;
+    for (auto s : splits)
     {
-        /* Get the next element in the split list now, just in case some
-         * naughty thunk destroys the one we're using. This reduces, but
-         * does not eliminate, the possibility of undefined results if
-         * a thunk removes splits from this account. */
-        next = g_list_next(split_p);
-
-        s = static_cast <Split*> (split_p->data);
-        trans = s->parent;
+        auto trans = s->parent;
         if (trans && (trans->marker < stage))
         {
             trans->marker = stage;
             if (thunk)
             {
-                retval = thunk(trans, cb_data);
+                auto retval = thunk(trans, cb_data);
                 if (retval) return retval;
             }
         }
@@ -5649,16 +5547,14 @@ gnc_account_tree_staged_transaction_traversal (const Account *acc,
         void *cb_data)
 {
     const AccountPrivate *priv;
-    GList *acc_p, *split_p;
     Transaction *trans;
-    Split *s;
     int retval;
 
     if (!acc) return 0;
 
     /* depth first traversal */
     priv = GET_PRIVATE(acc);
-    for (acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p))
+    for (auto acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p))
     {
         retval = gnc_account_tree_staged_transaction_traversal(static_cast <Account*> (acc_p->data),
                 stage, thunk, cb_data);
@@ -5666,9 +5562,8 @@ gnc_account_tree_staged_transaction_traversal (const Account *acc,
     }
 
     /* Now this account */
-    for (split_p = priv->splits; split_p; split_p = g_list_next(split_p))
+    for (auto s : priv->splits)
     {
-        s = static_cast <Split*> (split_p->data);
         trans = s->parent;
         if (trans && (trans->marker < stage))
         {
diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h
index 83fa8d3901..51379dacd6 100644
--- a/libgnucash/engine/Account.h
+++ b/libgnucash/engine/Account.h
@@ -1493,12 +1493,6 @@ typedef enum
      */
     void gnc_account_tree_begin_staged_transaction_traversals(Account *acc);
 
-    /** xaccSplitsBeginStagedTransactionTraversals() resets the traversal
-     *    marker for each transaction which is a parent of one of the
-     *    splits in the list.
-     */
-    void xaccSplitsBeginStagedTransactionTraversals(SplitList *splits);
-
     /** xaccAccountBeginStagedTransactionTraversals() resets the traversal
      *    marker for each transaction which is a parent of one of the
      *    splits in the account.
diff --git a/libgnucash/engine/Account.hpp b/libgnucash/engine/Account.hpp
new file mode 100644
index 0000000000..a947bee71f
--- /dev/null
+++ b/libgnucash/engine/Account.hpp
@@ -0,0 +1,58 @@
+/**********************************************************************
+ * Account.hpp
+ *                                                                    *
+ * 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 at gnu.org                     *
+ *                                                                    *
+ *********************************************************************/
+
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Account
+
+    @{ */
+/** @file Account.hpp
+ *  @brief Account public routines (C++ api)
+ */
+
+#ifndef GNC_ACCOUNT_HPP
+#define GNC_ACCOUNT_HPP
+
+#include <vector>
+#include <functional>
+
+#include <Account.h>
+
+using SplitsVec = std::vector<Split*>;
+
+const SplitsVec xaccAccountGetSplits (const Account*);
+
+/** scans account split list (in forward or reverse order) until
+ *    predicate split->bool returns true. Maybe return the split.
+ *
+ *  @param acc The account to which the split should be added.
+ *
+ *  @param predicate A split->bool predicate.
+ *
+ *  @param reverse To scan in reverse order
+ *
+ *  @result Split* or nullptr if not found */
+Split* gnc_account_find_split (const Account*, std::function<bool(const Split*)>, bool);
+
+#endif /* GNC_COMMODITY_HPP */
+/** @} */
+/** @} */
diff --git a/libgnucash/engine/AccountP.hpp b/libgnucash/engine/AccountP.hpp
index d950569a66..abd43f69c8 100644
--- a/libgnucash/engine/AccountP.hpp
+++ b/libgnucash/engine/AccountP.hpp
@@ -39,6 +39,7 @@
 #ifndef XACC_ACCOUNT_P_H
 #define XACC_ACCOUNT_P_H
 
+#include <vector>
 #include <optional>
 
 #include "Account.h"
@@ -118,7 +119,7 @@ typedef struct AccountPrivate
  
     gboolean balance_dirty;     /* balances in splits incorrect */
 
-    GList *splits;              /* list of split pointers */
+    std::vector<Split*> splits;              /* list of split pointers */
     gboolean sort_dirty;        /* sort order of splits is bad */
 
     LotList   *lots;		/* list of lot pointers */
diff --git a/libgnucash/engine/CMakeLists.txt b/libgnucash/engine/CMakeLists.txt
index 84178c99ca..dbd2516c9e 100644
--- a/libgnucash/engine/CMakeLists.txt
+++ b/libgnucash/engine/CMakeLists.txt
@@ -31,6 +31,7 @@ set(engine_noinst_HEADERS
 )
 
 set (engine_HEADERS
+  Account.hpp
   Account.h
   FreqSpec.h
   Recurrence.h
diff --git a/libgnucash/engine/mocks/gmock-Account.cpp b/libgnucash/engine/mocks/gmock-Account.cpp
index b85ce2527a..3250a2c1b4 100644
--- a/libgnucash/engine/mocks/gmock-Account.cpp
+++ b/libgnucash/engine/mocks/gmock-Account.cpp
@@ -76,6 +76,16 @@ xaccAccountGetSplitList (const Account *account)
     return mockaccount ? mockaccount->xaccAccountGetSplitList() : nullptr;
 }
 
+const std::vector<Split*>
+xaccAccountGetSplits (const Account *account)
+{
+    SCOPED_TRACE("");
+    auto mockaccount = gnc_mockaccount(account);
+    if (!mockaccount)
+        return {};
+    return mockaccount->xaccAccountGetSplits();
+}
+
 
 Account*
 gnc_account_imap_find_account (
diff --git a/libgnucash/engine/mocks/gmock-Account.h b/libgnucash/engine/mocks/gmock-Account.h
index fc71b0f8f7..97860fdc5e 100644
--- a/libgnucash/engine/mocks/gmock-Account.h
+++ b/libgnucash/engine/mocks/gmock-Account.h
@@ -4,6 +4,7 @@
 #include <gmock/gmock.h>
 
 #include <Account.h>
+#include <Account.hpp>
 #include <AccountP.hpp>
 #include <qofbook.h>
 
@@ -45,6 +46,7 @@ public:
     MOCK_CONST_METHOD0(get_commodity, gnc_commodity*());
     MOCK_CONST_METHOD2(for_each_transaction, gint(TransactionCallback, void*));
     MOCK_CONST_METHOD0(xaccAccountGetSplitList, SplitList*());
+    MOCK_CONST_METHOD0(xaccAccountGetSplits, std::vector<Split*>());
     MOCK_METHOD2(find_account, Account *(const char*, const char*));
     MOCK_METHOD3(add_account, void(const char*, const char*, Account*));
     MOCK_METHOD1(find_account_bayes, Account *(std::vector<const char*>&));
diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp
index 7c52744959..6a64a362c4 100644
--- a/libgnucash/engine/test/utest-Account.cpp
+++ b/libgnucash/engine/test/utest-Account.cpp
@@ -882,7 +882,7 @@ test_xaccFreeAccount (Fixture *fixture, gconstpointer pData)
     /* Check that we've got children, lots, and splits to remove */
     g_assert_true (p_priv->children != NULL);
     g_assert_true (p_priv->lots != NULL);
-    g_assert_true (p_priv->splits != NULL);
+    g_assert_true (p_priv->splits.size());
     g_assert_true (p_priv->parent != NULL);
     g_assert_true (p_priv->commodity != NULL);
     g_assert_cmpint (check1->hits, ==, 0);
@@ -983,7 +983,7 @@ test_xaccAccountCommitEdit (Fixture *fixture, gconstpointer pData)
     /* Check that we've got children, lots, and splits to remove */
     g_assert_true (p_priv->children != NULL);
     g_assert_true (p_priv->lots != NULL);
-    g_assert_true (p_priv->splits != NULL);
+    g_assert_true (p_priv->splits.size());
     g_assert_true (p_priv->parent != NULL);
     g_assert_true (p_priv->commodity != NULL);
     g_assert_cmpint (check1->hits, ==, 0);
@@ -998,7 +998,7 @@ test_xaccAccountCommitEdit (Fixture *fixture, gconstpointer pData)
     test_signal_assert_hits (sig2, 0);
     g_assert_true (p_priv->children != NULL);
     g_assert_true (p_priv->lots != NULL);
-    g_assert_true (p_priv->splits != NULL);
+    g_assert_true (p_priv->splits.size());
     g_assert_true (p_priv->parent != NULL);
     g_assert_true (p_priv->commodity != NULL);
     g_assert_cmpint (check1->hits, ==, 0);
@@ -1393,19 +1393,19 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
 
     /* Check that the call fails with invalid account and split (throws) */
     g_assert_true (!gnc_account_insert_split (NULL, split1));
-    g_assert_cmpuint (g_list_length (priv->splits), == , 0);
+    g_assert_cmpuint (priv->splits.size(), == , 0);
     g_assert_true (!priv->sort_dirty);
     g_assert_true (!priv->balance_dirty);
     test_signal_assert_hits (sig1, 0);
     test_signal_assert_hits (sig2, 0);
     g_assert_true (!gnc_account_insert_split (fixture->acct, NULL));
-    g_assert_cmpuint (g_list_length (priv->splits), == , 0);
+    g_assert_cmpuint (priv->splits.size(), == , 0);
     g_assert_true (!priv->sort_dirty);
     g_assert_true (!priv->balance_dirty);
     test_signal_assert_hits (sig1, 0);
     test_signal_assert_hits (sig2, 0);
     /* g_assert_true (!gnc_account_insert_split (fixture->acct, (Split*)priv)); */
-    /* g_assert_cmpuint (g_list_length (priv->splits), == , 0); */
+    /* g_assert_cmpuint (priv->splits.size(), == , 0); */
     /* g_assert_true (!priv->sort_dirty); */
     /* g_assert_true (!priv->balance_dirty); */
     /* test_signal_assert_hits (sig1, 0); */
@@ -1418,7 +1418,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
 
     /* Check that it works the first time */
     g_assert_true (gnc_account_insert_split (fixture->acct, split1));
-    g_assert_cmpuint (g_list_length (priv->splits), == , 1);
+    g_assert_cmpuint (priv->splits.size(), == , 1);
     g_assert_true (!priv->sort_dirty);
     g_assert_true (priv->balance_dirty);
     test_signal_assert_hits (sig1, 1);
@@ -1430,7 +1430,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
     sig3 = test_signal_new (&fixture->acct->inst, GNC_EVENT_ITEM_ADDED, split2);
     /* Now add a second split to the account and check that sort_dirty isn't set. We have to bump the editlevel to force this. */
     g_assert_true (gnc_account_insert_split (fixture->acct, split2));
-    g_assert_cmpuint (g_list_length (priv->splits), == , 2);
+    g_assert_cmpuint (priv->splits.size(), == , 2);
     g_assert_true (!priv->sort_dirty);
     g_assert_true (priv->balance_dirty);
     test_signal_assert_hits (sig1, 2);
@@ -1441,7 +1441,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
     qof_instance_increase_editlevel (fixture->acct);
     g_assert_true (gnc_account_insert_split (fixture->acct, split3));
     qof_instance_decrease_editlevel (fixture->acct);
-    g_assert_cmpuint (g_list_length (priv->splits), == , 3);
+    g_assert_cmpuint (priv->splits.size(), == , 3);
     g_assert_true (priv->sort_dirty);
     g_assert_true (priv->balance_dirty);
     test_signal_assert_hits (sig1, 3);
@@ -1452,7 +1452,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
     sig3 = test_signal_new (&fixture->acct->inst, GNC_EVENT_ITEM_REMOVED,
                             split3);
     g_assert_true (gnc_account_remove_split (fixture->acct, split3));
-    g_assert_cmpuint (g_list_length (priv->splits), == , 2);
+    g_assert_cmpuint (priv->splits.size(), == , 2);
     g_assert_true (priv->sort_dirty);
     g_assert_true (!priv->balance_dirty);
     test_signal_assert_hits (sig1, 4);
@@ -1460,7 +1460,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
     /* And do it again to make sure that it fails when the split has
      * already been removed */
     g_assert_true (!gnc_account_remove_split (fixture->acct, split3));
-    g_assert_cmpuint (g_list_length (priv->splits), == , 2);
+    g_assert_cmpuint (priv->splits.size(), == , 2);
     g_assert_true (priv->sort_dirty);
     g_assert_true (!priv->balance_dirty);
     test_signal_assert_hits (sig1, 4);



Summary of changes:
 gnucash/gnome-utils/dialog-account.c               |   8 +-
 gnucash/gnome-utils/dialog-transfer.cpp            |   5 +-
 gnucash/gnome-utils/gnc-autoclear.c                |   1 +
 gnucash/gnome-utils/gnc-file.c                     |   1 +
 gnucash/gnome/assistant-stock-transaction.cpp      |  10 +-
 gnucash/gnome/dialog-lot-viewer.c                  |   1 +
 gnucash/gnome/dialog-sx-editor.c                   |   2 +
 gnucash/gnome/gnc-plugin-page-account-tree.cpp     |  19 +-
 .../test/gtest-assistant-stock-transaction.cpp     |   3 +-
 gnucash/gnome/window-reconcile.cpp                 |  30 +-
 gnucash/import-export/import-backend.cpp           |   5 +-
 gnucash/import-export/import-main-matcher.cpp      |   4 +-
 .../ledger-core/split-register-control.cpp         |   1 +
 libgnucash/engine/Account.cpp                      | 340 +++++++--------------
 libgnucash/engine/Account.h                        |   6 -
 .../engine/{gnc-commodity.hpp => Account.hpp}      |  40 +--
 libgnucash/engine/AccountP.hpp                     |   3 +-
 libgnucash/engine/CMakeLists.txt                   |   1 +
 libgnucash/engine/SX-book.c                        |   1 +
 libgnucash/engine/SchedXaction.c                   |   1 +
 libgnucash/engine/SchedXaction.h                   |   1 +
 libgnucash/engine/Scrub.cpp                        |  10 +-
 libgnucash/engine/Scrub2.cpp                       |   8 +-
 libgnucash/engine/ScrubBusiness.c                  |   4 +-
 libgnucash/engine/cap-gains.cpp                    |   6 +-
 libgnucash/engine/mocks/gmock-Account.cpp          |  10 +
 libgnucash/engine/mocks/gmock-Account.h            |   2 +
 libgnucash/engine/policy.cpp                       |  41 +--
 libgnucash/engine/test-core/test-engine-stuff.cpp  |   1 -
 libgnucash/engine/test/utest-Account.cpp           |  22 +-
 libgnucash/engine/test/utest-Invoice.c             |   3 +-
 31 files changed, 228 insertions(+), 362 deletions(-)
 copy libgnucash/engine/{gnc-commodity.hpp => Account.hpp} (62%)



More information about the gnucash-changes mailing list