Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Supporting Custom C++ Types


Traits are powerful techniques with many applications to generic programming in C++. In this article, I use examples from the SOCI database library (soci.sf.net) to show how a simple TypeConversion traits class can be used to create an extensible framework that supports arbitrary user-defined types. The complete source code that implements this framework is available here.

SOCI is a database library with a minimalist syntax that mimics that of embedded SQL. (For more information, see "A Simple Oracle Call Interface," by Maciej Sobczak.) Listing One is a complete program that connects to a database (Oracle, in this case), creates a table, inserts a row, and queries the table.

Listing One
#include <soci.h>
#include <iostream>
#include <string>

using namespace SOCI;
using std::string;

int main()
{
    try
    {
        Session sql("oracle", "service=gen1 user=scott " "password=tiger");
        sql << "create table Person(id number, name varchar2(50))";
        int id(100);
        string name("Bjarne");
        sql << "insert into Person values (:ID, :NAME)", use(id), use(name);
        int id2;
        string name2;
        sql << "select id, name from Person", into(id2), into(name2);
        assert(id2 == 100);
        assert(name2 == "Bjarne");
        std::cout << name2 << " has id "<< id2 << std::endl;
        sql << "drop table person";
    }
    catch(std::exception& e)
    {
        std::cout<<e.what()<<std::endl;
    }
}

To use SOCI to insert (or update) data, you first declare and initialize variables of a type appropriate to the column into which you will be inserting. Then use the free function use() to bind the variables to placeholders in your SQL statement.

Similarly, to use SOCI to select data, you define variables of an appropriate type and pass them by reference to the free function into() to have them populated with the values returned by your select statement.

SOCI Implementation

SOCI supports native types such as int and std::string as parameters for use() and into() functions via template specialization. The framework internally makes use of an inheritance hierarchy, which is by design hidden from users.

The use() function in fact creates an instance of the class template UseType, which is specialized for each supported native type. The class UseType is derived from StandardUseType, which in turn is derived from the abstract base class UseTypeBase.

The into() function works similarly, creating instances of IntoType, which is also specialized for each built-in type. Class IntoType is derived from StandardIntoType, which in turn is derived from the abstract base class IntoTypeBase. Figure 1 presents the relevant UML class diagrams, and Listing Two implements the simplified class definitions.


Figure 1: SOCI internal class hierarchy.

Listing Two
 
class UseTypeBase
{
public:
    virtual void bind(Statement &st, int &position) = 0;
    virtual void preUse() = 0;
    virtual void postUse(bool gotData) = 0;
    virtual void cleanUp() = 0;
};

class StandardUseType : public UseTypeBase
{
public:
    StandardUseType(void *data, eExchangeType type,
                    std::string const &name = std::string());

    ~StandardUseType();
    virtual void bind(Statement &st, int &position);

private:
    virtual void preUse();
    virtual void postUse(bool gotData);
    virtual void cleanUp();

    void *data_;
    eExchangeType type_;
    eIndicator *ind_;
    std::string name_;
    StandardUseTypeBackEnd *backEnd_; //database specific
};

template <>
class UseType<int> : public StandardUseType
{
public:
    UseType(int &i, std::string const &name = std::string())
        : StandardUseType(&i, eXInteger, name) {}
};

template <>
class UseType<std::string> : public StandardUseType
{
public:
    UseType(std::string &s, std::string const &name = std::string())
        : StandardUseType(&s, eXStdString, name) {}
};

template <typename T> details::UseTypePtr use(T &t)
{
    return UseTypePtr(new UseType<T>(t));
}

// similar definitions for UseType<double> etc. not shown

class IntoTypeBase
{
public:
    virtual void define(Statement &st, int &position) = 0;
    virtual void preFetch() = 0;
    virtual void postFetch(bool gotData, bool calledFromFetch) = 0;
    virtual void cleanUp() = 0;
};

class StandardIntoType : public IntoTypeBase
{
public:
    StandardIntoType(void *data, eExchangeType type);
    virtual ~StandardIntoType();

private:
    virtual void define(Statement &st, int &position);
    virtual void preFetch();
    virtual void postFetch(bool gotData, bool calledFromFetch);
    virtual void cleanUp();

    void *data_;
    eExchangeType type_;
    StandardIntoTypeBackEnd *backEnd_;
};

template <> class IntoType<int> : public StandardIntoType
{
public:
    IntoType(int &i) : StandardIntoType(&i, eXInteger) {}
};

template <> class IntoType<std::string> : public StandardIntoType
{
public:
    IntoType(std::string &s) : StandardIntoType(&s, eXStdString) {}
};

template <>
class IntoType<std::tm> : public StandardIntoType
{
public:
    IntoType(std::tm &t) : StandardIntoType(&t, eXStdTm) {}
};

template <typename T> IntoTypePtr into(T &t)
{
    return IntoTypePtr(new IntoType<T>(t));
}

// similar definitions for IntoType<double> etc. not shown

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.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.