Skip to content

Commit

Permalink
Merge pull request #111 from zauguin/logging2
Browse files Browse the repository at this point in the history
Error logging
  • Loading branch information
zauguin committed May 13, 2017
2 parents e05aa4b + 317e2e1 commit 56af65d
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 0 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,30 @@ Additionally you can use `get_sql()` to see the SQL statement leading to the err
catch(sqlite::errors::constraint_primarykey e) { } */
```
You can also register a error logging function with `sqlite::error_log`.
The `<sqlite_modern_cpp/log.h>` header has to be included to make this function available.
The call to `sqlite::error_log` has to be the first call to any `sqlite_modern_cpp` function by your program.
```c++
error_log(
[&](sqlite_exception& e) {
cerr << e.get_code() << ": " << e.what() << endl;
},
[&](errors::misuse& e) {
/* You can behave differently to specific errors */
}
);
database db(":memory:");
db << "create table person (id integer primary key not null, name text);";
try {
db << "insert into person (id, name) values (?,?)" << 1 << "jack";
// inserting again to produce error
db << "insert into person (id, name) values (?,?)" << 1 << "jack";
}
catch (sqlite_exception& e) {}
```

Custom SQL functions
----

Expand Down
101 changes: 101 additions & 0 deletions hdr/sqlite_modern_cpp/log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "errors.h"

#include <sqlite3.h>

#include <utility>
#include <tuple>
#include <type_traits>

namespace sqlite {
namespace detail {
template<class>
using void_t = void;
template<class T, class = void>
struct is_callable : std::false_type {};
template<class Functor, class ...Arguments>
struct is_callable<Functor(Arguments...), void_t<decltype(std::declval<Functor>()(std::declval<Arguments>()...))>> : std::true_type {};
template<class Functor, class ...Functors>
class FunctorOverload: public Functor, public FunctorOverload<Functors...> {
public:
template<class Functor1, class ...Remaining>
FunctorOverload(Functor1 &&functor, Remaining &&... remaining):
Functor(std::forward<Functor1>(functor)),
FunctorOverload<Functors...>(std::forward<Remaining>(remaining)...) {}
using Functor::operator();
using FunctorOverload<Functors...>::operator();
};
template<class Functor>
class FunctorOverload<Functor>: public Functor {
public:
template<class Functor1>
FunctorOverload(Functor1 &&functor):
Functor(std::forward<Functor1>(functor)) {}
using Functor::operator();
};
template<class Functor>
class WrapIntoFunctor: public Functor {
public:
template<class Functor1>
WrapIntoFunctor(Functor1 &&functor):
Functor(std::forward<Functor1>(functor)) {}
using Functor::operator();
};
template<class ReturnType, class ...Arguments>
class WrapIntoFunctor<ReturnType(*)(Arguments...)> {
ReturnType(*ptr)(Arguments...);
public:
WrapIntoFunctor(ReturnType(*ptr)(Arguments...)): ptr(ptr) {}
ReturnType operator()(Arguments... arguments) { return (*ptr)(std::forward<Arguments>(arguments)...); }
};
inline void store_error_log_data_pointer(std::shared_ptr<void> ptr) {
static std::shared_ptr<void> stored;
stored = std::move(ptr);
}
template<class T>
std::shared_ptr<typename std::decay<T>::type> make_shared_inferred(T &&t) {
return std::make_shared<typename std::decay<T>::type>(std::forward<T>(t));
}
}
template<class Handler>
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler);
template<class Handler>
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler);
template<class ...Handler>
typename std::enable_if<sizeof...(Handler)>=2>::type
error_log(Handler &&...handler) {
return error_log(detail::FunctorOverload<detail::WrapIntoFunctor<typename std::decay<Handler>::type>...>(std::forward<Handler>(handler)...));
}
template<class Handler>
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler) {
return error_log(std::forward<Handler>(handler), [](const sqlite_exception&) {});
}
template<class Handler>
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler) {
auto ptr = detail::make_shared_inferred([handler = std::forward<Handler>(handler)](int error_code, const char *errstr) mutable {
switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \
derived \
default: handler(errors::name(errstr, "", error_code)); \
};break;
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
case SQLITE_ ## BASE ## _ ## SUB: \
handler(errors::base ## _ ## sub(errstr, "", error_code)); \
break;
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
default: handler(sqlite_exception(errstr, "", error_code)); \
}
});

sqlite3_config(SQLITE_CONFIG_LOG, (void(*)(void*,int,const char*))[](void *functor, int error_code, const char *errstr) {
(*static_cast<decltype(ptr.get())>(functor))(error_code, errstr);
}, ptr.get());
detail::store_error_log_data_pointer(std::move(ptr));
}
}
38 changes: 38 additions & 0 deletions tests/error_log.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <iostream>
#include <iomanip>
#include <string>
#include <memory>
#include <stdexcept>
#include <sqlite_modern_cpp.h>
#include <sqlite_modern_cpp/log.h>
using namespace sqlite;
using namespace std;


int main() {
bool error_detected = false;
error_log(
[&](errors::constraint) {
cerr << "Wrong error detected!" << endl;
},
[&](errors::constraint_primarykey e) {
cerr << e.get_code() << '/' << e.get_extended_code() << ": " << e.what() << endl;
error_detected = true;
}
);
database db(":memory:");
db << "CREATE TABLE person (id integer primary key not null, name TEXT);";

try {
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
// inserting again to produce error
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
} catch (errors::constraint& e) {
}

if(!error_detected) {
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}
35 changes: 35 additions & 0 deletions tests/error_log2.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <iostream>
#include <iomanip>
#include <string>
#include <memory>
#include <stdexcept>
#include <sqlite_modern_cpp.h>
#include <sqlite_modern_cpp/log.h>
using namespace sqlite;
using namespace std;


int main() {
bool error_detected = false;
error_log(
[&](errors::constraint e) {
cerr << e.get_code() << '/' << e.get_extended_code() << ": " << e.what() << endl;
error_detected = true;
}
);
database db(":memory:");
db << "CREATE TABLE person (id integer primary key not null, name TEXT);";

try {
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
// inserting again to produce error
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
} catch (errors::constraint& e) {
}

if(!error_detected) {
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}

0 comments on commit 56af65d

Please sign in to comment.