(I don't know why but my mail got mangled while going through the intertubes.
I'll try to resend. Hopefully this time it comes through ok.) On Friday 12 February 2016 17:15:41 John Ralls wrote: > > On Feb 12, 2016, at 1:09 PM, Geert Janssens > I think that what you really want to do is > > template <typename T> class GncTransProperty { > public: > GncTransProperty (T x, GncTransPropType p) : value{x}, property{p} > {}; T get_value() const { return value; } > GncTransPropType get_property() const { return property; } > > private: > T value; > GncTransPropType property; > } > > That does everything you want in 10 lines and avoids the evil void*. > Even better (at least until Transaction is converted to C++) would be > to expand its property list to include all of the things that the > importer will set. Then you can do even more, and get rid of the > second switch statement: > > template <typename T> class GncTransProperty { > public: > GncTransProperty (T x, const char* p) : value{x}, property{p} {}; > void set_trans_property (Transaction *txn) const { > GValue val = G_VALUE_INIT; > g_value_init_from_instance (&val, G_POINTER(value)); > g_object_set_property (G_OBJECT (txn), property, &val); > } > > private: > T value; > char *property; > } > > You'll have to specialize the set_txn_property implementation for > string, time64, and probably gnc_numeric since they're not GObjects. > The specialization code looks like: > > template <> class GncTransProperty <std::string>{ > public: > GncTransProperty (std::string& x, const char* p) : value{x}, > property{p} {}; void set_trans_property (Transaction *txn) const { > GValue val = G_VALUE_INIT; > g_value_set_string (&val, value.c_str()); > g_object_set_property (G_OBJECT (txn), property, &val); > } > > private: > std::string value; > char* property; > } > > With that and the changes in Transaction.c, the loop in > GncTransPropertyList::to_trans() can be reduced to > std::for_each(this->begin(), this->end(), [](auto > elem){elem.set_trans_property();}); > > BTW, except for the lambda and the curly-bracket initializers that's > all C++98. > > Note that replacing get_type and get_value with set_trans_property > would work with a class hierarchy, too, because it gets rid of the > return-type problem with get_value. The template version runs > slightly faster because the functions aren't called by vtable. The > template version is also more flexible: The base template can handle > any GObject type without specialization. It should also be possible > once Transaction is changed to C++ to create a more type safe (never > mind faster) way to handle properties to get rid of the > specializations too because we should be able to say > txn->set(property, value). > > Regards, > John Ralls Thanks for the detailed explanation John. It took me several readings to grok it. I certainly like the idea of a set_trans_property to avoid different return types. I had already attempted to use a template class instead, but ran into several issues (partly due my long hiatus in c++ coding). I'm redoing it now based on your suggestions. Current code is here: https://github.com/gjanssens/gnucash/blob/cpp/src/import-export/csv-imp/gnc-trans-props.cpp[1] and https://github.com/gjanssens/gnucash/blob/cpp/src/import-export/csv-imp/gnc-trans-props.hpp[2] I'm still having a couple of issues: 1. The first is mainly lack of knowledge. I note you write a constructor like this: GncTransProperty (T x, const char* p) : value{x}, property{p} {}; I'm not familiar with the ": value{x}, property{p}" notation. Is that a specific C++ notation to initialize private members ? Any caveats with it ? (I didn't come across an explanation in Meyers book so far, so I assume it's standard C++). Does it have a name I can google ? 2. I also note you are using "T x" in the constructor, suggesting the GncTransProperty is constructed from the properly typed value. However the importer so far only has a bunch of strings to process. The idea was exactly to let the GncTransproperty instances handle type conversion from the generic string to the proper internal type for later use. In this way that knowledge is encapsulated in the object that needs to know. If I forgo this, the calling code has to do the conversion, breaking the encapsulation and in fact removing much of the motivation to create the GncTransProperty class in the first place. So I have for now kept the more generic constructor GncTransProperty (std::string val, GncTransPropType p) This required specializations for each subtype of GncTransProperty we care about. 3. Just so you can follow I'll also note that I can't do this: template <typename T> class GncTransProperty ... std::vector<GncTransProperty> vctp; Based on the following StackOverflow topic, http://stackoverflow.com/questions/16527673/c-one-stdvector-containing-template-class-of-multiple-types I have hence chosen to write a pure virtual interface class GncTransProperty which is the base class of the GncTransPropImpl template class. With this I can define std::vector<std::unique_ptr<GncTransProperty>> vctp which is the base class of GncTransPropertyList and get on with my life. 4. Back to the real issues: if I would use g_object_set to set the date_posted for a transaction it will call different functionality inside the Transaction code than the importer is doing currently. g_object_set will eventually call xaccTransSetDatePostedTS where the importer has been calling xaccTransSetDatePostedSecsNormalized. The latter also sets a KVP (with the gdate only) where the former doesn't. I'm not sure if we really need that additional KVP, but don't like to pull too many strings at once either. _______________________________________________ gnucash-devel mailing list gnucash-devel@gnucash.org https://lists.gnucash.org/mailman/listinfo/gnucash-devel