-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathemacs-module-wrapper-template.tcc
155 lines (130 loc) · 5.64 KB
/
emacs-module-wrapper-template.tcc
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
155
// This is a library to ease building Emacs modules. Generally, when
// you register module functions with Emacs, you have to write logic
// to unpack & validate the parameters from an array of emacs_value
// types. This can lead to a lot of boiler plate if you are
// registering several different functions, so this library uses
// templates to generate the wrapper functions that call into the
// function you provide.
#ifndef __EMACS_MODULE_WRAPPER_TEMPLATE__
#define __EMACS_MODULE_WRAPPER_TEMPLATE__
#include <array>
#include <iostream>
#include <vector>
#include <emacs-module.h>
#include <os/log.h>
#include <os/signpost.h>
#include "function-traits.tcc"
#include "parameter-validation.tcc"
using namespace std;
os_log_t logger = os_log_create("com.nealsid.emacs.emwt", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
template<typename T>
struct TD;
template <typename... Args>
struct generate_argument_indices {
static int parameterIndex;
static constexpr initializer_list<int> values{
([] () {
if constexpr (is_same_v<Args, emacs_env*>) {
return -1;
} else {
return 0;
}
}()) ...
};
};
template<typename... Args>
int generate_argument_indices<Args...>::parameterIndex = 0;
template<typename... Args>
inline constexpr auto generate_argument_indices_vs = generate_argument_indices<Args...>::values;
template <typename F>
struct EmacsCallableBase;
template <typename R, typename... Args>
struct EmacsCallableBase<R(*)(Args...)> {
tuple<Args...> unpackedArgs;
vector<char *> pointersToDelete;
vector<int> argument_indices{generate_argument_indices_vs<Args...>};
auto unpackArguments(emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept -> void {
int argNumber = 0;
// When we generate code to unpack arguments, most of the user
// function arguments come from the args array that Emacs gives
// us. However, two don't: the emacs_env pointer, and the void*
// user data pointer. So we have to make sure that we don't
// increment argNumber or look into the args array in the generated
// code that passes those parameters, and, instead, just pass along
// whatever Emacs gives us.
unpackedArgs = {
(([&] () {
if (argNumber < nargs) {
auto ret = ValidateParameterFromElisp<Args>{}(env, args[argNumber], data);
cout << argument_indices[argNumber] << "\t";
argNumber += (int)(!is_type_any_of_v<Args, void*, emacs_env*>);
// If argNumber < nargs and this parameter is optional,
// it has to be specified.
if constexpr (is_optional_type_v<Args>) {
assert(ret);
}
if constexpr (is_same_v<Args, string_view>) {
pointersToDelete.push_back(const_cast<char*>(ret.data()));
}
if constexpr (is_same_v<Args, optional<string_view>>) {
pointersToDelete.push_back(const_cast<char*>(ret.value().data()));
}
return ret;
} else {
return Args(); // This is a little sketchy, but we only
// get here at runtime when the argument
// type is optional<T> and the argument has
// not been passed by the elisp caller. A
// default-constructed optional to
// represent an unset argument is what we
// require. TODO: There are potential
// extra copies here to look into remove.
}
} ())) ...
};
cout << endl;
}
auto cleanup() -> void {
for (auto ptr : pointersToDelete) {
delete [] ptr;
}
pointersToDelete.clear();
}
};
template <auto F>
struct EmacsCallable : EmacsCallableBase<decltype(F)> {
using function_traits = FunctionTraits<decltype(F)>;
using parameter_traits = typename function_traits::ParameterTraits;
static constexpr size_t requiredParameterCount =
parameter_traits::parameterCount - parameter_traits::optionalParameterCount;
static_assert(parameter_traits::allOptionalParametersTrailing, "Optional parameters must be trailing");
emacs_funcall_exit defineInEmacs(struct emacs_runtime *runtime, const char* lisp_function_name,
const char* documentation, void* data,
emacs_value (*fn)(emacs_env*, ptrdiff_t, emacs_value*, void*) noexcept) {
emacs_env* env = runtime->get_environment(runtime);
emacs_value func = env->make_function(env,
requiredParameterCount,
parameter_traits::parameterCount,
fn,
documentation,
data);
emacs_value symbol = env->intern(env, lisp_function_name);
emacs_value args[] = { symbol, func };
emacs_value defalias = env->intern(env, "defalias");
env->funcall(env, defalias, 2, args);
return env->non_local_exit_check(env);
}
auto operator()(emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept -> typename function_traits::RetType {
os_signpost_interval_begin(logger, OS_SIGNPOST_ID_EXCLUSIVE, "Function call");
this->unpackArguments(env, nargs, args, data);
auto x = std::apply(F, this->unpackedArgs);
this->cleanup();
os_signpost_interval_end(logger, OS_SIGNPOST_ID_EXCLUSIVE, "Function call");
return x;
}
};
template <auto C>
auto elispCallableFunction(emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept -> emacs_value {
return (*C)(env, nargs, args, data);
}
#endif // __EMACS_MODULE_WRAPPER_TEMPLATE__