-
Notifications
You must be signed in to change notification settings - Fork 20
/
connection_create_function.impl.hpp
155 lines (130 loc) · 5.03 KB
/
connection_create_function.impl.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Implementation of connection::create_function
#if !defined(SQXX_CONNECTION_CREATE_FUNCTION_IMPL_HPP_INCLUDED)
#define SQXX_CONNECTION_CREATE_FUNCTION_IMPL_HPP_INCLUDED
// Function types with C calling convention for callbacks
// https://stackoverflow.com/a/5590050/
extern "C" typedef void sqxx_function_call_type(sqlite3_context*, int, sqlite3_value**);
namespace sqxx {
namespace detail {
// Create a SQL function from a callable object
template<typename Callable>
sqxx_function_call_type function_call_ptr;
template<typename Callable>
void function_call_ptr(sqlite3_context *handle, int argc, sqlite3_value** argv) {
typedef callable_traits<Callable> traits;
context ctx(handle);
Callable *callable = reinterpret_cast<Callable*>(sqlite3_user_data(handle));
try {
if (argc != traits::argc) {
ctx.result_error_misuse();
return;
}
ctx.result<typename traits::return_type>(
apply_value_array<traits::argc, typename traits::return_type>(*callable, argv)
);
}
catch (const error &e) {
ctx.result_error_code(e.code);
}
catch (const std::bad_alloc &) {
ctx.result_error_nomem();
}
catch (const std::exception &e) {
ctx.result_error(e.what());
}
catch (...) {
ctx.result_error_misuse();
}
}
// Create a SQL function from a statically known function pointer
template<typename Function, Function *Fun>
sqxx_function_call_type function_call_staticfptr;
template<typename Function, Function *Fun>
void function_call_staticfptr(sqlite3_context *handle, int argc, sqlite3_value** argv) {
typedef std::remove_pointer_t<std::decay_t<Function>> FunctionType;
typedef callable_traits<FunctionType> traits;
context ctx(handle);
try {
if (argc != traits::argc) {
ctx.result_error_misuse();
return;
}
ctx.result<typename traits::return_type>(
apply_value_array<traits::argc, typename traits::return_type>(Fun, argv)
);
}
catch (const error &e) {
ctx.result_error_code(e.code);
}
catch (const std::bad_alloc &) {
ctx.result_error_nomem();
}
catch (const std::exception &e) {
ctx.result_error(e.what());
}
catch (...) {
ctx.result_error_misuse();
}
}
void create_function_register(sqlite3 *handle, const char *name, int narg, void *data,
sqxx_function_call_type *fun, sqxx_appdata_destroy_type *destroy);
template<typename Function>
std::enable_if_t<detail::decays_to_function_v<Function>, void>
create_function_dispatch(sqlite3 *handle, const char *name, Function fun) {
typedef std::remove_pointer_t<std::decay_t<Function>> FunctionType;
typedef callable_traits<FunctionType> traits;
// We store the function pointer as sqlite user data.
// There is no special cleanup necessary for this user data.
FunctionType *fptr = fun;
detail::create_function_register(handle, name, traits::argc,
reinterpret_cast<void*>(fptr), detail::function_call_ptr<FunctionType>, nullptr);
}
template<typename Callable>
std::enable_if_t<!detail::decays_to_function_v<Callable>, void>
create_function_dispatch(sqlite3 *handle, const char *name, Callable &&callable) {
typedef std::decay_t<Callable> CallableType;
typedef callable_traits<CallableType> traits;
// We store a copy of the callable as sqlite user data.
// We register a "destructor" that deletes this copy when the function
// gets removed.
CallableType *cptr = new CallableType(std::forward<Callable>(callable));
detail::create_function_register(handle, name, traits::argc,
reinterpret_cast<void*>(cptr), detail::function_call_ptr<CallableType>, detail::appdata_destroy_object<CallableType>);
}
} // namespace detail
template<typename Callable>
void connection::create_function(const char *name, Callable &&callable) {
detail::create_function_dispatch<Callable>(handle, name, std::forward<Callable>(callable));
}
// A function specified as a template parameter
template<typename Function, Function *Fun>
void connection::create_function(const char *name) {
typedef std::remove_pointer_t<std::decay_t<Function>> FunctionType;
typedef callable_traits<FunctionType> traits;
// Function type is known statically and we don't need to store any
// special sqlite user data.
detail::create_function_register(handle, name, traits::argc,
nullptr, detail::function_call_staticfptr<Function, Fun>, nullptr);
}
template<typename Function, Function *Fun>
void connection::create_function(const std::string &name) {
create_function<Function, Fun>(name.c_str());
}
// We could add overloads that allow removing a function by not explicitly specifying
// it's arity. But is that really usefull?
//
// A call like `remove_function("foo", some_fun)` might be misleading, since it wouldn't
// just remove `some_fun` if it's registered, but any "foo" function with the same arity.
//
//template<typename Callable>
//void connection::remove_function(const char *name, Callable &&/*callable*/) {
// remove_function(name, NArgs);
//}
//
//template<typename Function>
//void connection::remove_function(const char *name) {
// constexpr int NArgs = callable_traits<std::decay_t<Callable>>::cargs;
// remove_function<Function>(name);
//}
} // namespace sqxx
#endif // SQXX_CONNECTION_CREATE_FUNCTION_IMPL_HPP_INCLUDED