diff --git a/CMakeLists.txt b/CMakeLists.txt index 0146377..7c25f4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ else (MSVC) set(CPPLINT_ARG_OUTPUT "--output=eclipse") set(CPPCHECK_ARG_TEMPLATE "--template=gcc") # Useful compile flags and extra warnings - add_compile_options(-fstack-protector -Wall -Winit-self -Wswitch-enum -Wshadow -Winline) + add_compile_options(-fstack-protector -Wall -Wextra -Wpedantic -Wno-long-long -Wswitch-enum -Wshadow -Winline) if (CMAKE_COMPILER_IS_GNUCXX) # GCC flags if (SQLITECPP_USE_GCOV AND CMAKE_COMPILER_IS_GNUCXX) @@ -161,13 +161,24 @@ include_directories("${PROJECT_SOURCE_DIR}/include") # add sources of the wrapper as a "SQLiteCpp" static library add_library(SQLiteCpp ${SQLITECPP_SRC} ${SQLITECPP_INC} ${SQLITECPP_DOC} ${SQLITECPP_SCRIPT}) -target_link_libraries(SQLiteCpp sqlite3) -target_include_directories(SQLiteCpp PUBLIC "${PROJECT_SOURCE_DIR}/include") +# make the sqlite3 library part of the interface of the SQLiteCpp wrapper itself (the client app does not need to link to sqlite3) +target_link_libraries(SQLiteCpp PUBLIC sqlite3) if (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")) set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-fPIC") endif (UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")) +# Allow the library to be installed via "make install" and found with "find_package" +install(TARGETS SQLiteCpp + EXPORT ${PROJECT_NAME}Config + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + COMPONENT libraries) +target_include_directories(SQLiteCpp PUBLIC + $ + $) +install(DIRECTORY include/ DESTINATION include COMPONENT headers FILES_MATCHING REGEX ".*\\.(hpp|h)$") +install(EXPORT ${PROJECT_NAME}Config DESTINATION lib/cmake/${PROJECT_NAME}) ## Build provided copy of SQLite3 C library ## diff --git a/README.md b/README.md index 525dfc4..02b8cce 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,8 @@ SQLiteC++ [![Coveralls](https://img.shields.io/coveralls/SRombauts/SQLiteCpp.svg)](https://coveralls.io/github/SRombauts/SQLiteCpp "Coveralls test coverage") [![Join the chat at https://gitter.im/SRombauts/SQLiteCpp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SRombauts/SQLiteCpp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - SQLiteC++ (SQLiteCpp) is a smart and easy to use C++ SQLite3 wrapper. -See SQLiteC++ website http://srombauts.github.com/SQLiteCpp on GitHub. - Keywords: sqlite, sqlite3, C, library, wrapper C++ ## About SQLiteC++: @@ -22,7 +19,8 @@ with a few intuitive and well documented C++ classes. ### License: -Copyright (c) 2012-2016 Sébastien Rombauts (sebastien.rombauts@gmail.com) +Copyright (c) 2012-2017 Sébastien Rombauts (sebastien.rombauts@gmail.com) + Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) @@ -69,6 +67,7 @@ Developements and tests are done under the following OSs: - Ubuntu 14.04 (Travis CI) - Windows XP/10 - OS X 10.11 (Travis CI) + And the following IDEs/Compilers - GCC 4.8.4, 4.9.3, 5.3.0 and 6.1.1 (C++03, C++11, C++14, C++1z) - Clang 3.5 and 3.8 @@ -80,7 +79,7 @@ And the following IDEs/Compilers - an STL implementation (even an old one, like the one provided with VC6 should work) - exception support (the class Exception inherits from std::runtime_error) -- the SQLite library, either by linking to it dynamicaly or statically (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux), +- the SQLite library (3.7.15 minimum from 2012-12-12) either by linking to it dynamicaly or statically (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux), or by adding its source file in your project code base (source code provided in src/sqlite3 for Windows), with the SQLITE_ENABLE_COLUMN_METADATA macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata). diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c741881 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-slate \ No newline at end of file diff --git a/cpplint.py b/cpplint.py index 6b97648..8fecfa9 100755 --- a/cpplint.py +++ b/cpplint.py @@ -53,6 +53,15 @@ import sys import unicodedata +try: + xrange(0,1) + PY3 = False +except NameError: + PY3 = True # Python 3 + xrange = range + unicode = str + + _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] @@ -736,7 +745,7 @@ class _CppLintState(object): def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" # SRombauts: "cpplint:" prefix - for category, count in self.errors_by_category.iteritems(): + for category, count in self.errors_by_category.items(): sys.stderr.write('cpplint: Category \'%s\' errors found: %d\n' % (category, count)) # SRombauts: "cpplint:" prefix and error message only when appropriate @@ -3694,7 +3703,7 @@ def _GetTextInside(text, start_pattern): # Give opening punctuations to get the matching close-punctuations. matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) + closing_punctuation = set(matching_punctuation.values()) # Find the position to start extracting text. match = re.search(start_pattern, text, re.M) @@ -4779,10 +4788,11 @@ def main(): # Change stderr to write with replacement characters so we don't die # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') + if not PY3: + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') _cpplint_state.ResetErrorCounts() for filename in filenames: diff --git a/include/SQLiteCpp/Column.h b/include/SQLiteCpp/Column.h index cfafa07..be0babc 100644 --- a/include/SQLiteCpp/Column.h +++ b/include/SQLiteCpp/Column.h @@ -260,5 +260,22 @@ private: */ 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/Database.h b/include/SQLiteCpp/Database.h index 0129809..d3184c0 100644 --- a/include/SQLiteCpp/Database.h +++ b/include/SQLiteCpp/Database.h @@ -412,7 +412,7 @@ public: * * @throw SQLite::Exception in case of error */ - static const bool isUnencrypted(const std::string& aFilename); + static bool isUnencrypted(const std::string& aFilename); private: /// @{ Database must be non-copyable diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 594ee38..030eab9 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -427,6 +427,40 @@ public: */ 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 * @@ -565,7 +599,7 @@ private: /** * @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message * - * @param[in] SQLite return code to test against the SQLITE_OK expected value + * @param[in] aRet SQLite return code to test against the SQLITE_OK expected value */ inline void check(const int aRet) const { diff --git a/src/Database.cpp b/src/Database.cpp index cfa5447..164f3e3 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -259,13 +259,14 @@ void Database::rekey(const std::string& aNewKey) const check(ret); } #else // SQLITE_HAS_CODEC + static_cast(aNewKey); // silence unused parameter warning const SQLite::Exception exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); throw exception; #endif // SQLITE_HAS_CODEC } // Test if a file contains an unencrypted database. -const bool Database::isUnencrypted(const std::string& aFilename) +bool Database::isUnencrypted(const std::string& aFilename) { if (aFilename.length() > 0) { std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); diff --git a/tests/Statement_test.cpp b/tests/Statement_test.cpp index 7f38b6b..8bd626c 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 +