Skip to content

Commit 6b468a4

Browse files
authored
Merge pull request #95 from zauguin/variant
std::variant support
2 parents 94c3a68 + 2e96ba7 commit 6b468a4

File tree

4 files changed

+350
-14
lines changed

4 files changed

+350
-14
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,40 @@ If you do not have C++17 support, you can use boost optional instead by defining
338338
339339
**Note: boost support is deprecated and will be removed in future versions.**
340340
341+
Variant type support (C++17)
342+
----
343+
If your columns may have flexible types, you can use C++17's `std::variant` to extract the value.
344+
345+
```c++
346+
db << "CREATE TABLE tbl (id integer, data);";
347+
db << "INSERT INTO tbl VALUES (?, ?);" << 1 << vector<int> { 1, 2, 3};
348+
db << "INSERT INTO tbl VALUES (?, ?);" << 2 << 2.5;
349+
350+
db << "select data from tbl where id = 1"
351+
>> [](std::variant<vector<int>, double> data) {
352+
if(data.index() != 1) {
353+
cerr << "ERROR: we expected a blob" << std::endl;
354+
}
355+
356+
for(auto i : get<vector<int>>(data)) cout << i << ","; cout << endl;
357+
};
358+
359+
db << "select data from tbl where id = 2"
360+
>> [](std::variant<vector<int>, double> data) {
361+
if(data.index() != 2) {
362+
cerr << "ERROR: we expected a real number" << std::endl;
363+
}
364+
365+
cout << get<double>(data) << endl;
366+
};
367+
```
368+
369+
If you read a specific type and this type does not match the actual type in the SQlite database, yor data will be converted.
370+
This does not happen if you use a `variant`.
371+
If the `variant` does an alternative of the same value type, an `mismatch` exception will be thrown.
372+
The value types are NULL, integer, real number, text and BLOB.
373+
To support all possible values, you can use `variant<nullptr_t, sqlite_int64, double, string, vector<char>`.
374+
341375
Errors
342376
----
343377

hdr/sqlite_modern_cpp.h

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
#endif
1717
#endif
1818

19+
#ifdef __has_include
20+
#if __cplusplus > 201402 && __has_include(<variant>)
21+
#define MODERN_SQLITE_STD_VARIANT_SUPPORT
22+
#endif
23+
#endif
24+
1925
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
2026
#include <optional>
2127
#endif
@@ -36,10 +42,10 @@ namespace sqlite {
3642
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
3743
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
3844
int get_code() {return code;}
39-
std::string get_sql() {return sql;}
45+
std::string get_sql() {return sql;}
4046
private:
4147
int code;
42-
std::string sql;
48+
std::string sql;
4349
};
4450

4551
namespace exceptions {
@@ -110,7 +116,13 @@ namespace sqlite {
110116
else throw sqlite_exception(error_code, sql);
111117
}
112118
}
119+
}
120+
121+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
122+
#include "sqlite_modern_cpp/utility/variant.h"
123+
#endif
113124

125+
namespace sqlite {
114126
class database;
115127
class database_binder;
116128

@@ -161,19 +173,19 @@ namespace sqlite {
161173

162174
}
163175

164-
std::string sql() {
176+
std::string sql() {
165177
#if SQLITE_VERSION_NUMBER >= 3014000
166-
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
167-
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
168-
return str ? str.get() : original_sql();
178+
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
179+
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
180+
return str ? str.get() : original_sql();
169181
#else
170-
return original_sql();
182+
return original_sql();
171183
#endif
172-
}
184+
}
173185

174-
std::string original_sql() {
175-
return sqlite3_sql(_stmt.get());
176-
}
186+
std::string original_sql() {
187+
return sqlite3_sql(_stmt.get());
188+
}
177189

178190
void used(bool state) {
179191
if(execution_started == true && state == true) {
@@ -258,6 +270,13 @@ namespace sqlite {
258270
|| std::is_integral<Type>::value
259271
|| std::is_same<sqlite_int64, Type>::value
260272
> { };
273+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
274+
template <typename ...Args>
275+
struct is_sqlite_value< std::variant<Args...> > : public std::integral_constant<
276+
bool,
277+
true
278+
> { };
279+
#endif
261280

262281

263282
template<typename T> friend database_binder& operator <<(database_binder& db, const T& val);
@@ -269,6 +288,10 @@ namespace sqlite {
269288
friend database_binder& operator <<(database_binder& db, std::nullptr_t);
270289
template<typename T> friend database_binder& operator <<(database_binder& db, const std::unique_ptr<T>& val);
271290
template<typename T> friend void get_col_from_db(database_binder& db, int inx, std::unique_ptr<T>& val);
291+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
292+
template<typename ...Args> friend database_binder& operator <<(database_binder& db, const std::variant<Args...>& val);
293+
template<typename ...Args> friend void get_col_from_db(database_binder& db, int inx, std::variant<Args...>& val);
294+
#endif
272295
template<typename T> friend T operator++(database_binder& db, int);
273296
// Overload instead of specializing function templates (http://www.gotw.ca/publications/mill17.htm)
274297
friend database_binder& operator<<(database_binder& db, const int& val);
@@ -901,6 +924,28 @@ namespace sqlite {
901924
}
902925
#endif
903926

927+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
928+
template <typename ...Args> inline database_binder& operator <<(database_binder& db, const std::variant<Args...>& val) {
929+
std::visit([&](auto &&opt) {db << std::forward<decltype(opt)>(opt);}, val);
930+
return db;
931+
}
932+
template <typename ...Args> inline void store_result_in_db(sqlite3_context* db, const std::variant<Args...>& val) {
933+
std::visit([&](auto &&opt) {store_result_in_db(db, std::forward<decltype(opt)>(opt));}, val);
934+
}
935+
template <typename ...Args> inline void get_col_from_db(database_binder& db, int inx, std::variant<Args...>& val) {
936+
utility::variant_select<Args...>(sqlite3_column_type(db._stmt.get(), inx))([&](auto v) {
937+
get_col_from_db(db, inx, v);
938+
val = std::move(v);
939+
});
940+
}
941+
template <typename ...Args> inline void get_val_from_db(sqlite3_value *value, std::variant<Args...>& val) {
942+
utility::variant_select<Args...>(sqlite3_value_type(value))([&](auto v) {
943+
get_val_from_db(value, v);
944+
val = std::move(v);
945+
});
946+
}
947+
#endif
948+
904949
// Some ppl are lazy so we have a operator for proper prep. statemant handling.
905950
void inline operator++(database_binder& db, int) { db.execute(); db.reset(); }
906951

@@ -928,7 +973,7 @@ namespace sqlite {
928973
if(!ctxt) return;
929974
try {
930975
if(!ctxt->constructed) new(ctxt) AggregateCtxt<ContextType>();
931-
step<Count, Functions>(db, count, vals, ctxt->obj);
976+
step<Count, Functions>(db, count, vals, ctxt->obj);
932977
return;
933978
} catch(sqlite_exception &e) {
934979
sqlite3_result_error_code(db, e.get_code());
@@ -939,7 +984,7 @@ namespace sqlite {
939984
sqlite3_result_error(db, "Unknown error", -1);
940985
}
941986
if(ctxt && ctxt->constructed)
942-
ctxt->~AggregateCtxt();
987+
ctxt->~AggregateCtxt();
943988
}
944989

945990
template<
@@ -999,7 +1044,7 @@ namespace sqlite {
9991044
sqlite3_result_error(db, "Unknown error", -1);
10001045
}
10011046
if(ctxt && ctxt->constructed)
1002-
ctxt->~AggregateCtxt();
1047+
ctxt->~AggregateCtxt();
10031048
}
10041049

10051050
template<
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#pragma once
2+
3+
#include <sqlite3.h>
4+
#include <optional>
5+
#include <variant>
6+
7+
namespace sqlite::utility {
8+
template<typename ...Options>
9+
struct VariantFirstNullable {
10+
using type = void;
11+
};
12+
template<typename T, typename ...Options>
13+
struct VariantFirstNullable<T, Options...> {
14+
using type = typename VariantFirstNullable<Options...>::type;
15+
};
16+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
17+
template<typename T, typename ...Options>
18+
struct VariantFirstNullable<std::optional<T>, Options...> {
19+
using type = std::optional<T>;
20+
};
21+
#endif
22+
template<typename T, typename ...Options>
23+
struct VariantFirstNullable<std::unique_ptr<T>, Options...> {
24+
using type = std::unique_ptr<T>;
25+
};
26+
template<typename ...Options>
27+
struct VariantFirstNullable<std::nullptr_t, Options...> {
28+
using type = std::nullptr_t;
29+
};
30+
template<typename Callback, typename ...Options>
31+
inline void variant_select_null(Callback&&callback) {
32+
if constexpr(std::is_same_v<typename VariantFirstNullable<Options...>::type, void>) {
33+
throw exceptions::mismatch("NULL is unsupported by this variant.", "", SQLITE_MISMATCH);
34+
} else {
35+
std::forward<Callback>(callback)(typename VariantFirstNullable<Options...>::type());
36+
}
37+
}
38+
39+
template<typename ...Options>
40+
struct VariantFirstIntegerable {
41+
using type = void;
42+
};
43+
template<typename T, typename ...Options>
44+
struct VariantFirstIntegerable<T, Options...> {
45+
using type = typename VariantFirstIntegerable<Options...>::type;
46+
};
47+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
48+
template<typename T, typename ...Options>
49+
struct VariantFirstIntegerable<std::optional<T>, Options...> {
50+
using type = std::conditional_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstIntegerable<Options...>::type>;
51+
};
52+
#endif
53+
template<typename T, typename ...Options>
54+
struct VariantFirstIntegerable<std::enable_if_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>>, std::unique_ptr<T>, Options...> {
55+
using type = std::conditional_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstIntegerable<Options...>::type>;
56+
};
57+
template<typename ...Options>
58+
struct VariantFirstIntegerable<int, Options...> {
59+
using type = int;
60+
};
61+
template<typename ...Options>
62+
struct VariantFirstIntegerable<sqlite_int64, Options...> {
63+
using type = sqlite_int64;
64+
};
65+
template<typename Callback, typename ...Options>
66+
inline auto variant_select_integer(Callback&&callback) {
67+
if constexpr(std::is_same_v<typename VariantFirstIntegerable<Options...>::type, void>) {
68+
throw exceptions::mismatch("Integer is unsupported by this variant.", "", SQLITE_MISMATCH);
69+
} else {
70+
std::forward<Callback>(callback)(typename VariantFirstIntegerable<Options...>::type());
71+
}
72+
}
73+
74+
template<typename ...Options>
75+
struct VariantFirstFloatable {
76+
using type = void;
77+
};
78+
template<typename T, typename ...Options>
79+
struct VariantFirstFloatable<T, Options...> {
80+
using type = typename VariantFirstFloatable<Options...>::type;
81+
};
82+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
83+
template<typename T, typename ...Options>
84+
struct VariantFirstFloatable<std::optional<T>, Options...> {
85+
using type = std::conditional_t<std::is_same_v<typename VariantFirstFloatable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstFloatable<Options...>::type>;
86+
};
87+
#endif
88+
template<typename T, typename ...Options>
89+
struct VariantFirstFloatable<std::unique_ptr<T>, Options...> {
90+
using type = std::conditional_t<std::is_same_v<typename VariantFirstFloatable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstFloatable<Options...>::type>;
91+
};
92+
template<typename ...Options>
93+
struct VariantFirstFloatable<float, Options...> {
94+
using type = float;
95+
};
96+
template<typename ...Options>
97+
struct VariantFirstFloatable<double, Options...> {
98+
using type = double;
99+
};
100+
template<typename Callback, typename ...Options>
101+
inline auto variant_select_float(Callback&&callback) {
102+
if constexpr(std::is_same_v<typename VariantFirstFloatable<Options...>::type, void>) {
103+
throw exceptions::mismatch("Real is unsupported by this variant.", "", SQLITE_MISMATCH);
104+
} else {
105+
std::forward<Callback>(callback)(typename VariantFirstFloatable<Options...>::type());
106+
}
107+
}
108+
109+
template<typename ...Options>
110+
struct VariantFirstTextable {
111+
using type = void;
112+
};
113+
template<typename T, typename ...Options>
114+
struct VariantFirstTextable<T, Options...> {
115+
using type = typename VariantFirstTextable<void, Options...>::type;
116+
};
117+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
118+
template<typename T, typename ...Options>
119+
struct VariantFirstTextable<std::optional<T>, Options...> {
120+
using type = std::conditional_t<std::is_same_v<typename VariantFirstTextable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstTextable<Options...>::type>;
121+
};
122+
#endif
123+
template<typename T, typename ...Options>
124+
struct VariantFirstTextable<std::unique_ptr<T>, Options...> {
125+
using type = std::conditional_t<std::is_same_v<typename VariantFirstTextable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstTextable<Options...>::type>;
126+
};
127+
template<typename ...Options>
128+
struct VariantFirstTextable<std::string, Options...> {
129+
using type = std::string;
130+
};
131+
template<typename ...Options>
132+
struct VariantFirstTextable<std::u16string, Options...> {
133+
using type = std::u16string;
134+
};
135+
template<typename Callback, typename ...Options>
136+
inline void variant_select_text(Callback&&callback) {
137+
if constexpr(std::is_same_v<typename VariantFirstTextable<Options...>::type, void>) {
138+
throw exceptions::mismatch("Text is unsupported by this variant.", "", SQLITE_MISMATCH);
139+
} else {
140+
std::forward<Callback>(callback)(typename VariantFirstTextable<Options...>::type());
141+
}
142+
}
143+
144+
template<typename ...Options>
145+
struct VariantFirstBlobable {
146+
using type = void;
147+
};
148+
template<typename T, typename ...Options>
149+
struct VariantFirstBlobable<T, Options...> {
150+
using type = typename VariantFirstBlobable<Options...>::type;
151+
};
152+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
153+
template<typename T, typename ...Options>
154+
struct VariantFirstBlobable<std::optional<T>, Options...> {
155+
using type = std::conditional_t<std::is_same_v<typename VariantFirstBlobable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstBlobable<Options...>::type>;
156+
};
157+
#endif
158+
template<typename T, typename ...Options>
159+
struct VariantFirstBlobable<std::unique_ptr<T>, Options...> {
160+
using type = std::conditional_t<std::is_same_v<typename VariantFirstBlobable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstBlobable<Options...>::type>;
161+
};
162+
template<typename T, typename ...Options>
163+
struct VariantFirstBlobable<std::enable_if_t<std::is_pod_v<T>>, std::vector<T>, Options...> {
164+
using type = std::vector<T>;
165+
};
166+
template<typename Callback, typename ...Options>
167+
inline auto variant_select_blob(Callback&&callback) {
168+
if constexpr(std::is_same_v<typename VariantFirstBlobable<Options...>::type, void>) {
169+
throw exceptions::mismatch("Blob is unsupported by this variant.", "", SQLITE_MISMATCH);
170+
} else {
171+
std::forward<Callback>(callback)(typename VariantFirstBlobable<Options...>::type());
172+
}
173+
}
174+
175+
template<typename ...Options>
176+
inline auto variant_select(int type) {
177+
return [type](auto &&callback) {
178+
using Callback = decltype(callback);
179+
switch(type) {
180+
case SQLITE_NULL:
181+
variant_select_null<Callback, Options...>(std::forward<Callback>(callback));
182+
break;
183+
case SQLITE_INTEGER:
184+
variant_select_integer<Callback, Options...>(std::forward<Callback>(callback));
185+
break;
186+
case SQLITE_FLOAT:
187+
variant_select_float<Callback, Options...>(std::forward<Callback>(callback));
188+
break;
189+
case SQLITE_TEXT:
190+
variant_select_text<Callback, Options...>(std::forward<Callback>(callback));
191+
break;
192+
case SQLITE_BLOB:
193+
variant_select_blob<Callback, Options...>(std::forward<Callback>(callback));
194+
break;
195+
default:;
196+
/* assert(false); */
197+
}
198+
};
199+
}
200+
}

0 commit comments

Comments
 (0)