FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
C++
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
June 01, 2006

Supporting Custom C++ Types

(Page 3 of 6)

The Problem: Supporting Additional Types

SOCI provides explicit template specializations of UseType and IntoType for the Standard C++ types short, int, char, unsigned long, double, char*, std::string, and std::tm.

Of course, in practice, users will often prefer to work with other types, such as nonstandard String, Money, or Date classes. For example, if your development team has adopted the Boost libraries, you might like to use boost::gregorian::date (boost.org/doc/html/date_time.html) directly with SOCI, as in Listing Three.

 

// Requires support for boost::gregorian::date to be added, either // manually as in Listing Four, or via TypeConversion<T> as in Listing Six. #include "listing6.h" #include <soci.h> #include <boost/date_time/gregorian/gregorian.hpp> #include <iostream>

using namespace SOCI; using boost::gregorian::date; using boost::gregorian::months_of_year;

int main() { try { Session sql("oracle", "service=gen1 user=scott " "password=tiger"); sql << "create table person(id number, name varchar2(50)," " birthday date)"; int id(100); std::string name("Bjarne"); date bday(2001, boost::gregorian::Jan, 1); sql << "insert into person values(:id, :name, :birthday)", use(id), use(name), use(bday); std::tm bd; sql<<"select birthday from person", into(bd); date bday2(boost::gregorian::not_a_date_time); sql << "select birthday from person", into(bday2); assert(bday2 == bday); std::cout<<name<<"'s birthday is "<<bday2<<std::endl;

sql << "drop table person"; } catch(std::exception& e) { std::cout<<e.what()<<std::endl; } } #ifndef LISTING6_H #define LISTING6_H

Listing Three

SOCI could of course be made to support boost::gregorian::date "out of the box," but that wouldn't solve the larger problem. One of the realities of C++ is that, for better or worse, there are tens—if not hundreds—of String, Numeric, and Date classes in widespread use.

Since the SOCI implementation is driven by template specialization, there is a natural way you could add support for your preferred custom type. For example, to add support for boost::gregorian::date, you could provide explicit specializations for UseType<date> and IntoType<date>, as in Listing Four. Figure 2 presents the corresponding UML class diagram.

#include <boost/date_time/gregorian/gregorian.hpp>
using boost::gregorian::months_of_year;
using boost::gregorian::date;

template <> class IntoType<date> : public IntoType<std::tm> { public: IntoType(std::time_t &t) : IntoType<std::tm>(tm_), t_(t) {}

virtual void postFetch(bool gotData, bool calledFromFetch) { IntoType<std::tm>::postFetch(gotData, calledFromFetch); if (gotData) { std::tm* t = static_cast<std::tm*>(data_); value_ = date(t->tm_year + 1900, static_cast<months_of_year>(t.tm_mon + 1), t->tm_mday); } } private: date& value_; };

template <> class UseType<std::time_t> : public UseType<std::tm> { public: UseType(std::time_t &t, std::string const &name = std::string()) : UseType<std::tm>(tm_, name), t_(t) {}

virtual void preUse() { std::tm* t = static_cast<std::tm*>(data_); t->tm_isdst = -1; t->tm_year = value_.year() - 1900; t->tm_mon = value_.month() - 1; t->tm_mday = value_.day(); t->tm_hour = 0; t->tm_min = 0; t->tm_sec = 0; std::mktime(t);

UseType<std::tm>::preUse(); }

virtual void postUse() { UseType<std::tm>::postUse();

std::tm* t = static_cast<std::tm*>(data_); value_ = date(t->tm_year + 1900, static_cast<months_of_year>(t->tm_mon + 1), t->tm_mday); }

private: date& value_; };

Listing Four

Figure 2: Manually adding support for custom types.

In these example specializations, I've chosen to leverage the existing database access support for std::tm by having UseType<date> inherit from UseType<std::tm> and IntoType<date> inherit from IntoType<std::tm>.

A member variable value_ of type date holds a reference to the parameter, which is passed to into() and use(). In the case of IntoType<date>, the value_ data member is populated inside postFetch(), which is called by the framework after the database-related code is done executing.

Similarly, for UseType<date>, the value_ member is copied into StandardIntoType::data_ in preUse() before any database-related code executes. Because the parameter passed to the use() function can also be written to when used in conjunction with stored procedures, the value_ data member is also populated inside postUse();.

Previous Page | 1 Supporting Custom C++ Types | 2 SOCI Implementation | 3 The Problem: Supporting Additional Types | 4 Traits & TypeConversion<T> | 5 Pass-Through Version of TypeConversion | 6 Aggregate Types and Object-Relational Mapping Next Page
TOP 5 ARTICLES
No Top Articles.



MICROSITES
FEATURED TOPIC

ADDITIONAL TOPICS

INFO-LINK