Warren Young wrote:
> Rick Gutleber wrote:
>> Does it really make sense for allow_transactions() to be part of the
>> policy or should it be a bool option passed in to insertfrom( )?
>>
>> The reason I ask is because it seems silly to have to have two
>> different InsertPolicy objects that differ only in whether
>> transactions are allowed or not.
>
> There are patterns to allow the extra attribute without creating a
> parallel class hierarchy. I'd use traits:
>
> template <class AccessController = Transaction>
> class MyPolicy
> {
> public:
> typedef AccessController access_controller;
> ...
>
> Then you could create a no-op NoTransactions class with the same
> interface as Transaction, so that MyPolicy<NoTransactions> would be
> MyPolicy without transactions. This relieves from insertfrom() the
> burden to care about transactions:
>
> InsertPolicy::access_controller ac(&conn_);
Cool. Templates are definitely still not instinctive for me...
especially the idea of "faking" inheritance of an abstract interface.
It feels like cheating from an OO point of view, although I definitely
see the value. In my pre-template days, I'd always used multiple
inheritance with abstract classes that provided the interface. For
instance, if you have a collection that you want to be sortable, you
inherit from the ISortable class and fill in the methods it uses.
ISortable also included a couple of sort implementations you'd
automatically get once you implemented a couple or three methods like
::SortableCompare( ) and ::SortableSwap( ).
> A bool parameter to insertfrom() defaulting to true would make the
> code more complex.
At first, yes, but then I did this, putting the brains of the method in
another private method:
template <class RowT, class InsertPolicy>
Query& insertfrom(const std::vector<RowT>& con, InsertPolicy& policy,
bool allow_transaction = true) {
if ( allow_transaction ) {
Transaction transaction(*conn_);
if (_insertfrom(con, policy)) {
transaction.commit();
} else {
transaction.rollback();
}
} else {
_insertfrom(con, policy);
}
return *this;
}
But your way is a good idea.
> Based on the last code you posted, it looks like you'd have to add 4
> or so new tests to decide whether to do the transaction stuff. This
> way, you avoid the additional tests. This pays off in both
> understandability and run-time efficiency (the former much more
> important than the latter) because the compiler can optimize away
> NoTransactions entirely.
The trait idea works very well in this context.
>
> This also localizes concerns better: whether to use transactions or
> not *is* part of the insertion policy.
OK. I didn't see it that way, but that's a reasonable view.
> And finally, it does offer some flexibility, at the low low cost of a
> little extra template goo, which gets paid for out of other accounts:
> efficiency, code simplicity, and proper localization of concerns.
Sounds good to me.
The real issues here aren't how to do something, but more what should be
done at a more abstract level, and more to wrap my head around the
philosophies behind MySQL++. Also, it's nice to get some serious input
and feedback from people better versed in the _current_ C++ scene. I've
been using the language very successfully on and off for about 15 years,
but I realize that a lot of the new developments have changed the
landscape considerably. To wit, after spending a few hours catching up
on the C++0x scene (how long until it's called "C++1x"?) I realized how
radically they are changing, well really adding to, the language. In
particular, a piece written by Stroustrup made me understand that in
effect, C++ is becoming a two-tier language (my characterization, not
his). The outer tier is C++ the way it's pretty much always been since
1998, and the inner tier which allows people to make stuff like Boost
more powerful and flexible, and not necessarily stuff you'd use on a
daily basis (unless you are doing low-level tools like STL or Boost).