diff --git a/include/SQLiteCpp/Column.h b/include/SQLiteCpp/Column.h index cfafa079..be0babc4 100644 --- a/include/SQLiteCpp/Column.h +++ b/include/SQLiteCpp/Column.h @@ -260,5 +260,22 @@ class Column */ std::ostream& operator<<(std::ostream& aStream, const Column& aColumn); +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) + // Create an instance of T from the first N columns, see declaration in Statement.h for full details + template + T Statement::getColumns() + { + checkRow(); + checkIndex(N - 1); + return getColumns(std::make_integer_sequence{}); + } + + // Helper function called by getColums + template + T Statement::getColumns(const std::integer_sequence) + { + return T(Column(mStmtPtr, Is)...); + } +#endif } // namespace SQLite diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 594ee38c..6d1d3191 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -427,6 +427,40 @@ class Statement */ Column getColumn(const char* apName); +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) + /** + * @brief Return an instance of T constructed from copies of the first N columns + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from: + * - if provided column count is out of bound + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified column count is out of the [0, getColumnCount()) range. + * + * @tparam T Object type to construct + * @tparam N Number of columns + * + * @note Requires std=C++14 + */ + template + T getColumns(); + +private: + /** + * @brief Helper function used by getColumns to expand an integer_sequence used to generate + * the required Column objects + */ + template + T getColumns(const std::integer_sequence); + +public: +#endif + /** * @brief Test if the column value is NULL * diff --git a/tests/Statement_test.cpp b/tests/Statement_test.cpp index 7f38b6bb..8bd626c2 100644 --- a/tests/Statement_test.cpp +++ b/tests/Statement_test.cpp @@ -651,3 +651,60 @@ TEST(Statement, getName) { EXPECT_EQ("msg", oname1); #endif } + +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) +TEST(Statement, getColumns) { + struct GetRowTestStruct + { + int id; + std::string msg; + int integer; + double real; + GetRowTestStruct(int _id, std::string _msg, int _integer, double _real) + : id(_id), msg(_msg), integer(_integer), real(_real) + {} + + GetRowTestStruct(int _id, const std::string& _msg) + : id(_id), msg(_msg), integer(-1), real(0.0) + {} + }; + + // Create a new database + SQLite::Database db(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); + EXPECT_EQ(SQLite::OK, db.getErrorCode()); + EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode()); + + // Create a new table + EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL)")); + EXPECT_EQ(SQLite::OK, db.getErrorCode()); + EXPECT_EQ(SQLite::OK, db.getExtendedErrorCode()); + + // Create a first row + EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)")); + EXPECT_EQ(1, db.getLastInsertRowid()); + EXPECT_EQ(1, db.getTotalChanges()); + + // Compile a SQL query + SQLite::Statement query(db, "SELECT * FROM test"); + EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str()); + EXPECT_EQ(4, query.getColumnCount()); + query.executeStep(); + EXPECT_TRUE(query.isOk()); + EXPECT_FALSE(query.isDone()); + + // Get all columns + auto testStruct = query.getColumns(); + EXPECT_EQ(1, testStruct.id); + EXPECT_EQ("first", testStruct.msg); + EXPECT_EQ(123, testStruct.integer); + EXPECT_EQ(0.123, testStruct.real); + + // Get only the first 2 columns + auto testStruct2 = query.getColumns(); + EXPECT_EQ(1, testStruct2.id); + EXPECT_EQ("first", testStruct2.msg); + EXPECT_EQ(-1, testStruct2.integer); + EXPECT_EQ(0.0, testStruct2.real); +} +#endif +