Skip to main content
Code Review

Return to Question

replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

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

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

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

Tweeted twitter.com/#!/StackCodeReview/status/614867671620939776
added 32 characters in body
Source Link
nhgrif
  • 25.4k
  • 3
  • 64
  • 129
 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";
 }
 );
 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";
 }
 );
 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";
 }
 );
 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";
 }
 );
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341

C++ SQL wrapper/Statement

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";
 }
 );
}
default

AltStyle によって変換されたページ (->オリジナル) /