Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Part 2: (Part 1)
Working on my SQL project at last.

The concept is easy to use and integrate SQL into C++.

    ThorsSQL::Connection    mysql("mysql://host", "username", "password", "databaseName");
    ThorsSQL::Statement     bigEarnerStat(mysql, 
                                          "SELECT ID, Name, Salary FROM Employee WHERE Salary > % and Age < %"
                                          ThorsAnvil::SQL::Prepare);

    // Bind variables to '%' in statement
    // Then execute the SQL statement.
    // Call function for every row returned.
    bigEarnerStat.execute(Bind(1000000, 32), // parameter bound to % in statement.
        // Function executed for each row returned.
        // Parameters are matched against the SELECT in the statement.
        // A bad type conversion will throw an exception.
        [](u64 id, std::string const& name, int salary){
            std::cout << name << " is a fat cat earning $" << salary/100 << "." << salary%100 << "\n";
        }
    );

Statement:
Is the generic statement object.

StatementProxy:
Holds DB specific code used by the statement.

Cursor:
Private class.

Used to iterate over each returned row from the DB.

BindArgs and Bind:
Way of grouping arguments that need to be bound.
I tried to make this code work without this type but that meant I would need to put the function first and the bind arguments last in the execute() call (because of the way template var arguments are expanded).

Statement/Cursor is where we get the interesting meta programming happening. Have some fun and I hope you find it interesting.

Note: If you want to try compiling the code I suggest you check it out of the git repo and compile using the instructions there. But Saying that you can potentially compile it using only the source here just add a main().

Statement.h

#ifndef THORS_ANVIL_SQL_STATEMENT_H
#define THORS_ANVIL_SQL_STATEMENT_H

#include "SQLUtil.h"
#include <memory>
#include <string>

namespace ThorsAnvil
{
    namespace SQL
    {

class Connection;
class StatementProxy;
class Cursor;
template<typename... Args>
class BindArgs;

class Statement
{
    private:
        std::unique_ptr<StatementProxy>    statementProxy;
    public:
        Statement(Connection& connect, std::string const& selectStatement, StatementType = ThorsAnvil::SQL::Prepare);
        template<typename F, typename... R>
        void execute(BindArgs<R...> const& binds, F cb);
};

class StatementProxy
{
    public:
        virtual ~StatementProxy()
        {}

        virtual void   bind(char)                           = 0;
        virtual void   bind(signed char)                    = 0;
        virtual void   bind(signed short)                   = 0;
        virtual void   bind(signed int)                     = 0;
        virtual void   bind(signed long)                    = 0;
        virtual void   bind(signed long long)               = 0;
        virtual void   bind(unsigned char)                  = 0;
        virtual void   bind(unsigned short)                 = 0;
        virtual void   bind(unsigned int)                   = 0;
        virtual void   bind(unsigned long)                  = 0;
        virtual void   bind(unsigned long long)             = 0;

        virtual void   bind(float)                          = 0;
        virtual void   bind(double)                         = 0;
        virtual void   bind(long double)                    = 0;

        virtual void   bind(std::string const&)             = 0;

        // -----

        Cursor execute();
        virtual void doExecute()                            = 0;
        virtual bool more()                                 = 0;

        virtual void   retrieve(char&)                      = 0;
        virtual void   retrieve(signed char&)               = 0;
        virtual void   retrieve(signed short&)              = 0;
        virtual void   retrieve(signed int&)                = 0;
        virtual void   retrieve(signed long&)               = 0;
        virtual void   retrieve(signed long long&)          = 0;
        virtual void   retrieve(unsigned char&)             = 0;
        virtual void   retrieve(unsigned short&)            = 0;
        virtual void   retrieve(unsigned int&)              = 0;
        virtual void   retrieve(unsigned long&)             = 0;
        virtual void   retrieve(unsigned long long&)        = 0;

        virtual void   retrieve(float&)                     = 0;
        virtual void   retrieve(double&)                    = 0;
        virtual void   retrieve(long double&)               = 0;

        virtual void   retrieve(std::string&)               = 0;
};

class Cursor
{
    StatementProxy&     statementProxy;
    public:
        explicit operator bool();
        Cursor(StatementProxy& statementProxy);

        template<typename F>
        void activate(F cb);

        template<typename R, typename... Args>
        void activate_(std::function<R(Args...)> cb);

        template<typename F, typename A, std::size_t... ids>
        void activateWithArgs(F func, A& arguments, std::index_sequence<ids...>);

        template<typename V>
        int retrieve(V& value);
};

template<typename... Args>
class BindArgs
{
    std::tuple<std::reference_wrapper<Args>...>     arguments;
    public:
        BindArgs(Args... args)
            : arguments(args...)
        {}

        void bindTo(StatementProxy& statementProxy) const;
    private:
        template<std::size_t... ids>
        void bindArgsTo(StatementProxy& statementProxy, std::index_sequence<ids...>const&) const;
        template<std::size_t id>
        int bindTheArgument(StatementProxy& statementProxy) const;
};

// -- Bindings
template<typename... Args>
BindArgs<Args...> Bind(Args... args)
{
    return BindArgs<Args...>(args...);
}

// -- Statement

// Classes need to get the type of a lambda to
// Coerce the correct function in Cursor to be
// called.
namespace Detail
{
    template<typename T>
    struct FunctionTraits
        : public FunctionTraits<decltype(&T::operator())>
    {};

    template <typename ClassType, typename ReturnType, typename... Args>
    struct FunctionTraits<ReturnType(ClassType::*)(Args...) const>
    {
        typedef std::function<ReturnType(Args...)>  FunctionType;
    };
}

template<typename F, typename... R>
inline void Statement::execute(BindArgs<R...> const& binds, F cb)
{
    binds.bindTo(*statementProxy);
    Cursor cursor = statementProxy->execute();
    while(cursor) {
        typedef typename Detail::FunctionTraits<decltype(cb)>::FunctionType   CBTraits;
        cursor.activate<CBTraits>(cb);
    }
}

    }
}

#endif

Statement.tpp

namespace ThorsAnvil
{
    namespace SQL
    {

template<typename F>
inline void Cursor::activate(F cb)
{
    activate_(cb);
}

template<typename R, typename... Args>
inline void Cursor::activate_(std::function<R(Args...)> cb)
{
    std::tuple<typename std::decay<Args>::type...>    arguments;
    activateWithArgs(cb, arguments, std::make_index_sequence<sizeof...(Args)>());
}

template<typename F, typename A, std::size_t... ids>
inline void Cursor::activateWithArgs(F cb, A& arguments, std::index_sequence<ids...>)
{
    auto list = {retrieve(std::get<ids>(arguments))...};
    [&list](){}();
    cb(std::get<ids>(arguments)...);
}

template<typename V>
inline int Cursor::retrieve(V& value)
{
    statementProxy.retrieve(value);
    return 1;
}

template<typename... R>
inline void BindArgs<R...>::bindTo(StatementProxy& statementProxy) const
{
    bindArgsTo(statementProxy, std::make_index_sequence<sizeof...(R)>());
}

template<typename... R>
template<std::size_t... ids>
inline void BindArgs<R...>::bindArgsTo(StatementProxy& statementProxy, std::index_sequence<ids...>const&) const
{
    auto list = {bindTheArgument<ids>(statementProxy)...};
    [&list](){}();
}

template<typename... R>
template<std::size_t id>
inline int BindArgs<R...>::bindTheArgument(StatementProxy& statementProxy) const
{
    statementProxy.bind(std::get<id>(arguments));
    return id;
}

    }
}

Statement.cpp

#include "Statement.h"
#include "Connection.h"

using namespace ThorsAnvil::SQL;

        std::unique_ptr<StatementProxy>    statementProxy;
Statement::Statement(Connection& connect, std::string const& selectStatement, StatementType type)
    : statementProxy(connect.createStatementProxy(selectStatement, type))
{}

// -- StatementProxy

Cursor StatementProxy::execute()
{
    doExecute();
    return Cursor(*this);
}

// -- Cursor
inline Cursor::Cursor(StatementProxy& statementProxy)
    : statementProxy(statementProxy)
{}

inline Cursor::operator bool()
{
    return statementProxy.more();
}

test/StatementTest.cpp

#include "Statement.h"
#include "Connection.h"
#include <iostream>
#include <gtest/gtest.h>

TEST(StatementTest, call)
{
    using ThorsAnvil::SQL::Connection;
    using ThorsAnvil::SQL::Statement;
    using ThorsAnvil::SQL::Bind;

    Connection      connection("mysql://127.0.0.1:69", "root", "testPassword", "test");
    Statement       statement(connection, "Plop");

    statement.execute(Bind(15), [](int id, std::string const& name, short age, char sex, double height)
                                {
                                    std::cout << "Worked " << id << " : " << name << ": " << age << " : " << height << "\n";
                                }
    );
}
share|improve this question

1 Answer 1

[&list](){}();

No. I don't care why you have it -- never put something like this in real code. At the very least, add a comment explaining what it's doing there.

(I'd review more thoroughly but I don't C++ much; I feel like this deserves its own answer though.)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.