-
Notifications
You must be signed in to change notification settings - Fork 0
/
espeak-ng.hpp
213 lines (199 loc) · 6.79 KB
/
espeak-ng.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#pragma once
// External dependencies.
#include <espeak-ng/espeak_ng.h>
// Standard C++ libraries.
#include <stdexcept>
// Standard C libraries.
#include <cassert>
#include <cstdio>
/** \brief RAII wrappers for eSpeak NG functions.
*
* While wrapping the entire C API of eSpeak NG
* in a C++ API may be possible, it is unnecessary.
* The aim of interfaces in this namespace
* is to provide resource safety and reduce common scaffolding.
*/
namespace espeak_ng {
/** \brief Throw an exception if `code_to_check` indicates an error.
*
* \param code_to_check
* A return value from some eSpeak NG functions.
* \param current_error_context
* Extra information related to the error if not `nullptr`.
* The context is _not_ cleared before `throw`-ing.
* \exception std::runtime_error
* If an error is detected.
*
* If there is an error,
* the message corresponding to the error is written to `stderr`.
* An exception is then thrown after the message is written.
*
* \par Usage
* It is provided for use with `espeak_ng::error_context`
* to simplify error handling.
* ```cpp
* {
* // When this context goes out of scope,
* // the underlying resources will be released automatically.
* // This includes if an error is thrown, thereby guarantee clean-up.
* espeak_ng::error_context initialisation_error;
*
* // This class provides implicit conversion
* // to `espeak_ng_ERROR_CONTEXT*` for convenience
* // to be used with eSpeak NG C API.
* espeak_ng::throw_if_not_ok(
* espeak_ng_Initialize(initialisation_error),
* *initialisation_error);
*
* // This is not called if there is an error.
* // but clean-up of the error context is guaranteed by destructor
* // even in case of a `throw`-n exception.
* espeak_ng_Terminate();
* }
* ```
*/
void throw_if_not_ok(
espeak_ng_STATUS code_to_check,
espeak_ng_ERROR_CONTEXT current_error_context = nullptr) {
if (code_to_check != ENS_OK) {
espeak_ng_PrintStatusCodeMessage(
code_to_check, stderr, current_error_context);
throw std::runtime_error("eSpeak-NG error");
}
}
/** \brief Provides RAII for eSpeak NG error context.
*
* \par Purpose
* Some eSpeak NG functions
* contain an `OUT` `espeak_ng_ERROR_CONTEXT*` parameter
* to provide additional information when they encounter an error.
* And if an error is encountered,
* the argument given to the function must release the resources
* allocated for the `espeak_ng_ERROR_CONTEXT`
* using the function `espeak_ng_ClearErrorContext`.
* This class ensures that the function will be called.
*
* \par Usage
* Since instances of this class is expected to be used with the C API,
* the interface of this class is modelled after
* a pointer to `espeak_ng_ERROR_CONTEXT`
* instead of the error context itself.
* This means that, for most usage, instances of this class can be seen
* as a managed `espeak_ng_ERROR_CONTEXT*` pointer.
* To provide a limited pointer interface,
* an implicit conversion
* with `error_context::operator espeak_ng_ERROR_CONTEXT*()`
* gives a pointer to the stored `espeak_ng_ERROR_CONTEXT`.
* To provide an interface expected of a pointer,
* this class also provides the indirection `error_context::operator*`
* to "dereference" the implicit conversion.
* That is, it returns
* the stored `espeak_ng_ERROR_CONTEXT` `error_context::data` value.
* ```cpp
* {
* // When this context goes out of scope,
* // the underlying resources will be released automatically.
* espeak_ng::error_context initialisation_error;
*
* // This class provides implicit conversion
* // to `espeak_ng_ERROR_CONTEXT*` for convenience
* // to be used with eSpeak NG C API.
* auto code_to_check = espeak_ng_Initialize(initialisation_error);
*
* if (code_to_check != ENS_OK) {
* // Dereferenceing with the indirection operator
* // dereferences the implicit conversion,
* // thereby retrieving the underlying stored context.
* espeak_ng_PrintStatusCodeMessage(
* code_to_check, stderr, *initialisation_error);
*
* // When this excepion is `throw`-n,
* // stack unwinding will call destructor of this class
* // and release resources.
* throw;
* }
* espeak_ng_Terminate();
* }
* ```
*
* \see
* The function `espeak_ng::throw_if_not_ok` for
* possible further simplication.
*/
class error_context {
public:
/** \brief The stored eSpeak NG error context.
*
* Normal usage should not need to directly access this member.
* There is implicit conversion
* with `error_context::operator espeak_ng_ERROR_CONTEXT*()`
* to directly use `error_context` with eSpeak NG C API,
* and indirection `error_context::operator*`
* to access this `error_context::data`.
*/
espeak_ng_ERROR_CONTEXT data{nullptr};
/** \brief Ensures resources allocated to the context is released. */
~error_context() {
// It is safe to give `nullptr` and a pointer to `nullptr`
// to the release function.
espeak_ng_ClearErrorContext(&data);
}
/** \brief Implicit conversion to pointer to `error_context::data`.
*
* This is provided for convenience
* to be used in `OUT` parameters in the C API of eSpeak NG.
*/
operator espeak_ng_ERROR_CONTEXT*() {
assert(data == nullptr);
return &data;
}
/** \brief Returns the `error_context::data` value itself.
*
* Ownership of the error context remains with `this`.
* The returned value should be treated as a raw pointer
* (and it is a raw pointer in implementation)
* in the sense that no ownership is transferred.
*/
espeak_ng_ERROR_CONTEXT operator*() { return data; }
};
/** \brief Provides RAII for eSpeak NG initialisation.
*
* \par Purpose
* Usage of the C API of eSpeak NG begins with an `espeak_ng_Initialize`
* and ends with an `espeak_ng_Terminate`.
* This class ensures that the terminate function is called
* if the initialisation was successful.
*
* \par Usage
* Note that eSpeak NG must be given a path
* to its data before initialisation.
* Otherwise initialisation fails.
* ```cpp
* {
* espeak_ng_InitializePath(nullptr);
* espeak_ng::service service;
* } // Calls `espeak_ng_Terminate` unless initialisation failed.
* ```
*/
class service {
public:
/** \brief Calls `espeak_ng_Initialize`.
*
* \exception std::runtime_error
* If initialisation fails.
*/
service() {
error_context initialisation_error;
throw_if_not_ok(
espeak_ng_Initialize(initialisation_error),
*initialisation_error);
}
/** \brief Calls `espeak_ng_Terminate`. */
~service() {
// The termination function returns an error code,
// but the function always succeeds.
// So there is no need to check it.
espeak_ng_Terminate();
}
};
} // namespace espeak_ng