[Tuple] Help getting started with Tuple-based meta-programming
Hi All. I outline below code I keep writing which I'd like to replace with a sql_get_rows template function which would use the meta-type information bundled in the tuple declaration to automatically make as many calls to the get<T> function based on the tuple length, and "pack" the result of these calls into a tuple instance added to a container (vector in this case, but if it could be generalized to any container, all the better, although I'd be happy just with vector). I have experience with basic template stuff, and a little less basic stuff like CRTP, enable_if, and the like, but meta-programming is still new to me, which is why I'm seeking advice from experts to find out whether the below is : 1) possible, 2) would perform ok, and 3) where I should start looking to see examples doing this particular "tuple-enrolling" or perhaps even pseudo code outlining how one would go about implementing this, provided answers to (1) and (2) are positive of course. I suspect mpl and phoenix would need to be used, from the reading I've done, but the path to follow is quite unclear to me still. Any help in this matter would be appreciated. Thanks, --DD // Context: retrieving all the rows (result set) from a given SQL query. // pseudo code of boilerplate code I keep repeating right now struct { int id; std::string name; } Row; void doit() { ResultSet rset = sql_exec("select id, name from t"); std::vector<Row> rows; while (rset.hasMoreRows()) { Row row; row.id = rset.get<int>(0); row.name = rset.get<const char*>(1); rows.push_back(row); } // process rows } // Code I'd like to write instead void doit() { typedef boost::tuple<int, std::string> Row; std::vector<Row> rows; sql_get_rows("select id, name from t", rows); // optional arg to get only first N elements // process rows } PS: returning the vector would be better, but because of the copy involved I guess I must pass it as a non-const ref instead, to fill it up. Will the new move semantic from the upcoming standard allow a function to return the vector without the copy? Just curious.
AMDG Dominique Devienne wrote:
// Context: retrieving all the rows (result set) from a given SQL query. // pseudo code of boilerplate code I keep repeating right now struct { int id; std::string name; } Row; void doit() { ResultSet rset = sql_exec("select id, name from t"); std::vector<Row> rows; while (rset.hasMoreRows()) { Row row; row.id = rset.get<int>(0); row.name = rset.get<const char*>(1); rows.push_back(row); } // process rows }
// Code I'd like to write instead void doit() { typedef boost::tuple<int, std::string> Row; std::vector<Row> rows; sql_get_rows("select id, name from t", rows); // optional arg to get only first N elements // process rows }
PS: returning the vector would be better, but because of the copy involved I guess I must pass it as a non-const ref instead, to fill it up. Will the new move semantic from the upcoming standard allow a function to return the vector without the copy? Just curious
In this case, I think Boost.Fusion can help (warning untested). #include <boost/fusion/include/for_each.hpp> #include <boost/fusion/include/boost_tuple.hpp> struct get_t { ResultSet& r; int& n; template<class T> void operator()(T& t) const { t = r.get<T>(n++); } }; template<Row> void get_rows(const char* query, std::vector<Row>& out) { ResultSet rset = sql_exec(query); while(rset.hasMoreRows()) { Row row; int n = 0; get_t getter = { rset, n }; boost::fusion::for_each(row, getter); out.push_back(row); } } In Christ, Steven Watanabe
On Fri, Mar 20, 2009 at 3:01 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
In this case, I think Boost.Fusion can help (warning untested).
#include <boost/fusion/include/for_each.hpp> #include <boost/fusion/include/boost_tuple.hpp>
struct get_t { ResultSet& r; int& n; template<class T> void operator()(T& t) const { t = r.get<T>(n++); } };
template<Row> void get_rows(const char* query, std::vector<Row>& out) { ResultSet rset = sql_exec(query); while(rset.hasMoreRows()) { Row row; int n = 0; get_t getter = { rset, n }; boost::fusion::for_each(row, getter); out.push_back(row); } }
Simply amazing Steven :) Thanks a lot! Here's the code I ended up with, which reveals more context. Many thanks. I can't believe the quality and speed of your answer frankly. You made me day. Sincerely, --DD #include <sqlite3pp.h> #include <boost/fusion/include/for_each.hpp> #include <boost/fusion/include/boost_tuple.hpp> ... // Courtesy Steven Watanabe, Mar 20, 2009, boost-users mailing list. struct get_rows_binder { sqlite3pp::query& qry; int& bind_idx; template <class T> void operator()(const T& t) const { qry.bind(bind_idx++, t); } }; struct get_rows_getter { sqlite3pp::query::iterator& i; int& col_idx; template <class T> void operator()(T& t) const { t = i->get<T>(col_idx++); } }; template <class ROW, class BIND> void get_rows( const char* query, const BIND& binds, std::vector<ROW>& out ) { sqlite3pp::database& conn = DBConnection::instance()->db(); sqlite3pp::query qry(conn); checkerr(conn, qry.prepare(query)); if (boost::tuples::length<BIND>::value > 0) { int bind_idx = 1; // 1-based for binds get_rows_binder binder = { qry, bind_idx }; boost::fusion::for_each(binds, binder); } for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) { ROW row; int col_idx = 0; // 0-based for result-set columns get_rows_getter getter = { i, col_idx }; boost::fusion::for_each(row, getter); out.push_back(row); } } template <class ROW> void get_rows(const char* query, std::vector<ROW>& out) { get_rows(query, boost::make_tuple(), out); } void TypeIdTests::test_rtti_vtable() { ... { typedef boost::tuple<sqlite_int64> Row; std::vector<Row> rset; get_rows("select count(*) from rtti", rset); CPPUNIT_ASSERT_EQUAL(1, (int)rset.size()); CPPUNIT_ASSERT_EQUAL(expected_count, boost::get<0>(rset[0])); } { typedef boost::tuple<unsigned short, unsigned short, unsigned short> Row; std::vector<Row> rset; get_rows( "select type, is_a, distance from rtti where type = :1 and is_a = :2", boost::make_tuple(ts.transient_id(), ag.transient_id()), rset ); CPPUNIT_ASSERT_EQUAL(1, (int)rset.size()); CPPUNIT_ASSERT_EQUAL(ts.transient_id(), boost::get<0>(rset[0])); CPPUNIT_ASSERT_EQUAL(ag.transient_id(), boost::get<1>(rset[0])); CPPUNIT_ASSERT_EQUAL(unsigned short(1), boost::get<2>(rset[0])); } }
participants (2)
-
Dominique Devienne
-
Steven Watanabe