Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use std::string_view as argument instead of std::string on C++17 Compilers #167

Merged
merged 21 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
64c40af
C++17
jimmyjxiao Jun 15, 2018
2f2e7d8
Fix compilation on MSVC/Windows
jimmyjxiao Jun 15, 2018
5515487
Fix Modern C++ detection on MSVC
jimmyjxiao Jun 15, 2018
fd3f0bf
String_view arguments instead of string
jimmyjxiao Jun 15, 2018
c69ffc1
Fallback to traditional string on pre-c++17 compilers
jimmyjxiao Jun 15, 2018
e0e3a22
Looks like String.h doesn't include string_view.h, maybe this will fi…
jimmyjxiao Jun 15, 2018
6c95d0a
Fix preprocessor directives
jimmyjxiao Jun 15, 2018
3b181fe
has_sqlite_type specifically for stringviews is unnecessary, it looks…
jimmyjxiao Jun 15, 2018
b9ddb6b
Original SQL should have string, not string_view return type.
jimmyjxiao Jun 17, 2018
b08f6b6
Reuse UTF-8 prepare function, and add length to prepare call
jimmyjxiao Jun 17, 2018
080411a
Switch back to string parameters for database constructor
jimmyjxiao Jun 17, 2018
64f5f66
Function definitions back to std::string
jimmyjxiao Jun 17, 2018
9e56736
Sizes for string_view functions
jimmyjxiao Jun 17, 2018
a1849e2
Change preprocessor options for string_view to be more consistent wit…
jimmyjxiao Jun 17, 2018
fac3344
Switch to typedef instead of define
jimmyjxiao Jun 17, 2018
8e09803
Pass string_view by value
jimmyjxiao Jun 17, 2018
7d6ea22
Small Changes
jimmyjxiao Jun 18, 2018
c03e4e8
Add tests from string_view
jimmyjxiao Jun 18, 2018
b12f949
Update Readme
jimmyjxiao Jun 18, 2018
dffc090
oops
jimmyjxiao Jun 18, 2018
b0e3c8d
Sqlite takes bind text16 sizes in bytes
jimmyjxiao Jun 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ OPTION(ENABLE_SQLCIPHER_TESTS "enable sqlchipher test")

# Creates the file compile_commands.json in the build directory.
SET(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD 17)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still support C++14, so it would make sense to keep this set to 14. Especially since our Travis compiler is so old that it does not support most C++17 features anyway.


set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
include("cmake/HunterGate.cmake")
Expand Down Expand Up @@ -43,6 +43,7 @@ else()
endif()

catch_discover_tests(tests)
target_compile_options(tests PUBLIC $<$<CXX_COMPILER_ID:MSVC>:/Zc:__cplusplus> )

# Place the file in the source directory, permitting us to place a single configuration file for YCM there.
# YCM is the code-completion engine for (neo)vim https://github.com/Valloric/YouCompleteMe
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ int main() {
// int ,long, long long, float, double
// string , u16string
// sqlite3 only supports utf8 and utf16 strings, you should use std::string for utf8 and std::u16string for utf16.
// If you're using c++17, it takes `string_view` and `u16string_view` as arguments
// note that u"my text" is a utf16 string literal of type char16_t * .
db << "insert into user (age,name,weight) values (?,?,?);"
<< 20
Expand Down
35 changes: 11 additions & 24 deletions hdr/sqlite_modern_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ namespace sqlite {
return ++_inx;
}

sqlite3_stmt* _prepare(const std::u16string& sql) {
sqlite3_stmt* _prepare(u16str_ref sql) {
return _prepare(utility::utf16_to_utf8(sql));
}

sqlite3_stmt* _prepare(const std::string& sql) {
sqlite3_stmt* _prepare(str_ref sql) {
int hresult;
sqlite3_stmt* tmp = nullptr;
const char *remaining;
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, &remaining);
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), sql.length(), &tmp, &remaining);
if(hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, sql);
if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isspace(ch);}))
throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql);
Expand All @@ -105,13 +105,13 @@ namespace sqlite {

public:

database_binder(std::shared_ptr<sqlite3> db, std::u16string const & sql):
database_binder(std::shared_ptr<sqlite3> db, u16str_ref sql):
_db(db),
_stmt(_prepare(sql), sqlite3_finalize),
_inx(0) {
}

database_binder(std::shared_ptr<sqlite3> db, std::string const & sql):
database_binder(std::shared_ptr<sqlite3> db, str_ref sql):
_db(db),
_stmt(_prepare(sql), sqlite3_finalize),
_inx(0) {
Expand Down Expand Up @@ -372,36 +372,22 @@ namespace sqlite {
*this << R"(PRAGMA encoding = "UTF-16";)";
}

database(const std::u16string &db_name, const sqlite_config &config = {}): _db(nullptr) {
auto db_name_utf8 = utility::utf16_to_utf8(db_name);
sqlite3* tmp = nullptr;
auto ret = sqlite3_open_v2(db_name_utf8.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret);
sqlite3_extended_result_codes(_db.get(), true);
if(config.encoding != Encoding::UTF8)
database(const std::u16string &db_name, const sqlite_config &config = {}): database(utility::utf16_to_utf8(db_name), config) {
if (config.encoding == Encoding::ANY)
*this << R"(PRAGMA encoding = "UTF-16";)";
}

database(std::shared_ptr<sqlite3> db):
_db(db) {}

database_binder operator<<(const std::string& sql) {
database_binder operator<<(str_ref sql) {
return database_binder(_db, sql);
}

database_binder operator<<(const char* sql) {
return *this << std::string(sql);
}

database_binder operator<<(const std::u16string& sql) {
database_binder operator<<(u16str_ref sql) {
return database_binder(_db, sql);
}

database_binder operator<<(const char16_t* sql) {
return *this << std::u16string(sql);
}

connection_type connection() const { return _db; }

sqlite3_int64 last_insert_rowid() const {
Expand All @@ -418,7 +404,7 @@ namespace sqlite {

auto funcPtr = new auto(std::forward<Function>(func));
if(int result = sqlite3_create_function_v2(
_db.get(), name.c_str(), traits::arity, SQLITE_UTF8, funcPtr,
_db.get(), name.data(), traits::arity, SQLITE_UTF8, funcPtr,

This comment was marked as resolved.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should stick to .c_str() here because it makes clearer that we really need a c-style string, especially the trailing nul byte.

sql_function_binder::scalar<traits::arity, typename std::remove_reference<Function>::type>,
nullptr, nullptr, [](void* ptr){
delete static_cast<decltype(funcPtr)>(ptr);
Expand Down Expand Up @@ -652,3 +638,4 @@ namespace sqlite {
}
}
}

6 changes: 3 additions & 3 deletions hdr/sqlite_modern_cpp/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace sqlite {

class sqlite_exception: public std::runtime_error {
public:
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
sqlite_exception(const char* msg, str_ref sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
sqlite_exception(int code, str_ref sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
int get_code() const {return code & 0xFF;}
int get_extended_code() const {return code;}
std::string get_sql() const {return sql;}
Expand Down Expand Up @@ -40,7 +40,7 @@ namespace sqlite {
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; };

static void throw_sqlite_error(const int& error_code, const std::string &sql = "") {
static void throw_sqlite_error(const int& error_code, str_ref sql = "") {
switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \
Expand Down
4 changes: 2 additions & 2 deletions hdr/sqlite_modern_cpp/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ namespace sqlite {
}
});

sqlite3_config(SQLITE_CONFIG_LOG, (void(*)(void*,int,const char*))[](void *functor, int error_code, const char *errstr) {
sqlite3_config(SQLITE_CONFIG_LOG, static_cast<void(*)(void*,int,const char*)>([](void *functor, int error_code, const char *errstr) {
(*static_cast<decltype(ptr.get())>(functor))(error_code, errstr);
}, ptr.get());
}), ptr.get());
detail::store_error_log_data_pointer(std::move(ptr));
}
}
57 changes: 38 additions & 19 deletions hdr/sqlite_modern_cpp/type_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
#include <string>
#include <memory>
#include <vector>

#ifdef __has_include
#if __cplusplus >= 201703 && __has_include(<string_view>)
#define MODERN_SQLITE_STRINGVIEW_SUPPORT
#endif
#endif
#ifdef __has_include
#if __cplusplus > 201402 && __has_include(<optional>)
#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT
Expand All @@ -31,7 +35,20 @@
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
#include <variant>
#endif

#ifdef MODERN_SQLITE_STRINGVIEW_SUPPORT
#include <string_view>
namespace sqlite
{
typedef const std::string_view str_ref;
typedef const std::u16string_view u16str_ref;
}
#else
namespace sqlite
{
typedef const std::string& str_ref;
typedef const std::u16string& u16str_ref;
}
#endif
#include <sqlite3.h>
#include "errors.h"

Expand Down Expand Up @@ -150,16 +167,17 @@ namespace sqlite {
sqlite3_result_null(db);
}

// std::string
// str_ref
template<>
struct has_sqlite_type<std::string, SQLITE3_TEXT, void> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::string& val) {
return sqlite3_bind_text(stmt, inx, val.data(), -1, SQLITE_TRANSIENT);
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) {
return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT);
}

// Convert char* to string to trigger op<<(..., const std::string )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) { return bind_col_in_db(stmt, inx, std::string(STR, N-1)); }
// Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) {
return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT);
}

inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::string>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::string() :
Expand All @@ -170,31 +188,32 @@ namespace sqlite {
std::string(reinterpret_cast<char const *>(sqlite3_value_text(value)), sqlite3_value_bytes(value));
}

inline void store_result_in_db(sqlite3_context* db, const std::string& val) {
sqlite3_result_text(db, val.data(), -1, SQLITE_TRANSIENT);
inline void store_result_in_db(sqlite3_context* db, str_ref val) {
sqlite3_result_text(db, val.data(), val.length(), SQLITE_TRANSIENT);
}
// std::u16string
// u16str_ref
template<>
struct has_sqlite_type<std::u16string, SQLITE3_TEXT, void> : std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::u16string& val) {
return sqlite3_bind_text16(stmt, inx, val.data(), -1, SQLITE_TRANSIENT);
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) {
return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
}

// Convert char* to string to trigger op<<(..., const std::string )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) { return bind_col_in_db(stmt, inx, std::u16string(STR, N-1)); }
// Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) {
return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_TRANSIENT);
}

inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::u16string>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::u16string() :
std::u16string(reinterpret_cast<char16_t const *>(sqlite3_column_text16(stmt, inx)), sqlite3_column_bytes16(stmt, inx));
}
inline std::u16string get_val_from_db(sqlite3_value *value, result_type<std::u16string >) {
inline std::u16string get_val_from_db(sqlite3_value *value, result_type<std::u16string>) {
return sqlite3_value_type(value) == SQLITE_NULL ? std::u16string() :
std::u16string(reinterpret_cast<char16_t const *>(sqlite3_value_text16(value)), sqlite3_value_bytes16(value));
}

inline void store_result_in_db(sqlite3_context* db, const std::u16string& val) {
sqlite3_result_text16(db, val.data(), -1, SQLITE_TRANSIENT);
inline void store_result_in_db(sqlite3_context* db, u16str_ref val) {
sqlite3_result_text16(db, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
}

// Other integer types
Expand Down
2 changes: 1 addition & 1 deletion hdr/sqlite_modern_cpp/utility/utf16_utf8.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace sqlite {
namespace utility {
inline std::string utf16_to_utf8(const std::u16string &input) {
inline std::string utf16_to_utf8(u16str_ref input) {
struct : std::codecvt<char16_t, char, std::mbstate_t> {
} codecvt;
std::mbstate_t state{};
Expand Down
5 changes: 3 additions & 2 deletions tests/flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ struct TmpFile {
TmpFile(): fname("./flags.db") { }
~TmpFile() { remove(fname.c_str()); }
};

#if BYTE_ORDER == BIG_ENDIAN
#ifdef _WIN32
#define OUR_UTF16 "UTF-16le"
#elif BYTE_ORDER == BIG_ENDIAN
#define OUR_UTF16 "UTF-16be"
#else
#define OUR_UTF16 "UTF-16le"
Expand Down
26 changes: 26 additions & 0 deletions tests/string_view.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <iostream>
#include <cstdlib>
#include <sqlite_modern_cpp.h>
#include <catch.hpp>


#ifdef MODERN_SQLITE_STRINGVIEW_SUPPORT
#include <string_view>

using namespace sqlite;
using namespace std;
TEST_CASE("std::string_view works", "[string_view]") {
database db(":memory:");;
db << "CREATE TABLE foo (a integer, b string);\n";
const std::string_view test1 = "null terminated string view";
db << "INSERT INTO foo VALUES (?, ?)" << 1 << test1;
std::string str;
db << "SELECT b from FOO where a=?;" << 1 >> str;
REQUIRE(test1 == str);
const char s[] = "hello world";
std::string_view test2(&s[0], 2);
db << "INSERT INTO foo VALUES (?,?)" << 2 << test2;
db << "SELECT b from FOO where a=?" << 2 >> str;
REQUIRE(str == "he");
}
#endif