dtl


Binding Non-primitive Types

>Question:

>1) We like to store non-primitive types in the database like

>complex-numbers. We have been sniffing around in the sourcecode of DTL

>(bind_basics, BoundsIO, etc) how to extend DTL, but there seem to be

>many places where we have to add some code. Do you have some kind of

>'manual' or guideline which routines we should extend/add to DTL in

>order to support 'foreign' types?



Answer:

There are two ways to do this.  The first method is to simply create

a binding operator for your non-primitive type.  The second method

is to add appropriate constructors and cast operators to

the class that uses the non-primitive type.



Method 1

Suppose we have a non primitive type called "cplx" used to store complex numbers. Suppose also that we have a simple table called multiplex that holds some complex numbers: /* create table multiplex ( AC1_re INTEGER NOT NULL, AC1_im INTEGER NOT NULL, AC2_re INTEGER NOT NULL, AC2_im INTEGER NOT NULL); insert into multiplex(AC1_re, AC1_im, AC2_re, AC2_im) VALUES(1, 2, 3,4); */ struct cplx { int re, im; }; ostream& operator << (ostream &o, const cplx &c) { o << "(" << c.re << ", " << c.im << ")"; return o; } // Here is the key step! // Override the binding operator for the cplex // class so that it will bind the individual // members in this class as needed. BoundIO operator >> (BoundIO &b, cplx &c) { BoundIOs *pCols = b.GetBoundIOsPtr(); tstring root_name = b.GetName(); // Remove base column. pCols->EraseColumn(root_name); // Bind individual columns from root_name. // If you don't have a naming convention you can just // pass in desired field names as a comma delimited list and // use vector<tstring> fields = ParseCommaDelimitedList(root_name) (*pCols)[root_name + "_re"] >> c.re; return (*pCols)[root_name + "_im"] >> c.im; } struct multiplex { cplx AC1, AC2; }; ostream& operator << (ostream &o, const multiplex &mp) { o << mp.AC1 << ", " << mp.AC2; return o; } BEGIN_DTL_NAMESPACE template<> class DefaultBCA<multiplex> { public: void operator()(BoundIOs &cols, multiplex &row) { // N.B.!! These binding operators generate two columns each as per // our specialization of the binding operator for cplx cols["AC1"] >> row.AC1; cols["AC2"] >> row.AC2; } }; END_DTL_NAMESPACE void test_non_primitive() { DBView<multiplex> view("multiplex"); copy(view.begin(), view.end(), ostream_iterator<multiplex>(cout, "\n")); } In fact, you can get even more fancy than this and define default validation methods for your user defined class that will automatically be called when it is read from or written to the database. For an example of this see the user classes written by Paul Harris in lib/nullable.h, lib/dtl_enum.h and lib/dtl_set.h which define support for types that can hold a null value and the mapping of an enum type directly to a database field.

Method 2

// Example of mapping a user defined type to a database. // In this case the database holds complex numbers as two fields, // but we want to map them into a single "complex" type in our class. // We can hanlde this by defining appropriate constructors and // cast operators for the class. // Define a struct and DBView to hold complex numbers from the table "complex_value" DTL_TABLE2(complex_value, int, real, int, imaginary ); // The above MACRO defines // struct complex_value_row {int real; int imaginary;} // DBView<complex_value_row> complex_value_view; // For our final class we want to represent items in the object/class // differently than they are held in the database so we define our class as class cplx_value { complex c; public: // construct from a database row format for read cplx_value(const complex_value_row &r) : c(r.real, r.imaginary) {} // convert to a database row format for write operator complex_value_row() {complex_value_row r; r.real = c.re(); r.imaginary = c.im(); return r;} } // This cleanly separates the database representation from the class representation, but we can still read/write quite transparently // to or from this class. e.g. void read_function() { // read from DB vector<cplx_value> values; // copy / read just as we normally would, the only difference is that the // iterator holds an representation for the database rows since they // don't exactly match the structure we want for our class copy(complex_value_view.begin(), complex_value_view.end(), back_inserter(values)); }

[DTL Home]

Copyright © 2002, Michael Gradman and Corwin Joy.

Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Corwin Joy and Michael Gradman make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

SourceForge Logo

This site written using the ORB. [The ORB]