commit 143a60e76578ec1b35ac851815479377a362d486
Author: David Sorber <david.sorber@gmail.com>
Date:   Tue Jan 24 10:47:33 2017 -0500

    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.

diff --git a/software/sqlite_poc/DBManager.cc b/software/sqlite_poc/DBManager.cc
index 188c82c..86da533 100644
--- a/software/sqlite_poc/DBManager.cc
+++ b/software/sqlite_poc/DBManager.cc
@@ -2,9 +2,48 @@
 
 #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)
     {
diff --git a/software/sqlite_poc/DBManager.h b/software/sqlite_poc/DBManager.h
index b9c699a..1649bb9 100644
--- a/software/sqlite_poc/DBManager.h
+++ b/software/sqlite_poc/DBManager.h
@@ -1,127 +1,96 @@
-
 #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;
 };
diff --git a/software/sqlite_poc/Makefile b/software/sqlite_poc/Makefile
index 38d7059..86e9cab 100644
--- a/software/sqlite_poc/Makefile
+++ b/software/sqlite_poc/Makefile
@@ -7,7 +7,7 @@ all: main
 main: $(OBJECTS)
 	g++ $(CXXFLAGS) $(OBJECTS) -lsqlite3 -o poc_test
 
-%.o : %.c
+%.o : %.c %.h
 	g++ $(CXXFLAGS) -c $<
 
 clean:
diff --git a/software/sqlite_poc/main.cc b/software/sqlite_poc/main.cc
index 15a6edf..45f5326 100644
--- a/software/sqlite_poc/main.cc
+++ b/software/sqlite_poc/main.cc
@@ -39,14 +39,13 @@ int main(int argc, char** argv)
     // 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
