gnucash future: Set C and C++ standards to 23.

John Ralls jralls at code.gnucash.org
Tue Jun 23 17:50:50 EDT 2026


Updated	 via  https://github.com/Gnucash/gnucash/commit/2b93466c (commit)
	from  https://github.com/Gnucash/gnucash/commit/122cb6dc (commit)



commit 2b93466c213b082bb2d382ee377a936bd695122f
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jun 22 19:37:33 2026 -0700

    Set C and C++ standards to 23.
    
    This required some adjustments:
    * New warnings character-conversion and deprecated-volatile are
    disabled for the reasons indicated in the adjacent comments.
    * C23 is a lot stricter about function pointer signatures so some
    callbacks in gnc-frequency.c and dialog-sx-editor had to be cast using
    the long-available GLib macros.
    * C++23 is stricter about a reference (foo&) parameter taking an
    lvalue only; a lambda used in several csv-importer files was trying to
    pass an rvalue so the parameter had to be changed to perfect
    forward (foo&&).
    * C++23 doesn't allow oring together enum values from different enum
    types, even anonymous ones, so the two gnc_numeric enums for rounding
    and denominators are combined into a single enum GncNumericRoundDenom.
    * C++20's new automatic generation of comparison functions combined
    with its stricter automatic conversion rules caused trouble with
    operator==() and operator!=() for gnc::GUID and GncGUID, complaining
    about ambiguity or not being able to find operator==(const GncGuid&,
    const GncGuid&). The fix has two parts: Changing operator!=(const
    gnc::GUID&, const gnc::GUID&) to operator==(const gnc::GUID&, const
    gnc::GUID&) to resolve the conversion problem and changing the call
    forwarding operator==(const GncGuid&, const GncGuid&) to only cast the
    first argument to gnc::GUID so that it could work unambiguously with
    operator==(const gnc::GUID&, const GncGuid&).
    * python sqlite3test: C23 apparently makes AppleClang a lot pickier
    about includes.

diff --git a/.github/workflows/mac-tests.yaml b/.github/workflows/mac-tests.yaml
index 2cce9cf266..2d9ddb80bd 100644
--- a/.github/workflows/mac-tests.yaml
+++ b/.github/workflows/mac-tests.yaml
@@ -3,7 +3,7 @@ on: [push, pull_request]
 permissions: {}
 jobs:
   ci_tests_mac:
-    runs-on: macos-latest
+    runs-on: macos-26
     name: macOS CI Tests
     env:
       TZ: America/Los_Angeles
@@ -28,8 +28,9 @@ jobs:
 
       id: googletest
       with:
-        file-url: 'https://github.com/google/googletest/archive/release-1.10.0.tar.gz'
-        file-name: googletest.tar.gz
+        file-url: 'https://github.com/google/googletest/releases/download/v1.17.0/googletest-1.17.0.tar.gz'
+        file-name: googletest-1.17.0.tar.gz
+        sha256: '65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c'
     - name: Extract Dependencies
       run: |
         mkdir -p $HOME/gnucash/inst
@@ -37,7 +38,7 @@ jobs:
         tar -xf $GITHUB_WORKSPACE/gnucash-future-mac-dependencies.tar.xz
         mkdir $HOME/gnucash/source
         cd $HOME/gnucash/source
-        tar -xf $GITHUB_WORKSPACE/googletest.tar.gz
+        tar -xf $GITHUB_WORKSPACE/googletest-1.17.0.tar.gz
         mv googletest-* googletest
     - name: Configure GnuCash
       run: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d3f18739b..67867ba2c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -616,21 +616,31 @@ endif()
 add_definitions(-D_GNU_SOURCE)
 
 # Set up the language standards:
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 23)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
-set(CMAKE_C_STANDARD 11)
+set(CMAKE_C_STANDARD 23)
 set(CMAKE_C_STANDARD_REQUIRED ON)
 set(CMAKE_C_EXTENSIONS ON)
 
+# -Wno-deprecated-volatile, -Wno-volatile: Guile heavily uses
+# -volatile. Guile is C but we include its headers in C++ swig files.
+if (CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang")
+  set(volatile_warning "-Wno-deprecated-volatile")
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  set(volatile_warning "-Wno-volatile")
+else()
+  message(FATAL_ERROR "Only clang and gcc are supported compilers.")
+endif()
 if (UNIX)
   set( CMAKE_C_FLAGS "-Werror -Wall -Wmissing-prototypes -Wmissing-declarations ${CMAKE_C_FLAGS}")
-  set( CMAKE_CXX_FLAGS "-Werror -Wall -Wmissing-declarations ${CMAKE_CXX_FLAGS}")
+  # -Wno-character-conversions: Googletest casts char8_t* to char32_t
+  set( CMAKE_CXX_FLAGS "-Werror -Wall -Wmissing-declarations -Wno-character-conversion  ${volatile_warning} ${CMAKE_CXX_FLAGS}")
   set( CMAKE_C_FLAGS_RELEASE "-O3 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 ${CMAKE_C_FLAGS}")
 endif()
 if (MINGW)
   set( CMAKE_C_FLAGS "-Werror -Wall -Wmissing-prototypes -Wmissing-declarations ${CMAKE_C_FLAGS}")
-  set( CMAKE_CXX_FLAGS "-Werror -Wall -Wmissing-declarations -DWINVER=0x0A00 ${CMAKE_CXX_FLAGS}") # Workaround for bug in gtest on mingw, see https://github.com/google/googletest/issues/893 and https://github.com/google/googletest/issues/920
+  set( CMAKE_CXX_FLAGS "-Werror -Wall -Wmissing-declarations -Wno-character-conversion ${volatile_warning} -DWINVER=0x0A00 ${CMAKE_CXX_FLAGS}") # Workaround for bug in gtest on mingw, see https://github.com/google/googletest/issues/893 and https://github.com/google/googletest/issues/920
 endif()
 
 if (APPLE)
diff --git a/bindings/python/sqlite3test.c b/bindings/python/sqlite3test.c
index 9195bcd97d..5d88d674d3 100644
--- a/bindings/python/sqlite3test.c
+++ b/bindings/python/sqlite3test.c
@@ -19,7 +19,9 @@
 \********************************************************************/
 
 #include <config.h>
-#include "qofsession.h"
+#include <unistd.h>
+#include <gnc-engine.h>
+#include <qof.h>
 #define TESTFILE "/tmp/blah.gnucash"
 int main()
 {
diff --git a/gnucash/gnome-utils/gnc-frequency.c b/gnucash/gnome-utils/gnc-frequency.c
index e7de0ade15..a69665119b 100644
--- a/gnucash/gnome-utils/gnc-frequency.c
+++ b/gnucash/gnome-utils/gnc-frequency.c
@@ -152,13 +152,13 @@ gnc_frequency_init(GncFrequency *gf)
         void (*fn)();
     } comboBoxes[] =
     {
-        { "freq_combobox",              freq_combo_changed },
-        { "semimonthly_first",          semimonthly_sel_changed },
-        { "semimonthly_first_weekend",  semimonthly_sel_changed },
-        { "semimonthly_second",         semimonthly_sel_changed },
-        { "semimonthly_second_weekend", semimonthly_sel_changed },
-        { "monthly_day",                monthly_sel_changed },
-        { "monthly_weekend",            monthly_sel_changed },
+        { "freq_combobox",              G_CALLBACK(freq_combo_changed) },
+        { "semimonthly_first",          G_CALLBACK(semimonthly_sel_changed) },
+        { "semimonthly_first_weekend",  G_CALLBACK(semimonthly_sel_changed) },
+        { "semimonthly_second",         G_CALLBACK(semimonthly_sel_changed) },
+        { "semimonthly_second_weekend", G_CALLBACK(semimonthly_sel_changed) },
+        { "monthly_day",                G_CALLBACK(monthly_sel_changed) },
+        { "monthly_weekend",            G_CALLBACK(monthly_sel_changed) },
         { NULL,                         NULL }
     };
 
@@ -168,11 +168,11 @@ gnc_frequency_init(GncFrequency *gf)
         void (*fn)();
     } spinVals[] =
     {
-        { "daily_spin",       spin_changed_helper },
-        { "weekly_spin",      spin_changed_helper },
-        { "semimonthly_spin", spin_changed_helper },
-        { "monthly_spin",     spin_changed_helper },
-        { NULL,               NULL }
+        { "daily_spin",       G_CALLBACK(spin_changed_helper) },
+        { "weekly_spin",      G_CALLBACK(spin_changed_helper) },
+        { "semimonthly_spin", G_CALLBACK(spin_changed_helper) },
+        { "monthly_spin",     G_CALLBACK(spin_changed_helper) },
+      { NULL,               NULL }
     };
 
     gtk_orientable_set_orientation (GTK_ORIENTABLE(gf), GTK_ORIENTATION_VERTICAL);
diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c
index 7e2bc00e1e..1cae867e51 100644
--- a/gnucash/gnome/dialog-sx-editor.c
+++ b/gnucash/gnome/dialog-sx-editor.c
@@ -1193,19 +1193,19 @@ gnc_ui_scheduled_xaction_editor_dialog_create (GtkWindow *parent,
         void     (*fn)();
         gpointer objectData;
     } widgets[] =
-          {
-              { "ok_button",      "clicked",       editor_ok_button_clicked_cb,     NULL },
-              { "cancel_button",  "clicked",       editor_cancel_button_clicked_cb, NULL },
-              { "help_button",    "clicked",       editor_help_button_clicked_cb,   NULL },
-              { "rb_noend",       "toggled",       endgroup_rb_toggled_cb,          GINT_TO_POINTER (END_NEVER_OPTION) },
-              { "rb_enddate",     "toggled",       endgroup_rb_toggled_cb,          GINT_TO_POINTER (END_DATE_OPTION) },
-              { "rb_num_occur",   "toggled",       endgroup_rb_toggled_cb,          GINT_TO_POINTER (NUM_OCCUR_OPTION) },
-              { "remain_spin" ,   "value-changed", sxed_excal_update_adapt_cb,      NULL },
-              { "enabled_opt",    "toggled",       enabled_toggled_cb,              NULL },
-              { "autocreate_opt", "toggled",       autocreate_toggled_cb,           NULL },
-              { "advance_opt",    "toggled",       advance_toggled_cb,              NULL },
-              { "remind_opt",     "toggled",       remind_toggled_cb,               NULL },
-              { NULL,             NULL,            NULL,                            NULL }
+        {
+            { "ok_button",     "clicked",       G_CALLBACK(editor_ok_button_clicked_cb),     NULL },
+            { "cancel_button", "clicked",       G_CALLBACK(editor_cancel_button_clicked_cb), NULL },
+            { "help_button",   "clicked",       G_CALLBACK(editor_help_button_clicked_cb),   NULL },
+            { "rb_noend",      "toggled",       G_CALLBACK(endgroup_rb_toggled_cb),          GINT_TO_POINTER (END_NEVER_OPTION) },
+            { "rb_enddate",    "toggled",       G_CALLBACK(endgroup_rb_toggled_cb),          GINT_TO_POINTER (END_DATE_OPTION) },
+            { "rb_num_occur",  "toggled",       G_CALLBACK(endgroup_rb_toggled_cb),          GINT_TO_POINTER (NUM_OCCUR_OPTION) },
+            { "remain_spin" ,  "value-changed", G_CALLBACK(sxed_excal_update_adapt_cb),      NULL },
+            { "enabled_opt",   "toggled",       G_CALLBACK(enabled_toggled_cb),              NULL },
+            { "autocreate_opt", "toggled",       G_CALLBACK(autocreate_toggled_cb),           NULL },
+            { "advance_opt",   "toggled",       G_CALLBACK(advance_toggled_cb),              NULL },
+            { "remind_opt",    "toggled",       G_CALLBACK(remind_toggled_cb),               NULL },
+            { NULL,             NULL,            NULL,                            NULL }
           };
 
     dlgExists = gnc_find_gui_components (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index c62ed927ae..e88c4d40ff 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1431,7 +1431,7 @@ CsvImpTransAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte
         fcolor = "black";
         bcolor = "pink";
         err_msg = std::string(_("This line has the following parse issues:"));
-        auto add_non_acct_err_bullet = [](std::string& a, ErrMap::value_type& b)->std::string
+        auto add_non_acct_err_bullet = [](std::string&& a, ErrMap::value_type& b)->std::string
                                 {
                                     if ((b.first == GncTransPropType::ACCOUNT) ||
                                         (b.first == GncTransPropType::TACCOUNT))
@@ -2130,7 +2130,7 @@ CsvImpTransAssist::assist_match_page_prepare ()
          */
         auto err_msg = std::string(err.what());
         auto err_msgs = err.errors();
-        auto add_bullet_item = [](std::string& a, ErrMap::value_type& b)->std::string { return std::move(a) + "\n• " + b.second; };
+        auto add_bullet_item = [](std::string&& a, ErrMap::value_type& b)->std::string { return std::move(a) + "\n• " + b.second; };
         err_msg = std::accumulate (err_msgs.begin(), err_msgs.end(), std::move (err_msg), add_bullet_item);
 
         gnc_error_dialog (GTK_WINDOW (csv_imp_asst),
diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp b/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp
index e14d3b4ebb..e9645d2675 100644
--- a/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp
+++ b/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp
@@ -332,7 +332,10 @@ std::shared_ptr<DraftTransaction> GncPreTrans::create_trans (QofBook* book, gnc_
     if (!check.empty())
     {
         auto err_msg = std::string("Not creating transaction because essentials not set properly:");
-        auto add_bullet_item = [](std::string& a, std::string& b)->std::string { return std::move(a) + "\n• " + b; };
+        auto add_bullet_item = [](std::string&& a, std::string& b)->std::string {
+            static const std::string bullet{"\n• "};
+            return std::move(a) + "\n• " + b;
+        };
         err_msg = std::accumulate (check.begin(), check.end(), std::move (err_msg), add_bullet_item);
         PWARN ("%s", err_msg.c_str());
         return nullptr;
@@ -721,7 +724,7 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
     if (!check.empty())
     {
         auto err_msg = std::string("Not creating split because essentials not set properly:");
-        auto add_bullet_item = [](std::string& a, std::string& b)->std::string { return std::move(a) + "\n• " + b; };
+        auto add_bullet_item = [](std::string&& a, std::string& b)->std::string { return std::move(a) + "\n• " + b; };
         err_msg = std::accumulate (check.begin(), check.end(), std::move (err_msg), add_bullet_item);
         PWARN ("%s", err_msg.c_str());
         return;
diff --git a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp
index ae5e6f396f..53dbe18442 100644
--- a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp
+++ b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp
@@ -42,7 +42,6 @@
 #include <iostream>
 #include <string>
 
-const std::string csv_group_prefix{"CSV-"};
 const std::string no_settings{N_("No Settings")};
 const std::string gnc_exp{N_("GnuCash Export Format")};
 const std::string gnc_exp_4{N_("GnuCash Export Format (4.x and older)")};
diff --git a/gnucash/import-export/csv-imp/gnc-import-tx.cpp b/gnucash/import-export/csv-imp/gnc-import-tx.cpp
index 9484e0a26a..5b0b53bcc1 100644
--- a/gnucash/import-export/csv-imp/gnc-import-tx.cpp
+++ b/gnucash/import-export/csv-imp/gnc-import-tx.cpp
@@ -471,7 +471,7 @@ std::string ErrorList::str()
     auto err_msg = std::string();
     if (!m_error.empty())
     {
-        auto add_bullet_item = [](std::string& a, std::string& b)->std::string { return std::move(a) + "\n• " + b; };
+        auto add_bullet_item = [](std::string&& a, std::string& b)->std::string { return std::move(a) + "\n• " + b; };
         err_msg = std::accumulate (m_error.begin(), m_error.end(), std::move (err_msg), add_bullet_item);
         err_msg.erase (0, 1);
     }
diff --git a/gnucash/register/ledger-core/split-register.c b/gnucash/register/ledger-core/split-register.c
index 3afd39dcf8..9ae1d7e146 100644
--- a/gnucash/register/ledger-core/split-register.c
+++ b/gnucash/register/ledger-core/split-register.c
@@ -883,7 +883,7 @@ gnc_split_register_copy_current_internal (SplitRegister* reg,
     }
 
     copied_item.cursor_class = cursor_class;
-    gnc_hook_add_dangler (HOOK_BOOK_CLOSED, clear_copied_item, NULL, NULL);
+    gnc_hook_add_dangler (HOOK_BOOK_CLOSED, (GFunc)clear_copied_item, NULL, NULL);
     LEAVE ("%s %s", use_cut_semantics ? "cut" : "copied",
            cursor_class == CURSOR_CLASS_SPLIT ? "split" : "transaction");
 }
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 019c613a53..5288d505a2 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -67,7 +67,6 @@ static const std::string KEY_INCLUDE_CHILDREN("include-children");
 static const std::string KEY_POSTPONE("postpone");
 static const std::string KEY_LOT_MGMT("lot-mgmt");
 static const std::string KEY_ONLINE_ID("online_id");
-static const std::string KEY_IMP_APPEND_TEXT("import-append-text");
 static const std::string AB_KEY("hbci");
 static const std::string AB_ACCOUNT_ID("account-id");
 static const std::string AB_ACCOUNT_UID("account-uid");
diff --git a/libgnucash/engine/gnc-numeric.h b/libgnucash/engine/gnc-numeric.h
index 102270f3af..c0a143cc55 100644
--- a/libgnucash/engine/gnc-numeric.h
+++ b/libgnucash/engine/gnc-numeric.h
@@ -140,7 +140,7 @@ typedef struct _gnc_numeric gnc_numeric;
  *
  * Possible rounding instructions are:
  */
-enum
+enum GncNumericRoundDenom
 {
     /** Round toward -infinity */
     GNC_HOW_RND_FLOOR            = 0x01,
@@ -174,12 +174,8 @@ enum
     /** Never round at all, and signal an error if there is a
      *  fractional result in a computation.
      */
-    GNC_HOW_RND_NEVER            = 0x08
-};
-
+    GNC_HOW_RND_NEVER            = 0x08,
 /** How to compute a denominator, or'ed into the "how" field. */
-enum
-{
     /** Use any denominator which gives an exactly correct ratio of
      *  numerator to denominator. Use EXACT when you do not wish to
      *  lose any information in the result but also do not want to
diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index 946f3c1d03..0b8e928b28 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -430,9 +430,9 @@ bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
 }
 
 bool
-operator != (GUID const & one, GUID const & two) noexcept
+operator == (GUID const & one, GUID const & two) noexcept
 {
-    return one.implementation != two.implementation;
+    return one.implementation == two.implementation;
 }
 
 GUID & GUID::operator = (GUID && other) noexcept
@@ -453,5 +453,5 @@ GUID::operator GncGUID () const noexcept
 bool
 operator==(const GncGUID& lhs, const GncGUID& rhs)
 {
-    return gnc::GUID{lhs} == gnc::GUID{rhs};
+    return gnc::GUID{lhs} == rhs;
 }
diff --git a/libgnucash/engine/guid.hpp b/libgnucash/engine/guid.hpp
index 7a883ecd9b..10e48b9a32 100644
--- a/libgnucash/engine/guid.hpp
+++ b/libgnucash/engine/guid.hpp
@@ -58,10 +58,10 @@ struct GUID
     auto end () const noexcept -> decltype (implementation.end ());
     bool operator < (GUID const &) noexcept;
     friend bool operator == (GUID const &, GncGUID const &) noexcept;
-    friend bool operator != (GUID const &, GUID const &) noexcept;
+    friend bool operator == (GUID const &, GUID const &) noexcept;
 };
 
-bool operator != (GUID const &, GUID const &) noexcept;
+bool operator == (GUID const &, GUID const &) noexcept;
 bool operator == (GUID const &, GncGUID const &) noexcept;
 
 



Summary of changes:
 .github/workflows/mac-tests.yaml                   |  9 ++++----
 CMakeLists.txt                                     | 18 +++++++++++----
 bindings/python/sqlite3test.c                      |  4 +++-
 gnucash/gnome-utils/gnc-frequency.c                | 24 ++++++++++----------
 gnucash/gnome/dialog-sx-editor.c                   | 26 +++++++++++-----------
 .../csv-imp/assistant-csv-trans-import.cpp         |  4 ++--
 gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp |  7 ++++--
 .../import-export/csv-imp/gnc-imp-settings-csv.cpp |  1 -
 gnucash/import-export/csv-imp/gnc-import-tx.cpp    |  2 +-
 gnucash/register/ledger-core/split-register.c      |  2 +-
 libgnucash/engine/Account.cpp                      |  1 -
 libgnucash/engine/gnc-numeric.h                    |  8 ++-----
 libgnucash/engine/guid.cpp                         |  6 ++---
 libgnucash/engine/guid.hpp                         |  4 ++--
 14 files changed, 63 insertions(+), 53 deletions(-)



More information about the gnucash-changes mailing list