Project

General

Profile

« Previous | Next » 

Revision 143a60e7

Added by David Sorber over 9 years ago

After much contemplation I realized that my previous approach to solving this execute query template function problem was more wrong than right. Several days of staring at and reading about variadic templates later I figured out the correct way to accomplish my goal.

View differences:

software/sqlite_poc/DBManager.cc
#include "DBManager.h"
// Helper function for int64_t type
void get_value(int64_t& val, int idx, sqlite3_stmt* stmt)
{
if (sqlite3_column_type(stmt, idx) != SQLITE_INTEGER)
{
assert(false);
}
val = sqlite3_column_int64(stmt, idx);
}
// Helper function for double type
void get_value(double& val, int idx, sqlite3_stmt* stmt)
{
if (sqlite3_column_type(stmt, idx) != SQLITE_FLOAT)
{
assert(false);
}
val = sqlite3_column_double(stmt, idx);
}
// Helper function for string type
void get_value(std::string& val, int idx, sqlite3_stmt* stmt)
{
if (sqlite3_column_type(stmt, idx) == SQLITE_TEXT)
{
val.assign(reinterpret_cast<const char*>(sqlite3_column_text(stmt, idx)));
}
else if (sqlite3_column_type(stmt, idx) == SQLITE_NULL)
{
val.assign("");
}
else
{
assert(false);
}
}
DBManager::DBManager(const std::string& db_path)
{
int rc = sqlite3_open(db_path.c_str(), &this->db);
if (rc)
{
software/sqlite_poc/DBManager.h
#include <cassert>
#include <string>
#include <tuple>
#include <type_traits>
#include <vector>
#include <sqlite3.h>
#include <sqlite3.h>
// Helper function prototypes
void get_value(int64_t& val, int idx, sqlite3_stmt* stmt);
void get_value(double& val, int idx, sqlite3_stmt* stmt);
void get_value(std::string& val, int idx, sqlite3_stmt* stmt);
template<size_t I>
struct builder_impl
{
template<typename T, typename G>
static void set_value(T& tup, size_t idx, G& val)
template<typename T>
static void set_values_helper(T& tup, sqlite3_stmt* stmt)
{
if (idx == I - 1)
{
std::get<I - 1>(tup) = val;
}
else
{
builder_impl<I - 1>::set_value(tup, idx, val);
}
// Assign the "current" value
get_value(std::get<I - 1>(tup), I - 1, stmt);
// Recurse to the next value in the tuple
builder_impl<I - 1>::set_values_helper(tup, stmt);
}
};
template <>
template<>
struct builder_impl<0>
{
template<typename T, typename G>
static void set_value(T& tup, size_t idx, G& val)
{
assert(false);
template<typename T>
static void set_values_helper(T& tup, sqlite3_stmt* stmt)
{
// Stop recursing
return;
}
};
template <typename G, typename... Ts>
void set_value_at(std::tuple<Ts...>& tup, size_t idx, G const& val)
template <typename... Ts>
void set_values(std::tuple<Ts...>& tup, sqlite3_stmt* stmt)
{
builder_impl<sizeof...(Ts)>::set_value(tup, idx, val);
}
template <typename G, typename... Ts>
void set_value_at(std::tuple<Ts...>& tup, size_t idx, G& val)
{
builder_impl<sizeof...(Ts)>::set_value(tup, idx, val);
builder_impl<sizeof...(Ts)>::set_values_helper(tup, stmt);
}
class DBManager
{
public:
// Constructor
DBManager(const std::string& db_path);
// Destructor
~DBManager();
// execute query
// Execute query template function
template<typename... T>
std::vector<std::tuple<T...>*>* execute_query(
const std::string& sql_statement,
int& return_code)
{
sqlite3_stmt* sqlite_statement;
// Prepare the sqlite statement
return_code = sqlite3_prepare_v2(db, sql_statement.c_str(), -1,
return_code = sqlite3_prepare_v2(db, sql_statement.c_str(), -1,
&sqlite_statement, nullptr);
if (return_code != SQLITE_OK)
{
return nullptr;
}
auto output_vector = new std::vector<std::tuple<T...>*>();
}
auto output_vector = new std::vector<std::tuple<T...>*>();
// Iterate until we've collected all the output
int step_return = 0;
while ((step_return = sqlite3_step(sqlite_statement)) != SQLITE_DONE)
{
// Create a new result row to be populated below
// Create a new result row
auto result_row = new std::tuple<T...>();
// Iterate over the columns in the row
int num_cols = sqlite3_data_count(sqlite_statement);
for (int col_idx = 0; col_idx < num_cols; col_idx++)
{
switch(sqlite3_column_type(sqlite_statement, col_idx))
{
case SQLITE_INTEGER:
set_value_at(*result_row, col_idx,
sqlite3_column_int64(sqlite_statement,
col_idx));
break;
case SQLITE_FLOAT:
set_value_at(*result_row, col_idx,
sqlite3_column_double(sqlite_statement,
col_idx));
break;
case SQLITE_TEXT:
//~ set_value_at(*result_row, col_idx,
//~ std::string(reinterpret_cast<const char*>(sqlite3_column_text(sqlite_statement, col_idx))));
break;
case SQLITE_BLOB:
break;
case SQLITE_NULL:
//~ set_value_at(*result_row, col_idx, std::string(""));
break;
}
}
// Now populate the result row using some neato variadic
// template function magic NOTE: a small amount of black magic
// is in use here; animal sacrifices not necessary
set_values(*result_row, sqlite_statement);
// Add the result row to our vector of output rows
output_vector->push_back(result_row);
}
return output_vector;
}
private:
sqlite3 *db;
};
software/sqlite_poc/Makefile
main: $(OBJECTS)
g++ $(CXXFLAGS) $(OBJECTS) -lsqlite3 -o poc_test
%.o : %.c
%.o : %.c %.h
g++ $(CXXFLAGS) -c $<
clean:
software/sqlite_poc/main.cc
// Make a DBManager instance which will connect to our test DB automagically
DBManager db_mgr("database.db");
// Make a query
std::string query("SELECT Number, Float FROM sample;");
std::string query("SELECT Number, Words, Float FROM sample;");
// Execute the query making sure the template params match up with the
// query params
int rc = 0;
auto results = db_mgr.execute_query<int64_t, double>(query, rc);
auto results = db_mgr.execute_query<int64_t, std::string, double>(query, rc);
std::cout << "Return code: " << rc << std::endl;
// If we returned successfully, print out the result rows

Also available in: Unified diff