Skip to content

Commit 56af65d

Browse files
authored
Merge pull request #111 from zauguin/logging2
Error logging
2 parents e05aa4b + 317e2e1 commit 56af65d

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

README.md

+24
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,30 @@ Additionally you can use `get_sql()` to see the SQL statement leading to the err
402402
catch(sqlite::errors::constraint_primarykey e) { } */
403403
```
404404
405+
You can also register a error logging function with `sqlite::error_log`.
406+
The `<sqlite_modern_cpp/log.h>` header has to be included to make this function available.
407+
The call to `sqlite::error_log` has to be the first call to any `sqlite_modern_cpp` function by your program.
408+
409+
```c++
410+
error_log(
411+
[&](sqlite_exception& e) {
412+
cerr << e.get_code() << ": " << e.what() << endl;
413+
},
414+
[&](errors::misuse& e) {
415+
/* You can behave differently to specific errors */
416+
}
417+
);
418+
database db(":memory:");
419+
db << "create table person (id integer primary key not null, name text);";
420+
421+
try {
422+
db << "insert into person (id, name) values (?,?)" << 1 << "jack";
423+
// inserting again to produce error
424+
db << "insert into person (id, name) values (?,?)" << 1 << "jack";
425+
}
426+
catch (sqlite_exception& e) {}
427+
```
428+
405429
Custom SQL functions
406430
----
407431

hdr/sqlite_modern_cpp/log.h

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#include "errors.h"
2+
3+
#include <sqlite3.h>
4+
5+
#include <utility>
6+
#include <tuple>
7+
#include <type_traits>
8+
9+
namespace sqlite {
10+
namespace detail {
11+
template<class>
12+
using void_t = void;
13+
template<class T, class = void>
14+
struct is_callable : std::false_type {};
15+
template<class Functor, class ...Arguments>
16+
struct is_callable<Functor(Arguments...), void_t<decltype(std::declval<Functor>()(std::declval<Arguments>()...))>> : std::true_type {};
17+
template<class Functor, class ...Functors>
18+
class FunctorOverload: public Functor, public FunctorOverload<Functors...> {
19+
public:
20+
template<class Functor1, class ...Remaining>
21+
FunctorOverload(Functor1 &&functor, Remaining &&... remaining):
22+
Functor(std::forward<Functor1>(functor)),
23+
FunctorOverload<Functors...>(std::forward<Remaining>(remaining)...) {}
24+
using Functor::operator();
25+
using FunctorOverload<Functors...>::operator();
26+
};
27+
template<class Functor>
28+
class FunctorOverload<Functor>: public Functor {
29+
public:
30+
template<class Functor1>
31+
FunctorOverload(Functor1 &&functor):
32+
Functor(std::forward<Functor1>(functor)) {}
33+
using Functor::operator();
34+
};
35+
template<class Functor>
36+
class WrapIntoFunctor: public Functor {
37+
public:
38+
template<class Functor1>
39+
WrapIntoFunctor(Functor1 &&functor):
40+
Functor(std::forward<Functor1>(functor)) {}
41+
using Functor::operator();
42+
};
43+
template<class ReturnType, class ...Arguments>
44+
class WrapIntoFunctor<ReturnType(*)(Arguments...)> {
45+
ReturnType(*ptr)(Arguments...);
46+
public:
47+
WrapIntoFunctor(ReturnType(*ptr)(Arguments...)): ptr(ptr) {}
48+
ReturnType operator()(Arguments... arguments) { return (*ptr)(std::forward<Arguments>(arguments)...); }
49+
};
50+
inline void store_error_log_data_pointer(std::shared_ptr<void> ptr) {
51+
static std::shared_ptr<void> stored;
52+
stored = std::move(ptr);
53+
}
54+
template<class T>
55+
std::shared_ptr<typename std::decay<T>::type> make_shared_inferred(T &&t) {
56+
return std::make_shared<typename std::decay<T>::type>(std::forward<T>(t));
57+
}
58+
}
59+
template<class Handler>
60+
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
61+
error_log(Handler &&handler);
62+
template<class Handler>
63+
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
64+
error_log(Handler &&handler);
65+
template<class ...Handler>
66+
typename std::enable_if<sizeof...(Handler)>=2>::type
67+
error_log(Handler &&...handler) {
68+
return error_log(detail::FunctorOverload<detail::WrapIntoFunctor<typename std::decay<Handler>::type>...>(std::forward<Handler>(handler)...));
69+
}
70+
template<class Handler>
71+
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
72+
error_log(Handler &&handler) {
73+
return error_log(std::forward<Handler>(handler), [](const sqlite_exception&) {});
74+
}
75+
template<class Handler>
76+
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
77+
error_log(Handler &&handler) {
78+
auto ptr = detail::make_shared_inferred([handler = std::forward<Handler>(handler)](int error_code, const char *errstr) mutable {
79+
switch(error_code & 0xFF) {
80+
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
81+
case SQLITE_ ## NAME: switch(error_code) { \
82+
derived \
83+
default: handler(errors::name(errstr, "", error_code)); \
84+
};break;
85+
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
86+
case SQLITE_ ## BASE ## _ ## SUB: \
87+
handler(errors::base ## _ ## sub(errstr, "", error_code)); \
88+
break;
89+
#include "lists/error_codes.h"
90+
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
91+
#undef SQLITE_MODERN_CPP_ERROR_CODE
92+
default: handler(sqlite_exception(errstr, "", error_code)); \
93+
}
94+
});
95+
96+
sqlite3_config(SQLITE_CONFIG_LOG, (void(*)(void*,int,const char*))[](void *functor, int error_code, const char *errstr) {
97+
(*static_cast<decltype(ptr.get())>(functor))(error_code, errstr);
98+
}, ptr.get());
99+
detail::store_error_log_data_pointer(std::move(ptr));
100+
}
101+
}

tests/error_log.cc

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <iostream>
2+
#include <iomanip>
3+
#include <string>
4+
#include <memory>
5+
#include <stdexcept>
6+
#include <sqlite_modern_cpp.h>
7+
#include <sqlite_modern_cpp/log.h>
8+
using namespace sqlite;
9+
using namespace std;
10+
11+
12+
int main() {
13+
bool error_detected = false;
14+
error_log(
15+
[&](errors::constraint) {
16+
cerr << "Wrong error detected!" << endl;
17+
},
18+
[&](errors::constraint_primarykey e) {
19+
cerr << e.get_code() << '/' << e.get_extended_code() << ": " << e.what() << endl;
20+
error_detected = true;
21+
}
22+
);
23+
database db(":memory:");
24+
db << "CREATE TABLE person (id integer primary key not null, name TEXT);";
25+
26+
try {
27+
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
28+
// inserting again to produce error
29+
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
30+
} catch (errors::constraint& e) {
31+
}
32+
33+
if(!error_detected) {
34+
exit(EXIT_FAILURE);
35+
}
36+
37+
exit(EXIT_SUCCESS);
38+
}

tests/error_log2.cc

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <iostream>
2+
#include <iomanip>
3+
#include <string>
4+
#include <memory>
5+
#include <stdexcept>
6+
#include <sqlite_modern_cpp.h>
7+
#include <sqlite_modern_cpp/log.h>
8+
using namespace sqlite;
9+
using namespace std;
10+
11+
12+
int main() {
13+
bool error_detected = false;
14+
error_log(
15+
[&](errors::constraint e) {
16+
cerr << e.get_code() << '/' << e.get_extended_code() << ": " << e.what() << endl;
17+
error_detected = true;
18+
}
19+
);
20+
database db(":memory:");
21+
db << "CREATE TABLE person (id integer primary key not null, name TEXT);";
22+
23+
try {
24+
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
25+
// inserting again to produce error
26+
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
27+
} catch (errors::constraint& e) {
28+
}
29+
30+
if(!error_detected) {
31+
exit(EXIT_FAILURE);
32+
}
33+
34+
exit(EXIT_SUCCESS);
35+
}

0 commit comments

Comments
 (0)