
More thoughts after looking at the code: You use std::string for exception messages, names of fields, etc. I think this is a mistake. Many databases return internationalized wide error messages and use wide strings for table and column names. Also, you will ideally want your library to return internationalized error messages. In DTL we ended up converting halfway through to support this - much better to get it right in the first place. variant: I'm don't think the variant type will exactly fit what you want here. I like the discriminated union which I would consider to be a better choice than boost::any since row allocation efficiency is an issue. One major difference, though, is that you're going to want "sticky types". The idea here is that once a field is created with a particular type (by reading what type it should be from the database) it needs to stay that type. When a user assigns say a string into a date field the field must not convert into a string type but instead must cast the field to a date and throw an error if that type of conversion is not legal. Ideally, this check gets done at compile time. This "sticky type" logic is what we did in DTL when we did a variant field. Also, I see that you are binding std::string as the type for character data in your variant type. Be aware that this leads you down the slippery slope of having to support arbitrary length strings with the associated problems that I mentioned in my previous post. You're also going to want to bind a boost date_time type (maybe the ptime type), a wstring type, a blob type and possibly a long string type. Adding new types shouldn't be a big deal though so this could easily be skipped for the initial design. BUT, I think you will benefit from including a date type early on since this gives an early example of what I call "complex types", i.e. types that are held in your class but do not map directly to a primitive database type but instead require reading into some kind of intermediate buffer and then translation to give the final type. In the case of dates, the primitive is (I think) an unsigned long but ODBC reads dates as a big {YEAR, MONTH, DAY, HH, MM, SS} type struct which has to be read and then translated. Take a look at what we did here: http://dtemplatelib.sourceforge.net/fmtUserTypes.htm to support the ability of users to write their own binding operators to build up complex field types as needed. Your field_description only has about 1/3 of the information you need to bind a field to a database column. Really, you need three kinds of sections for a binding (maybe in 3 classes) 1. C++ data type. 2. Database data type and related information. You can't just assume this from the C++ type. Sometimes you will need to specify extra information about the SQL type to bind nondefault values for use in things like mapping to larger strings, specifying non-default precision for dates etc. The database datatype will be driver layer specific, i.e. different for ODBC and various native driver layers. 3. Mapping between 1 <--> 2. Can include things like the address of where to put the data, intermediate buffers that may be used to manage the mappings, possibly any conversion policy. Here is an example snippet from DTL where we ended up having all 3 of these things in a single class: void *addr; // absolute address of C++ member data ptrdiff_t offset; // relative offset of this field from base address of its Row SDWORD sqlType; SDWORD cType; SDWORD paramType; // input, output or input/output type of field tstring name; // column name int typeId; // enum for type bool IsPrimitive; // is this a simple POD type, or one where we first need to fetch to an intermediate buffer then initialize such as a date or string type SDWORD size; // SQL size SDWORD bufferLength; // how big a buffer do we need to transfer chunks, this is a member here so that users can override and set it to be large for big blobs CountedPtr<SDWORD> bytesFetched; static const SDWORD lenAtExec; // needed for PutData() int VariantRowIdx; // -1: It isn't a variant row BoundIOs_base *pBoundIOs; // refers to collection of bindings that this object belongs to BoundType bindingType; // column or param??? int colNo; // column number MemPtr strbuf; // buffer needed for use with "complex types"