gnucash master: Implement KvpFrame in C++ using std::vector

John Ralls jralls at code.gnucash.org
Mon Nov 3 15:29:11 EST 2014


Updated	 via  https://github.com/Gnucash/gnucash/commit/076f1fb2 (commit)
	from  https://github.com/Gnucash/gnucash/commit/6c2a42bf (commit)



commit 076f1fb25df02602a9d1ed5ecdebc4a1f3d18940
Author: lmat <dartme18 at gmail.com>
Date:   Fri Oct 3 22:05:37 2014 -0400

    Implement KvpFrame in C++ using std::vector
    
    KvpFrame was implemented using GList. Given the current desire
    to distance ourselves from glib and acquaint the project with
    C++, the standard library thereof, and boost libraries, KvpFrame
    has been replaced by an implementation that uses a std::map<
    const char *, KvpValueImpl *>.
    
    There were some cases of the KvpFrame's glist being accessed
    directly. A new API to help callers access the KvpFrame's contents
    systematically by providing a list of keys has been created, and
    call sites of the GList code have been updated.
    
    Another deprecated #define was found and removed (kvp_frame_set_str).

diff --git a/src/backend/xml/sixtp-dom-generators.c b/src/backend/xml/sixtp-dom-generators.c
index 0fc8e74..6217697 100644
--- a/src/backend/xml/sixtp-dom-generators.c
+++ b/src/backend/xml/sixtp-dom-generators.c
@@ -258,10 +258,8 @@ add_text_to_node(xmlNodePtr node, gchar *type, gchar *val)
     g_free(newval);
 }
 
-
-
 static void
-add_kvp_slot(gpointer key, gpointer value, gpointer data);
+add_kvp_slot(const char * key, KvpValue* value, xmlNodePtr node);
 
 static void
 add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val)
@@ -342,15 +340,19 @@ add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val)
     case KVP_TYPE_FRAME:
     {
         KvpFrame *frame;
+        const char ** keys;
+        unsigned int i;
 
         xmlSetProp(val_node, BAD_CAST "type", BAD_CAST "frame");
 
         frame = kvp_value_get_frame (val);
-        if (!frame || !kvp_frame_get_hash (frame))
+        if (!frame)
             break;
 
-        g_hash_table_foreach_sorted(kvp_frame_get_hash(frame),
-                                    add_kvp_slot, val_node, (GCompareFunc)strcmp);
+        keys = kvp_frame_get_keys(frame);
+        for (i = 0; keys[i]; ++i)
+            add_kvp_slot(keys[i], kvp_frame_get_value(frame, keys[i]), val_node);
+        g_free(keys);
     }
     break;
     default:
@@ -359,43 +361,36 @@ add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val)
 }
 
 static void
-add_kvp_slot(gpointer key, gpointer value, gpointer data)
+add_kvp_slot(const char * key, KvpValue* value, xmlNodePtr node)
 {
     xmlNodePtr slot_node;
-    xmlNodePtr node = (xmlNodePtr)data;
     gchar *newkey = g_strdup ((gchar*)key);
     slot_node = xmlNewChild(node, NULL, BAD_CAST "slot", NULL);
 
     xmlNewTextChild(slot_node, NULL, BAD_CAST "slot:key",
 		    checked_char_cast (newkey));
     g_free (newkey);
-    add_kvp_value_node(slot_node, "slot:value", (KvpValue*)value);
+    add_kvp_value_node(slot_node, "slot:value", value);
 }
 
 xmlNodePtr
 kvp_frame_to_dom_tree(const char *tag, const KvpFrame *frame)
 {
     xmlNodePtr ret;
+    const char ** keys;
+    unsigned int i;
 
     if (!frame)
     {
         return NULL;
     }
 
-    if (!kvp_frame_get_hash(frame))
-    {
-        return NULL;
-    }
-
-    if (g_hash_table_size(kvp_frame_get_hash(frame)) == 0)
-    {
-        return NULL;
-    }
-
     ret = xmlNewNode(NULL, BAD_CAST tag);
 
-    g_hash_table_foreach_sorted(kvp_frame_get_hash(frame),
-                                add_kvp_slot, ret, (GCompareFunc)strcmp);
+    keys = kvp_frame_get_keys(frame);
+    for (i = 0; keys[i]; ++i)
+        add_kvp_slot(keys[i], kvp_frame_get_value(frame, keys[i]), ret);
+    g_free(keys);
 
     return ret;
 }
diff --git a/src/engine/Split.c b/src/engine/Split.c
index e5cf17d..345d30a 100644
--- a/src/engine/Split.c
+++ b/src/engine/Split.c
@@ -2057,7 +2057,7 @@ xaccSplitMakeStockSplit(Split *s)
     xaccTransBeginEdit (s->parent);
 
     s->value = gnc_numeric_zero();
-    kvp_frame_set_str(s->inst.kvp_data, "split-type", "stock-split");
+    kvp_frame_set_string(s->inst.kvp_data, "split-type", "stock-split");
     SET_GAINS_VDIRTY(s);
     mark_split(s);
     qof_instance_set_dirty(QOF_INSTANCE(s));
diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c
index 61a6f57..01caf28 100644
--- a/src/engine/Transaction.c
+++ b/src/engine/Transaction.c
@@ -2018,7 +2018,7 @@ xaccTransSetTxnType (Transaction *trans, char type)
     char s[2] = {type, '\0'};
     g_return_if_fail(trans);
     xaccTransBeginEdit(trans);
-    kvp_frame_set_str (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s);
+    kvp_frame_set_string (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2041,7 +2041,7 @@ xaccTransSetReadOnly (Transaction *trans, const char *reason)
     if (trans && reason)
     {
         xaccTransBeginEdit(trans);
-        kvp_frame_set_str (trans->inst.kvp_data,
+        kvp_frame_set_string (trans->inst.kvp_data,
                            TRANS_READ_ONLY_REASON, reason);
         qof_instance_set_dirty(QOF_INSTANCE(trans));
         xaccTransCommitEdit(trans);
@@ -2098,7 +2098,7 @@ xaccTransSetAssociation (Transaction *trans, const char *assoc)
     if (!trans || !assoc) return;
     xaccTransBeginEdit(trans);
 
-    kvp_frame_set_str (trans->inst.kvp_data, assoc_uri_str, assoc);
+    kvp_frame_set_string (trans->inst.kvp_data, assoc_uri_str, assoc);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2117,7 +2117,7 @@ xaccTransSetNotes (Transaction *trans, const char *notes)
     if (!trans || !notes) return;
     xaccTransBeginEdit(trans);
 
-    kvp_frame_set_str (trans->inst.kvp_data, trans_notes_str, notes);
+    kvp_frame_set_string (trans->inst.kvp_data, trans_notes_str, notes);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
diff --git a/src/engine/gnc-lot.c b/src/engine/gnc-lot.c
index b0474e4..f0c951a 100644
--- a/src/engine/gnc-lot.c
+++ b/src/engine/gnc-lot.c
@@ -455,7 +455,7 @@ gnc_lot_set_title (GNCLot *lot, const char *str)
     if (!lot) return;
     qof_begin_edit(QOF_INSTANCE(lot));
     qof_instance_set_dirty(QOF_INSTANCE(lot));
-    kvp_frame_set_str (qof_instance_get_slots(QOF_INSTANCE (lot)),
+    kvp_frame_set_string (qof_instance_get_slots(QOF_INSTANCE (lot)),
 		       "/title", str);
     gnc_lot_commit_edit(lot);
 }
@@ -466,7 +466,7 @@ gnc_lot_set_notes (GNCLot *lot, const char *str)
     if (!lot) return;
     gnc_lot_begin_edit(lot);
     qof_instance_set_dirty(QOF_INSTANCE(lot));
-    kvp_frame_set_str (qof_instance_get_slots (QOF_INSTANCE (lot)),
+    kvp_frame_set_string (qof_instance_get_slots (QOF_INSTANCE (lot)),
 		       "/notes", str);
     gnc_lot_commit_edit(lot);
 }
diff --git a/src/libqof/qof/kvp-value.cpp b/src/libqof/qof/kvp-value.cpp
index 4df79c3..f92e711 100644
--- a/src/libqof/qof/kvp-value.cpp
+++ b/src/libqof/qof/kvp-value.cpp
@@ -1,5 +1,7 @@
 /********************************************************************\
  * kvp-value.cpp -- Implements a key-value frame system             *
+ * Copyright (C) 2014 Aaron Laws                                    *
+ *                                                                  *
  * 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   *
@@ -20,11 +22,11 @@
 \********************************************************************/
 
 #include "kvp-value.hpp"
+#include "kvp_frame.hpp"
 #include <cmath>
 #include <sstream>
 #include <iomanip>
 #include <stdexcept>
-#include "kvp_frame-p.hpp"
 
 KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept
 {
@@ -75,7 +77,7 @@ KvpValueImpl::get_type() const noexcept
         return KvpValueType::KVP_TYPE_TIMESPEC;
     else if (datastore.type() == typeid(GList *))
         return KvpValueType::KVP_TYPE_GLIST;
-    else if (datastore.type() == typeid(KvpFrame *))
+    else if (datastore.type() == typeid(KvpFrameImpl *))
         return KvpValueType::KVP_TYPE_FRAME;
     else if (datastore.type() == typeid(GDate))
         return KvpValueType::KVP_TYPE_GDATE;
@@ -208,7 +210,6 @@ KvpValueImpl::to_string() const noexcept
     boost::apply_visitor(visitor, datastore);
 
     /*We still use g_strdup since the return value will be freed by g_free*/
-
     return g_strdup(ret.str().c_str());
 }
 
@@ -224,12 +225,11 @@ struct compare_visitor : boost::static_visitor<int>
     int operator()( T & one, T & two) const
     {
         /*This will work for any type that implements < and ==.*/
-        if ( one < two )
+        if (one < two)
             return -1;
-        else if (one == two)
-            return 0;
-        else
+        if (two < one)
             return 1;
+        return 0;
     }
 };
 template <> int compare_visitor::operator()(char * const & one, char * const & two) const
@@ -265,9 +265,8 @@ template <> int compare_visitor::operator()(double const & one, double const & t
     if (std::isnan(one) && std::isnan(two))
         return 0;
     if (one < two) return -1;
-    /*perhaps we should have tolerance here?*/
-    if (one == two) return 0;
-    return 1;
+    if (two < one) return 1;
+    return 0;
 }
 
 int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept
diff --git a/src/libqof/qof/kvp-value.hpp b/src/libqof/qof/kvp-value.hpp
index 4c1b1ae..bf1b616 100644
--- a/src/libqof/qof/kvp-value.hpp
+++ b/src/libqof/qof/kvp-value.hpp
@@ -1,8 +1,7 @@
-#ifndef gnc_kvp_value_type
-#define gnc_kvp_value_type
-
 /********************************************************************\
  * kvp-value.hpp -- Implements a key-value frame system             *
+ * Copyright (C) 2014 Aaron Laws                                    *
+ *                                                                  *
  * 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   *
@@ -22,6 +21,9 @@
  *                                                                  *
 \********************************************************************/
 
+#ifndef GNC_KVP_VALUE_TYPE
+#define GNC_KVP_VALUE_TYPE
+
 extern "C"
 {
 #include "config.h"
@@ -35,6 +37,7 @@ extern "C"
 
 struct KvpValueImpl
 {
+    public:
     /**
      * Performs a deep copy
      */
@@ -100,7 +103,7 @@ struct KvpValueImpl
         GncGUID *,
         Timespec,
         GList *,
-        boost::recursive_wrapper<KvpFrame *>,
+        KvpFrame *,
         GDate> datastore;
 };
 
diff --git a/src/libqof/qof/kvp_frame-p.hpp b/src/libqof/qof/kvp_frame-p.hpp
deleted file mode 100644
index 3678083..0000000
--- a/src/libqof/qof/kvp_frame-p.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef kvp_frame_p_header
-#define kvp_frame_p_header
-
-/* Note that we keep the keys for this hash table in the
- * qof_string_cache, as it is very likely we will see the
- * same keys over and over again  */
-
-struct _KvpFrame
-{
-    GHashTable  * hash;
-};
-
-
-#endif
diff --git a/src/libqof/qof/kvp_frame.cpp b/src/libqof/qof/kvp_frame.cpp
index db20f9c..4ba546e 100644
--- a/src/libqof/qof/kvp_frame.cpp
+++ b/src/libqof/qof/kvp_frame.cpp
@@ -32,108 +32,188 @@ extern "C"
 #include <string.h>
 }
 
-#include <typeinfo>
-#include <iostream>
 #include "kvp-value.hpp"
-#include "kvp_frame-p.hpp"
+#include "kvp_frame.hpp"
+#include <typeinfo>
+#include <sstream>
+#include <algorithm>
+#include <vector>
 
+KvpFrameImpl::KvpFrameImpl(const KvpFrameImpl & rhs) noexcept
+{
+    std::for_each(rhs.m_valuemap.begin(), rhs.m_valuemap.end(),
+        [this](const map_type::value_type & a)
+        {
+            auto key = static_cast<char *>(qof_string_cache_insert(a.first));
+            auto val = new KvpValueImpl(*a.second);
+            this->m_valuemap.insert({key,val});
+        }
+    );
+}
 
-/* This static indicates the debugging module that this .o belongs to.  */
-static QofLogModule log_module = QOF_MOD_KVP;
+KvpValueImpl * KvpFrameImpl::replace_nc(const char * key, KvpValueImpl * value) noexcept
+{
+    if (!key) return nullptr;
+    auto spot = m_valuemap.find(key);
+    KvpValueImpl * ret {nullptr};
+    if (spot != m_valuemap.end())
+    {
+        qof_string_cache_remove(spot->first);
+        ret = spot->second;
+        m_valuemap.erase(spot);
+    }
 
-/* *******************************************************************
- * KvpFrame functions
- ********************************************************************/
+    if (value)
+    {
+        auto cachedkey = static_cast<const char *>(qof_string_cache_insert(key));
+        m_valuemap.insert({cachedkey,value});
+    }
 
-static guint
-kvp_hash_func(gconstpointer v)
+    return ret;
+}
+
+std::string
+KvpFrameImpl::to_string() const noexcept
 {
-    return g_str_hash(v);
+    std::ostringstream ret;
+    ret << "{\n";
+
+    std::for_each(m_valuemap.begin(), m_valuemap.end(),
+        [this,&ret](const map_type::value_type &a)
+        {
+            ret << "    ";
+            if (a.first)
+                ret << a.first;
+            ret << " => ";
+            if (a.second)
+                ret << a.second->to_string();
+            ret << ",\n";
+        }
+    );
+
+    ret << "}\n";
+    return ret.str();
 }
 
-static gint
-kvp_comp_func(gconstpointer v, gconstpointer v2)
+std::vector<std::string>
+KvpFrameImpl::get_keys() const noexcept
 {
-    return g_str_equal(v, v2);
+    std::vector<std::string> ret;
+    std::for_each(m_valuemap.begin(), m_valuemap.end(),
+        [&ret](const KvpFrameImpl::map_type::value_type &a)
+        {
+            ret.push_back(a.first);
+        }
+    );
+    return ret;
 }
 
-static gboolean
-init_frame_body_if_needed(KvpFrame *f)
+void
+KvpFrameImpl::for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data),
+              void *data) const noexcept
 {
-    if (!f->hash)
-    {
-        f->hash = g_hash_table_new(&kvp_hash_func, &kvp_comp_func);
-    }
-    return(f->hash != NULL);
+    if (!proc) return;
+    std::for_each (m_valuemap.begin(), m_valuemap.end(),
+        [proc,data](const KvpFrameImpl::map_type::value_type & a)
+        {
+            proc (a.first, a.second, data);
+        }
+    );
 }
 
-KvpFrame *
-kvp_frame_new(void)
+KvpValueImpl *
+KvpFrameImpl::get_slot(const char * key) const noexcept
 {
-    KvpFrame * retval = g_new0(KvpFrame, 1);
+    auto spot = m_valuemap.find(key);
+    if (spot == m_valuemap.end())
+        return nullptr;
+    return spot->second;
+}
 
-    /* Save space until the frame is actually used */
-    retval->hash = NULL;
-    return retval;
+int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept
+{
+    if (one && !two) return 1;
+    if (!one && two) return -1;
+    if (!one && !two) return 0;
+    return compare(*one, *two);
 }
 
-static void
-kvp_frame_delete_worker(gpointer key, gpointer value, G_GNUC_UNUSED gpointer user_data)
+/**
+ * If the first KvpFrameImpl has an item that the second does not, 1 is returned.
+ * The first item within the two KvpFrameImpl that is not similar, that comparison is returned.
+ * If all the items within the first KvpFrameImpl match items within the second, but the
+ *   second has more elements, -1 is returned.
+ * Otherwise, 0 is returned.
+ */
+int compare(const KvpFrameImpl & one, const KvpFrameImpl & two) noexcept
 {
-    qof_string_cache_remove(key);
-    kvp_value_delete(static_cast<KvpValue *>(value));
+    for (const auto & a : one.m_valuemap)
+    {
+        auto otherspot = two.m_valuemap.find(a.first);
+        if (otherspot == two.m_valuemap.end())
+        {
+            return 1;
+        }
+        auto comparison = compare(a.second,otherspot->second);
+
+        if (comparison != 0)
+            return comparison;
+    }
+
+    if (one.m_valuemap.size() < two.m_valuemap.size())
+        return -1;
+    return 0;
+}
+
+/* This static indicates the debugging module that this .o belongs to.  */
+static QofLogModule log_module = QOF_MOD_KVP;
+
+KvpFrame *
+kvp_frame_new(void)
+{
+    auto ret = new KvpFrameImpl();
+    return static_cast<KvpFrame *>(ret);
 }
 
 void
 kvp_frame_delete(KvpFrame * frame)
 {
     if (!frame) return;
+    auto realframe = static_cast<KvpFrameImpl *>(frame);
+    delete realframe;
+}
 
-    if (frame->hash)
-    {
-        /* free any allocated resource for frame or its children */
-        g_hash_table_foreach(frame->hash, & kvp_frame_delete_worker,
-                             (gpointer)frame);
-
-        /* delete the hash table */
-        g_hash_table_destroy(frame->hash);
-        frame->hash = NULL;
-    }
-    g_free(frame);
+const char **
+kvp_frame_get_keys(const KvpFrame * frame)
+{
+    if (!frame) return nullptr;
+    auto realframe = static_cast<KvpFrameImpl const *>(frame);
+    const auto & keys = realframe->get_keys();
+    const char ** ret = g_new(const char *, keys.size() + 1);
+    unsigned int spot {0};
+    std::for_each(keys.begin(), keys.end(),
+        [&ret,&spot](const std::string &a)
+        {
+            ret[spot++] = g_strdup(a.c_str());
+        }
+    );
+    ret[keys.size()] = nullptr;
+    return ret;
 }
 
 gboolean
 kvp_frame_is_empty(const KvpFrame * frame)
 {
     if (!frame) return TRUE;
-    if (!frame->hash) return TRUE;
-    return FALSE;
-}
-
-static void
-kvp_frame_copy_worker(gpointer key, gpointer value, gpointer user_data)
-{
-    KvpFrame * dest = (KvpFrame *)user_data;
-    g_hash_table_insert(dest->hash,
-                        qof_string_cache_insert(key),
-                        static_cast<void*>(kvp_value_copy(static_cast<KvpValue*>(value))));
+    auto realframe = static_cast<KvpFrameImpl const *>(frame);
+    return realframe->get_keys().size() == 0;
 }
 
 KvpFrame *
 kvp_frame_copy(const KvpFrame * frame)
 {
-    KvpFrame * retval = kvp_frame_new();
-
-    if (!frame) return retval;
-
-    if (frame->hash)
-    {
-        if (!init_frame_body_if_needed(retval)) return(NULL);
-        g_hash_table_foreach(frame->hash,
-                             & kvp_frame_copy_worker,
-                             (gpointer)retval);
-    }
-    return retval;
+    auto ret = new KvpFrameImpl(*static_cast<KvpFrameImpl const *>(frame));
+    return ret;
 }
 
 /* Replace the old value with the new value.  Return the old value.
@@ -144,33 +224,11 @@ static KvpValue *
 kvp_frame_replace_slot_nc (KvpFrame * frame, const char * slot,
                            KvpValue * new_value)
 {
-    gpointer orig_key;
-    gpointer orig_value = NULL;
-    int      key_exists;
-
-    if (!frame || !slot) return NULL;
-    if (!init_frame_body_if_needed(frame)) return NULL; /* Error ... */
-
-    key_exists = g_hash_table_lookup_extended(frame->hash, slot,
-                 & orig_key, & orig_value);
-    if (key_exists)
-    {
-        g_hash_table_remove(frame->hash, slot);
-        qof_string_cache_remove(orig_key);
-    }
-    else
-    {
-        orig_value = NULL;
-    }
-
-    if (new_value)
-    {
-        g_hash_table_insert(frame->hash,
-                            qof_string_cache_insert((gpointer) slot),
-                            new_value);
-    }
+    if (!frame) return NULL;
 
-    return (KvpValue *) orig_value;
+    auto realframe = static_cast<KvpFrameImpl *>(frame);
+    auto realnewvalue = static_cast<KvpValueImpl *>(new_value);
+    return realframe->replace_nc(slot, realnewvalue);
 }
 
 /* Passing in a null value into this routine has the effect
@@ -549,15 +607,11 @@ kvp_frame_set_slot_nc(KvpFrame * frame, const char * slot,
 KvpValue *
 kvp_frame_get_slot(const KvpFrame * frame, const char * slot)
 {
-    KvpValue *v;
     if (!frame) return NULL;
-    if (!frame->hash) return NULL;
-    v = static_cast<KvpValue*>(g_hash_table_lookup(frame->hash, slot));
-    return v;
+    auto realframe = static_cast<const KvpFrameImpl *>(frame);
+    return realframe->get_slot(slot);
 }
 
-/* ============================================================ */
-
 void
 kvp_frame_set_slot_path (KvpFrame *frame,
                          KvpValue * new_value,
@@ -1060,10 +1114,8 @@ kvp_frame_for_each_slot(KvpFrame *f,
                         gpointer data)
 {
     if (!f) return;
-    if (!proc) return;
-    if (!(f->hash)) return;
-
-    g_hash_table_foreach(f->hash, (GHFunc) proc, data);
+    auto realframe = static_cast<KvpFrameImpl *>(f);
+    realframe->for_each_slot(proc, data);
 }
 
 int
@@ -1074,59 +1126,12 @@ kvp_value_compare(const KvpValue * okva, const KvpValue * okvb)
     return compare(kva, kvb);
 }
 
-typedef struct
-{
-    gint compare;
-    KvpFrame *other_frame;
-} kvp_frame_cmp_status;
-
-static void
-kvp_frame_compare_helper(const char *key, KvpValue * val, gpointer data)
-{
-    kvp_frame_cmp_status *status = (kvp_frame_cmp_status *) data;
-    if (status->compare == 0)
-    {
-        KvpFrame *other_frame = status->other_frame;
-        KvpValue *other_val = kvp_frame_get_slot(other_frame, key);
-
-        if (other_val)
-        {
-            status->compare = kvp_value_compare(val, other_val);
-        }
-        else
-        {
-            status->compare = 1;
-        }
-    }
-}
-
 gint
 kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb)
 {
-    kvp_frame_cmp_status status;
-
-    if (fa == fb) return 0;
-    /* nothing is always less than something */
-    if (!fa && fb) return -1;
-    if (fa && !fb) return 1;
-
-    /* nothing is always less than something */
-    if (!fa->hash && fb->hash) return -1;
-    if (fa->hash && !fb->hash) return 1;
-
-    status.compare = 0;
-    status.other_frame = (KvpFrame *) fb;
-
-    kvp_frame_for_each_slot((KvpFrame *) fa, kvp_frame_compare_helper, &status);
-
-    if (status.compare != 0)
-        return status.compare;
-
-    status.other_frame = (KvpFrame *) fa;
-
-    kvp_frame_for_each_slot((KvpFrame *) fb, kvp_frame_compare_helper, &status);
-
-    return(-status.compare);
+    auto realone = static_cast<const KvpFrameImpl *>(fa);
+    auto realtwo = static_cast<const KvpFrameImpl *>(fb);
+    return compare(realone, realtwo);
 }
 
 char *
@@ -1137,7 +1142,6 @@ kvp_value_to_string(const KvpValue * val)
     return realval->to_string();
 }
 
-/* struct for kvp frame static funtion testing*/
 #ifdef __cplusplus
 extern "C"
 {
@@ -1163,53 +1167,13 @@ init_static_test_pointers( void )
     p_get_trailer_or_null = get_trailer_or_null;
 }
 
-/* ----- */
-
-static void
-kvp_frame_to_string_helper(gpointer key, gpointer value, gpointer data)
-{
-    gchar *tmp_val;
-    gchar **str = (gchar**)data;
-    gchar *old_data = *str;
-
-    tmp_val = kvp_value_to_string((KvpValue *)value);
-
-    *str = g_strdup_printf("%s    %s => %s,\n",
-                           *str ? *str : "",
-                           key ? (char *) key : "",
-                           tmp_val ? tmp_val : "");
-
-    g_free(old_data);
-    g_free(tmp_val);
-}
-
-gchar*
+char*
 kvp_frame_to_string(const KvpFrame *frame)
 {
-    gchar *tmp1;
-
-    g_return_val_if_fail (frame != NULL, NULL);
-
-    tmp1 = g_strdup_printf("{\n");
-
-    if (frame->hash)
-        g_hash_table_foreach(frame->hash, kvp_frame_to_string_helper, &tmp1);
-
-    {
-        gchar *tmp2;
-        tmp2 = g_strdup_printf("%s}\n", tmp1);
-        g_free(tmp1);
-        tmp1 = tmp2;
-    }
-
-    return tmp1;
-}
-
-GHashTable*
-kvp_frame_get_hash(const KvpFrame *frame)
-{
-    g_return_val_if_fail (frame != NULL, NULL);
-    return frame->hash;
+    auto realframe = static_cast<KvpFrameImpl const *>(frame);
+    /*We'll use g_strdup
+     because it will be freed using g_free.*/
+    return g_strdup(realframe->to_string().c_str());
 }
 
 static GValue *gvalue_from_kvp_value (KvpValue *);
diff --git a/src/libqof/qof/kvp_frame.h b/src/libqof/qof/kvp_frame.h
index aa8ed2c..0f9423f 100644
--- a/src/libqof/qof/kvp_frame.h
+++ b/src/libqof/qof/kvp_frame.h
@@ -73,7 +73,7 @@ extern "C"
 #define QOF_MOD_KVP "qof.kvp"
 
 /** Opaque frame structure */
-typedef struct _KvpFrame KvpFrame;
+typedef struct KvpFrameImpl KvpFrame;
 
 /** A KvpValue is a union with possible types enumerated in the
  * KvpValueType enum. */
@@ -138,6 +138,17 @@ gboolean     kvp_frame_is_empty(const KvpFrame * frame);
 @{
 */
 
+/**
+ * Retrieve the keys for the frame.
+ *
+ * Returns a null-terminated array of the keys which can be
+ * used to look up values and determine the pairs in this frame.
+ *
+ * The caller should free the array using g_free, but should
+ * not free the keys.
+ */
+const char ** kvp_frame_get_keys(const KvpFrame * frame);
+
 /**    store the value of the
  *     gint64 at the indicated path. If not all frame components of
  *     the path exist, they are created.
@@ -169,12 +180,6 @@ void kvp_frame_set_numeric(KvpFrame * frame, const gchar * path, gnc_numeric nva
  */
 void kvp_frame_set_timespec(KvpFrame * frame, const gchar * path, Timespec ts);
 
-/** \deprecated
-
-Use kvp_frame_set_string instead of kvp_frame_set_str
-*/
-#define kvp_frame_set_str kvp_frame_set_string
-
 /** \brief Store a copy of the string at the indicated path.
 
  *    If not all frame components of the path
@@ -572,7 +577,6 @@ void kvp_frame_for_each_slot(KvpFrame *f,
 
 /** Internal helper routines, you probably shouldn't be using these. */
 gchar* kvp_frame_to_string(const KvpFrame *frame);
-GHashTable* kvp_frame_get_hash(const KvpFrame *frame);
 
 /** KvpItem: GValue Exchange
  * \brief Transfer of KVP to and from GValue, with the key
diff --git a/src/libqof/qof/kvp_frame.hpp b/src/libqof/qof/kvp_frame.hpp
new file mode 100644
index 0000000..3f5e3dc
--- /dev/null
+++ b/src/libqof/qof/kvp_frame.hpp
@@ -0,0 +1,80 @@
+/********************************************************************\
+ * kvp-frame.hpp -- Implements a key-value frame system             *
+ * Copyright (C) 2014 Aaron Laws                                    *
+ *                                                                  *
+ * 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                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_KVP_FRAME_TYPE
+#define GNC_KVP_FRAME_TYPE
+
+#include "kvp-value.hpp"
+#include <map>
+#include <string>
+#include <vector>
+#include <cstring>
+
+class cstring_comparer
+{
+    public:
+    /* Returns true if one is less than two. */
+    bool operator()(const char * one, const char * two) const
+    {
+        auto ret = std::strcmp(one, two) < 0;
+        return ret;
+    }
+};
+
+struct KvpFrameImpl
+{
+    typedef std::map<const char *, KvpValueImpl *, cstring_comparer> map_type;
+
+    public:
+    KvpFrameImpl() noexcept {};
+
+    /**
+     * Performs a deep copy.
+     */
+    KvpFrameImpl(const KvpFrameImpl &) noexcept;
+
+    /**
+     * Replaces the KvpValueImpl at the specified spot, and returns
+     * the old KvpValueImpl which used to occupy the spot.
+     *
+     * If no KvpValueImpl was at the spot, nullptr is returned.
+     */
+    KvpValueImpl * replace_nc(const char * key, KvpValueImpl * newvalue) noexcept;
+
+    std::string to_string() const noexcept;
+
+    std::vector<std::string> get_keys() const noexcept;
+
+    KvpValueImpl * get_slot(const char * key) const noexcept;
+
+    void for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data), void *data) const noexcept;
+
+    friend int compare(const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
+
+    private:
+    map_type m_valuemap;
+};
+
+int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
+int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept;
+#endif
diff --git a/src/libqof/qof/test/test-kvp_frame.c b/src/libqof/qof/test/test-kvp_frame.c
index 1c59fe5..2dc272c 100644
--- a/src/libqof/qof/test/test-kvp_frame.c
+++ b/src/libqof/qof/test/test-kvp_frame.c
@@ -185,6 +185,7 @@ test_kvp_frame_copy( Fixture *fixture, gconstpointer pData )
     g_assert( to_copy );
     g_assert( !kvp_frame_is_empty( to_copy ) );
     g_assert( to_copy != fixture->frame );
+
     g_assert_cmpint( kvp_frame_compare( fixture->frame, to_copy ), == , 0 );
     copy_gint64 = kvp_frame_get_gint64( to_copy, "gint64-type" );
     g_assert( &copy_gint64 != &test_gint64 );
@@ -1114,7 +1115,6 @@ test_kvp_frame_to_string( Fixture *fixture, gconstpointer pData )
 static void
 test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
 {
-    GHashTable *frame_hash = NULL;
     KvpValue *input_value, *output_value;
 
     g_assert( fixture->frame );
@@ -1136,9 +1136,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
     g_assert( output_value );
     g_assert( input_value != output_value ); /* copied */
     g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); /* old value removed */
-    frame_hash = kvp_frame_get_hash( fixture->frame );
-    g_assert( frame_hash );
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* be sure it was replaced */
     kvp_value_delete( input_value );
 
     g_test_message( "Test when existing path elements are not frames" );
@@ -1146,7 +1143,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
     kvp_frame_set_slot_path( fixture->frame, input_value, "test", "test2", NULL );
     g_assert( kvp_frame_get_slot_path( fixture->frame, "test2", NULL ) == NULL );/* was not added */
     g_assert_cmpint( kvp_value_compare( output_value, kvp_frame_get_slot_path( fixture->frame, "test", NULL ) ), == , 0 ); /* nothing changed */
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* didn't change */
     kvp_value_delete( input_value );
 
     g_test_message( "Test frames are created along the path when needed" );
@@ -1159,7 +1155,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
     g_assert( output_value );
     g_assert( input_value != output_value ); /* copied */
     g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 );
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 2 );
     kvp_value_delete( input_value );
 }
 
@@ -1168,7 +1163,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
 {
     /* similar to previous test except path is passed as GSList*/
     GSList *path_list = NULL;
-    GHashTable *frame_hash = NULL;
     KvpValue *input_value, *output_value;
 
     g_assert( fixture->frame );
@@ -1191,9 +1185,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
     g_assert( output_value );
     g_assert( input_value != output_value ); /* copied */
     g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); /* old value removed */
-    frame_hash = kvp_frame_get_hash( fixture->frame );
-    g_assert( frame_hash );
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* be sure it was replaced */
     kvp_value_delete( input_value );
 
     g_test_message( "Test when existing path elements are not frames" );
@@ -1202,7 +1193,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
     kvp_frame_set_slot_path_gslist( fixture->frame, input_value, path_list );
     g_assert( kvp_frame_get_slot_path( fixture->frame, "test2", NULL ) == NULL );/* was not added */
     g_assert_cmpint( kvp_value_compare( output_value, kvp_frame_get_slot_path( fixture->frame, "test", NULL ) ), == , 0 ); /* nothing changed */
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* didn't change */
     kvp_value_delete( input_value );
 
     g_test_message( "Test frames are created along the path when needed" );
@@ -1217,7 +1207,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
     g_assert( output_value );
     g_assert( input_value != output_value ); /* copied */
     g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 );
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 2 );
     kvp_value_delete( input_value );
 
     g_slist_free( path_list );
@@ -1226,8 +1215,8 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
 static void
 test_kvp_frame_replace_slot_nc( Fixture *fixture, gconstpointer pData )
 {
-    GHashTable *frame_hash;
-    KvpValue *orig_value, *orig_value2, *copy_value;
+    KvpValue *orig_value, *orig_value2;
+    const char ** keys;
     /* test indirectly static function kvp_frame_replace_slot_nc */
     g_assert( fixture->frame );
     g_assert( kvp_frame_is_empty( fixture->frame ) );
@@ -1236,23 +1225,23 @@ test_kvp_frame_replace_slot_nc( Fixture *fixture, gconstpointer pData )
     orig_value = kvp_value_new_gint64( 2 );
     kvp_frame_set_slot( fixture->frame, "test", orig_value );
     g_assert( !kvp_frame_is_empty( fixture->frame ) );
-    frame_hash = kvp_frame_get_hash( fixture->frame );
-    g_assert( frame_hash );
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 );
-    copy_value = g_hash_table_lookup( frame_hash, "test" );
-    g_assert( orig_value != copy_value );
-    g_assert_cmpint( kvp_value_compare( orig_value, copy_value ), == , 0 );
+    keys = kvp_frame_get_keys( fixture->frame );
+    g_assert( keys );
+    g_assert( !keys[1] );
+    g_assert( orig_value != kvp_frame_get_value( fixture->frame, keys[0] ) );
+    g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value ), ==, 0 );
+    g_free( keys );
 
     g_test_message( "Test when value is replaced" );
     orig_value2 = kvp_value_new_gint64( 5 );
     kvp_frame_set_slot( fixture->frame, "test", orig_value2 );
-    frame_hash = kvp_frame_get_hash( fixture->frame );
-    g_assert( frame_hash );
-    g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 );
-    copy_value = g_hash_table_lookup( frame_hash, "test" );
-    g_assert( orig_value2 != copy_value );
-    g_assert_cmpint( kvp_value_compare( orig_value2, copy_value ), == , 0 );
-    g_assert_cmpint( kvp_value_compare( orig_value, copy_value ), != , 0 );
+    keys = kvp_frame_get_keys( fixture->frame );
+    g_assert( keys );
+    g_assert( !keys[1] );
+    g_assert( orig_value != kvp_frame_get_value( fixture->frame, keys[0] ) );
+    g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value2 ), ==, 0 );
+    g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value ), !=, 0 );
+    g_free( keys );
 
     kvp_value_delete( orig_value );
     kvp_value_delete( orig_value2 );
@@ -1653,6 +1642,33 @@ test_kvp_frame_set_gvalue (Fixture *fixture, gconstpointer pData)
     kvp_frame_delete (n_frame);
 }
 
+static void
+test_kvp_frame_get_keys( void )
+{
+    KvpFrame * frame = kvp_frame_new();
+    const char * key1 = "number one";
+    const char * key2 = "number one/number two";
+    const char * key3 = "number three";
+    const char * val1 = "Value1";
+    const char * val2 = "Value2";
+    const char * val3 = "Value3";
+    unsigned int spot = 0;
+    const char ** keys;
+    kvp_frame_set_string(frame, key1, val1);
+    kvp_frame_set_string(frame, key2, val2);
+    kvp_frame_set_string(frame, key3, val3);
+    keys = kvp_frame_get_keys(frame);
+
+    g_assert(keys);
+    g_assert(keys[spot]);
+    g_assert(strcmp(keys[spot++], val1));
+    g_assert(keys[spot]);
+    g_assert(strcmp(keys[spot++], val3));
+    g_assert(!keys[spot]);
+
+    g_free(keys);
+    kvp_frame_delete(frame);
+}
 
 void
 test_suite_kvp_frame( void )
@@ -1675,6 +1691,7 @@ test_suite_kvp_frame( void )
     GNC_TEST_ADD( suitename, "kvp frame set slot path", Fixture, NULL, setup, test_kvp_frame_set_slot_path, teardown );
     GNC_TEST_ADD( suitename, "kvp frame set slot path gslist", Fixture, NULL, setup, test_kvp_frame_set_slot_path_gslist, teardown );
     GNC_TEST_ADD( suitename, "kvp frame replace slot nc", Fixture, NULL, setup, test_kvp_frame_replace_slot_nc, teardown );
+    GNC_TEST_ADD_FUNC( suitename, "kvp frame get keys", test_kvp_frame_get_keys );
     GNC_TEST_ADD( suitename, "get trailer make", Fixture, NULL, setup_static, test_get_trailer_make, teardown_static );
     GNC_TEST_ADD( suitename, "kvp value glist to string", Fixture, NULL, setup_static, test_kvp_value_glist_to_string, teardown_static );
     GNC_TEST_ADD( suitename, "get or make", Fixture, NULL, setup_static, test_get_or_make, teardown_static );



Summary of changes:
 src/backend/xml/sixtp-dom-generators.c |  37 ++--
 src/engine/Split.c                     |   2 +-
 src/engine/Transaction.c               |   8 +-
 src/engine/gnc-lot.c                   |   4 +-
 src/libqof/qof/kvp-value.cpp           |  19 +-
 src/libqof/qof/kvp-value.hpp           |  11 +-
 src/libqof/qof/kvp_frame-p.hpp         |  14 --
 src/libqof/qof/kvp_frame.cpp           | 358 +++++++++++++++------------------
 src/libqof/qof/kvp_frame.h             |  20 +-
 src/libqof/qof/kvp_frame.hpp           |  80 ++++++++
 src/libqof/qof/test/test-kvp_frame.c   |  71 ++++---
 11 files changed, 336 insertions(+), 288 deletions(-)
 delete mode 100644 src/libqof/qof/kvp_frame-p.hpp
 create mode 100644 src/libqof/qof/kvp_frame.hpp



More information about the gnucash-changes mailing list