From 3022d50b38e96327841145eb4f039c826b5694eb Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Sat, 4 Jun 2016 22:06:38 +0200 Subject: [PATCH 1/5] initial submission of variadic template for bind tested with CXXFLAGS="-std=c++14" cmake with gcc 5.3.1. --- Doxyfile | 2 +- examples/example1/main.cpp | 39 +++++++++++++++++ include/SQLiteCpp/VariadicBind.h | 74 ++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 include/SQLiteCpp/VariadicBind.h diff --git a/Doxyfile b/Doxyfile index 4a5a701..7fd615b 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1935,7 +1935,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = __cplusplus=201402L # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/examples/example1/main.cpp b/examples/example1/main.cpp index 45ba078..b11640d 100644 --- a/examples/example1/main.cpp +++ b/examples/example1/main.cpp @@ -16,6 +16,8 @@ #include #include +#include + #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER namespace SQLite @@ -80,6 +82,34 @@ private: SQLite::Statement mQuery; ///< Database prepared SQL query }; +void demonstrateVariadicBind() { +#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) + // Open a database file in create/write mode + SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + db.exec("DROP TABLE IF EXISTS test"); + db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"); + + { + SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)"); + + SQLite::bind(query, 42, "fortytwo"); + // Execute the one-step query to insert the blob + int nb = query.exec(); + std::cout << "INSERT INTO test VALUES (NULL, ?)\", returned " << nb + << std::endl; + } + + SQLite::Statement query(db, "SELECT * FROM test"); + std::cout << "SELECT * FROM test :\n"; + if (query.executeStep()) { + std::cout << query.getColumn(0).getInt() << "\t\"" + << query.getColumn(1).getText() << "\"\n"; + } +#else + throw std::runtime_error("demonstrateVariadicBind(): sorry, no c++14 support in this build."); +#endif +} int main () { @@ -422,6 +452,15 @@ int main () } remove("out.png"); + //example with variadic bind (requires c++14) + try { + std::cout<<"cplusplus version is "<<__cplusplus<<'\n'; + demonstrateVariadicBind(); + } catch (std::exception& e) { + std::cout << "SQLite exception: " << e.what() << std::endl; + return EXIT_FAILURE; // unexpected error : exit the example program + } + std::cout << "everything ok, quitting\n"; return EXIT_SUCCESS; diff --git a/include/SQLiteCpp/VariadicBind.h b/include/SQLiteCpp/VariadicBind.h new file mode 100644 index 0000000..2448230 --- /dev/null +++ b/include/SQLiteCpp/VariadicBind.h @@ -0,0 +1,74 @@ +/** + * @file VariadicBind.h + * @ingroup SQLiteCpp + * @brief Convenience function for Statement::bind(...) + * + * Copyright (c) 2016 Paul Dreik (github@pauldreik.se) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include + +namespace SQLite +{ + + +//this requires c++14. seems like visual studio 2015 should work (yet untested). +#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) +/// @cond +#include +#include + +/// implementation detail for variadic bind. +namespace detail { +template +inline void invoke_with_index(F&& f, std::integer_sequence, + const Args& ...args) { + std::initializer_list { (f(I+1,args),0)... }; +} + +/// implementation detail for variadic bind. +template +inline void invoke_with_index(F&&f, const Args& ... args) { + invoke_with_index(std::forward(f),std::index_sequence_for(), args...); +} + +} //namespace detail +/// @endcond + +/** + * \brief Convenience function for calling Statement::bind(...) once for each argument given. + * + * This takes care of incrementing the index between each calls to bind. + * + * This feature requires a c++14 capable compiler. + * + * \code{.cpp} + * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC +void bind(SQLite::Statement& s,const Args& ... args) { + + static_assert(sizeof...(args)>0,"please invoke bind with one or more args"); + + auto f=[&s](std::size_t index, const auto& value) { + s.bind(index,value); + }; + detail::invoke_with_index(f, args...); +} +#else +//not supported in older c++. provide a fallback? +#endif // c++14 + +} // namespace SQLite From 808a9c894e0efd8fe228787a5b8b7c055b179372 Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Wed, 15 Jun 2016 20:39:20 +0200 Subject: [PATCH 2/5] add unit test for variadic bind --- CMakeLists.txt | 1 + tests/VariadicBind_test.cpp | 77 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tests/VariadicBind_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a410bc8..535ffef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ set(SQLITECPP_TESTS tests/Database_test.cpp tests/Statement_test.cpp tests/Backup_test.cpp + tests/VariadicBind_test.cpp ) source_group(tests FILES ${SQLITECPP_TESTS}) diff --git a/tests/VariadicBind_test.cpp b/tests/VariadicBind_test.cpp new file mode 100644 index 0000000..95782e2 --- /dev/null +++ b/tests/VariadicBind_test.cpp @@ -0,0 +1,77 @@ +/** + * @file VariadicBind_test.cpp + * @ingroup tests + * @brief Test of variadic bind + * + * Copyright (c) 2016 Paul Dreik (github@pauldreik.se) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ + +#include +#include +#include + +#include + +#include + +//this requires c++14. seems like visual studio 2015 should work (yet untested). +#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) + +TEST(VariadicBind, invalid) { + // Create a new database +#if 0 + SQLite::Database db("variadic_bind_test.db3", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); +#else + SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); +#endif + + EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test")); + EXPECT_EQ(0, + db.exec( + "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') ")); + EXPECT_TRUE(db.tableExists("test")); + + { + SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)"); + + //zero arguments - should give compile time error through a static assert + //SQLite::bind(query); + + //bind one argument less than expected - should be fine. + //the unspecified argument should be set to null, not the default. + SQLite::bind(query, 1); + EXPECT_EQ(1, query.exec()); + query.reset(); + + //bind all arguments - should work just fine + SQLite::bind(query, 2, "two"); + EXPECT_EQ(1, query.exec()); + query.reset(); + + //bind too many arguments - should throw. + EXPECT_THROW(SQLite::bind(query, 3, "three", 0), SQLite::Exception); + EXPECT_EQ(1, query.exec()); + } + + //make sure the content is as expected + { + using namespace std::string_literals; + + SQLite::Statement query(db, "SELECT id, value FROM test ORDER BY id"s); + std::vector > results; + while (query.executeStep()) { + const int id = query.getColumn(0); + std::string value = query.getColumn(1); + results.emplace_back( id, std::move(value) ); + } + EXPECT_EQ(std::size_t(3), results.size()); + + EXPECT_EQ(std::make_pair(1,""s), results.at(0)); + EXPECT_EQ(std::make_pair(2,"two"s), results.at(1)); + EXPECT_EQ(std::make_pair(3,"three"s), results.at(2)); + } +} +#endif // c++14 From 4a92dde562a670f4493e561770f561b2c284391f Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Wed, 15 Jun 2016 20:39:51 +0200 Subject: [PATCH 3/5] dont include std headers inside our own namespace --- include/SQLiteCpp/VariadicBind.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/SQLiteCpp/VariadicBind.h b/include/SQLiteCpp/VariadicBind.h index 2448230..8cce653 100644 --- a/include/SQLiteCpp/VariadicBind.h +++ b/include/SQLiteCpp/VariadicBind.h @@ -12,8 +12,7 @@ #include -namespace SQLite -{ + //this requires c++14. seems like visual studio 2015 should work (yet untested). @@ -22,6 +21,9 @@ namespace SQLite #include #include +namespace SQLite +{ + /// implementation detail for variadic bind. namespace detail { template @@ -67,8 +69,10 @@ void bind(SQLite::Statement& s,const Args& ... args) { }; detail::invoke_with_index(f, args...); } + +} // namespace SQLite + #else //not supported in older c++. provide a fallback? #endif // c++14 -} // namespace SQLite From 9234eb32164fd34fc820be8325a5e9298a66816d Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Wed, 15 Jun 2016 20:43:11 +0200 Subject: [PATCH 4/5] polish before pull request --- examples/example1/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/example1/main.cpp b/examples/example1/main.cpp index b11640d..4049940 100644 --- a/examples/example1/main.cpp +++ b/examples/example1/main.cpp @@ -454,7 +454,6 @@ int main () //example with variadic bind (requires c++14) try { - std::cout<<"cplusplus version is "<<__cplusplus<<'\n'; demonstrateVariadicBind(); } catch (std::exception& e) { std::cout << "SQLite exception: " << e.what() << std::endl; From 356c50a342608eea353742e89e2eddade9d90a8a Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Wed, 15 Jun 2016 21:01:51 +0200 Subject: [PATCH 5/5] do not run variadic bind example for c++ older than c++14 --- examples/example1/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/example1/main.cpp b/examples/example1/main.cpp index 4049940..9cd38cf 100644 --- a/examples/example1/main.cpp +++ b/examples/example1/main.cpp @@ -453,12 +453,14 @@ int main () remove("out.png"); //example with variadic bind (requires c++14) +#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) try { demonstrateVariadicBind(); } catch (std::exception& e) { std::cout << "SQLite exception: " << e.what() << std::endl; return EXIT_FAILURE; // unexpected error : exit the example program } +#endif std::cout << "everything ok, quitting\n";