-
Notifications
You must be signed in to change notification settings - Fork 162
Use std::string_view as argument instead of std::string on C++17 Compilers #167
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
Changes from 9 commits
64c40af
2f2e7d8
5515487
fd3f0bf
c69ffc1
e0e3a22
6c95d0a
3b181fe
b9ddb6b
b08f6b6
080411a
64f5f66
9e56736
a1849e2
fac3344
8e09803
7d6ea22
c03e4e8
b12f949
dffc090
b0e3c8d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,11 +85,19 @@ namespace sqlite { | |
return ++_inx; | ||
} | ||
|
||
sqlite3_stmt* _prepare(const std::u16string& sql) { | ||
return _prepare(utility::utf16_to_utf8(sql)); | ||
sqlite3_stmt* _prepare(const U16STR_REF& sql) { | ||
//return _prepare(utility::utf16_to_utf8(sql)); | ||
int hresult; | ||
sqlite3_stmt* tmp = nullptr; | ||
const void *remaining; | ||
hresult = sqlite3_prepare16_v2(_db.get(), sql.data(), -1, &tmp, &remaining); | ||
if (hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, utility::utf16_to_utf8(sql.data())); | ||
if (!std::all_of(static_cast<const char16_t*>(remaining), sql.data() + sql.size(), [](char16_t ch) {return std::isspace(ch); })) | ||
throw errors::more_statements("Multiple semicolon separated statements are unsupported", utility::utf16_to_utf8(sql.data())); | ||
return tmp; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an advantage in not reusing the UTF-8 version?
|
||
} | ||
|
||
sqlite3_stmt* _prepare(const std::string& sql) { | ||
sqlite3_stmt* _prepare(const STR_REF& sql) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should accept a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can I ask what the advantage of this would be? I know that in my use case, I'm going to have a few There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In most cases they will be inlined, then it doesn't make a difference. Otherwise the advantage is that a const reference requires some object to reference, so the Disclaimer: I did not run a benchmark on this so I could be wrong. In this case, I would love to hear why. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're probably right. I wrote some super crappy benchmarks and it seems passing by string_view was slightly faster: https://github.com/zowpowow/sqlite_modern_cpp/tree/str_view_by_value |
||
int hresult; | ||
sqlite3_stmt* tmp = nullptr; | ||
const char *remaining; | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
@@ -105,13 +113,13 @@ namespace sqlite { | |
|
||
public: | ||
|
||
database_binder(std::shared_ptr<sqlite3> db, std::u16string const & sql): | ||
database_binder(std::shared_ptr<sqlite3> db, U16STR_REF const & 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 const & sql): | ||
_db(db), | ||
_stmt(_prepare(sql), sqlite3_finalize), | ||
_inx(0) { | ||
|
@@ -362,7 +370,7 @@ namespace sqlite { | |
std::shared_ptr<sqlite3> _db; | ||
|
||
public: | ||
database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) { | ||
database(const STR_REF &db_name, const sqlite_config &config = {}): _db(nullptr) { | ||
sqlite3* tmp = nullptr; | ||
auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast<int>(config.flags), config.zVfs); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assumes that |
||
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed. | ||
|
@@ -372,8 +380,8 @@ 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); | ||
database(const U16STR_REF &db_name, const sqlite_config &config = {}): _db(nullptr) { | ||
auto db_name_utf8 = utility::utf16_to_utf8(db_name.data()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same problem as above. |
||
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. | ||
|
@@ -386,20 +394,20 @@ namespace sqlite { | |
database(std::shared_ptr<sqlite3> db): | ||
_db(db) {} | ||
|
||
database_binder operator<<(const std::string& sql) { | ||
database_binder operator<<(const STR_REF& sql) { | ||
return database_binder(_db, sql); | ||
} | ||
|
||
database_binder operator<<(const char* sql) { | ||
return *this << std::string(sql); | ||
return *this << STR_REF(sql); | ||
} | ||
|
||
database_binder operator<<(const std::u16string& sql) { | ||
database_binder operator<<(const U16STR_REF& sql) { | ||
return database_binder(_db, sql); | ||
} | ||
|
||
database_binder operator<<(const char16_t* sql) { | ||
return *this << std::u16string(sql); | ||
return *this << U16STR_REF(sql); | ||
} | ||
|
||
connection_type connection() const { return _db; } | ||
|
@@ -413,12 +421,12 @@ namespace sqlite { | |
} | ||
|
||
template <typename Function> | ||
void define(const std::string &name, Function&& func) { | ||
void define(const STR_REF &name, Function&& func) { | ||
typedef utility::function_traits<Function> traits; | ||
|
||
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.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should stick to |
||
sql_function_binder::scalar<traits::arity, typename std::remove_reference<Function>::type>, | ||
nullptr, nullptr, [](void* ptr){ | ||
delete static_cast<decltype(funcPtr)>(ptr); | ||
|
@@ -427,13 +435,13 @@ namespace sqlite { | |
} | ||
|
||
template <typename StepFunction, typename FinalFunction> | ||
void define(const std::string &name, StepFunction&& step, FinalFunction&& final) { | ||
void define(const STR_REF &name, StepFunction&& step, FinalFunction&& final) { | ||
typedef utility::function_traits<StepFunction> traits; | ||
using ContextType = typename std::remove_reference<typename traits::template argument<0>>::type; | ||
|
||
auto funcPtr = new auto(std::make_pair(std::forward<StepFunction>(step), std::forward<FinalFunction>(final))); | ||
if(int result = sqlite3_create_function_v2( | ||
_db.get(), name.c_str(), traits::arity - 1, SQLITE_UTF8, funcPtr, nullptr, | ||
_db.get(), name.data(), traits::arity - 1, SQLITE_UTF8, funcPtr, nullptr, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above |
||
sql_function_binder::step<ContextType, traits::arity, typename std::remove_reference<decltype(*funcPtr)>::type>, | ||
sql_function_binder::final<ContextType, typename std::remove_reference<decltype(*funcPtr)>::type>, | ||
[](void* ptr){ | ||
|
@@ -652,3 +660,5 @@ namespace sqlite { | |
} | ||
} | ||
} | ||
#undef STR_REF | ||
#undef U16STR_REF | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add the newline at the end of the file. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(std::move(sql)) {} | ||
sqlite_exception(int code, STR_REF sql): runtime_error(sqlite3_errstr(code)), code(code), sql(std::move(sql)) {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
int get_code() const {return code & 0xFF;} | ||
int get_extended_code() const {return code;} | ||
std::string get_sql() const {return sql;} | ||
|
@@ -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, const STR_REF &sql = "") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is still a const& |
||
switch(error_code & 0xFF) { | ||
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \ | ||
case SQLITE_ ## NAME: switch(error_code) { \ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,20 @@ | |
#include <string> | ||
#include <memory> | ||
#include <vector> | ||
|
||
#ifdef __has_include | ||
#if __cplusplus >= 201703 && __has_include(<string_view>) | ||
#define MODERN_SQLITE_STRINGVIEW_SUPPORT | ||
#include <string_view> | ||
#define STR_REF std::string_view | ||
#define U16STR_REF std::u16string_view | ||
#else | ||
#define STR_REF std::string | ||
#define U16STR_REF std::u16string | ||
#endif | ||
#else | ||
#define STR_REF std::string | ||
#define U16STR_REF std::u16string | ||
#endif | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is more modern and more robust to use typedefs instead of macros. It is also more consistant with |
||
#ifdef __has_include | ||
#if __cplusplus > 201402 && __has_include(<optional>) | ||
#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT | ||
|
@@ -150,16 +163,15 @@ 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) { | ||
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const STR_REF& val) { | ||
return sqlite3_bind_text(stmt, inx, val.data(), -1, SQLITE_TRANSIENT); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
// 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 bind_col_in_db(stmt, inx, STR_REF(STR, N-1)); } | ||
|
||
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() : | ||
|
@@ -170,30 +182,29 @@ 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) { | ||
inline void store_result_in_db(sqlite3_context* db, const STR_REF& val) { | ||
sqlite3_result_text(db, val.data(), -1, SQLITE_TRANSIENT); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
// 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) { | ||
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const U16STR_REF& val) { | ||
return sqlite3_bind_text16(stmt, inx, val.data(), -1, SQLITE_TRANSIENT); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
// 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 bind_col_in_db(stmt, inx, U16STR_REF(STR, N-1)); } | ||
|
||
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) { | ||
inline void store_result_in_db(sqlite3_context* db, const U16STR_REF& val) { | ||
sqlite3_result_text16(db, val.data(), -1, SQLITE_TRANSIENT); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
|
There was a problem hiding this comment.
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.