Skip to content

Commit 44d141d

Browse files
committed
Improve error reporting by storing the last error message, uniform exception handling and helper functions
1 parent e762b9e commit 44d141d

11 files changed

+146
-232
lines changed

include/lsl/common.h

+2
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ typedef enum {
150150
_lsl_error_code_maxval = 0x7f000000
151151
} lsl_error_code_t;
152152

153+
/// Return an explanation for the last error
154+
extern LIBLSL_C_API const char *lsl_last_error(void);
153155

154156
/**
155157
* LSL version the binary was compiled against

include/lsl_cpp.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ class stream_info {
180180
double nominal_srate = IRREGULAR_RATE, channel_format_t channel_format = cf_float32,
181181
const std::string &source_id = std::string())
182182
: obj(lsl_create_streaminfo((name.c_str()), (type.c_str()), channel_count, nominal_srate,
183-
(lsl_channel_format_t)channel_format, (source_id.c_str()))) {}
183+
(lsl_channel_format_t)channel_format, (source_id.c_str()))) {
184+
if (obj == nullptr) throw std::invalid_argument(lsl_last_error());
185+
}
184186
stream_info(lsl_streaminfo handle) : obj(handle) {}
185187

186188

src/common.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
#pragma comment(lib, "winmm.lib")
1313
#endif
1414

15+
char last_error[512];
16+
1517
extern "C" {
18+
1619
LIBLSL_C_API int32_t lsl_protocol_version() {
1720
return lsl::api_config::get_instance()->use_protocol_version();
1821
}
@@ -28,6 +31,8 @@ LIBLSL_C_API double lsl_local_clock() {
2831
LIBLSL_C_API void lsl_destroy_string(char *s) {
2932
if (s) free(s);
3033
}
34+
35+
LIBLSL_C_API const char *lsl_last_error(void) { return last_error; }
3136
}
3237

3338
// === implementation of misc functions ===

src/common.h

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ extern "C" {
3131
"Please do not compile this with a lslboost version older than 1.45 because the library would otherwise not be protocol-compatible with builds using other versions."
3232
#endif
3333

34+
extern char last_error[512];
35+
3436
// the highest supported protocol version
3537
// * 100 is the original version, supported by library versions 1.00+
3638
// * 110 is an alternative protocol that improves throughput, supported by library versions 1.10+

src/lsl_c_api_helpers.hpp

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#pragma once
2+
3+
#include "common.h"
4+
#include <cstdint>
5+
#include <cstring>
6+
7+
/// Helper for LSL_STORE_EXCEPTION_IN
8+
#define LSLCATCHANDSTORE(ecvar, Exception, code) \
9+
catch (Exception & e) { \
10+
strncpy(last_error, e.what(), sizeof(last_error) - 1); \
11+
int32_t *__ec = ecvar; \
12+
if (__ec != nullptr) *__ec = code; \
13+
}
14+
15+
/// Helper for LSL_STORE_EXCEPTION_IN
16+
#define LSLCATCHANDRETURN(Exception, code) \
17+
catch (Exception & e) { \
18+
strncpy(last_error, e.what(), sizeof(last_error) - 1); \
19+
return code; \
20+
}
21+
22+
23+
/**
24+
* C API exception helper.
25+
*
26+
* Inserts `catch()` statements for stl and liblsl exceptions and sets the
27+
* variable pointed to by `ec` to the corresponding value from
28+
* `lsl_error_code_t` and copies the error message to `last_error`.
29+
*
30+
* If you're trying to create a function `lsl_foobar_do_bar` that wraps the `FooBar`'s member
31+
* function `do_bar`, this allows you to write
32+
*
33+
* ```
34+
* function lsl_foobar_do_bar(lsl_foobar obj, int x, double y, int32_t *ec) {
35+
* try {
36+
* obj->do_bar(x, y);
37+
* } LSL_STORE_EXCEPTION_IN(ec)
38+
* }
39+
* ```
40+
*
41+
* instead of
42+
*
43+
* ```
44+
* function lsl_foobar_do_bar(lsl_foobar obj, int x, double y, int32_t *ec) {
45+
* if(ec) *ec = lsl_no_error;
46+
* try {
47+
* return obj->do_bar(x, y);
48+
* }
49+
* catch(Ex1 &e) {
50+
* if(ec) *ec = lsl_error1;
51+
* strncpy(last_error, e.what(), sizeof(last_error-1);
52+
* }
53+
* catch(Ex2 &) {
54+
* if(ec) *ec = lsl_error2;
55+
* strncpy(last_error, e.what(), sizeof(last_error-1);
56+
* }
57+
* // etc.
58+
* return 0;
59+
* }
60+
* ```
61+
*/
62+
#define LSL_STORE_EXCEPTION_IN(ecvar) \
63+
LSLCATCHANDSTORE(ecvar, lsl::timeout_error, lsl_timeout_error) \
64+
LSLCATCHANDSTORE(ecvar, lsl::lost_error, lsl_lost_error) \
65+
LSLCATCHANDSTORE(ecvar, std::range_error, lsl_argument_error) \
66+
LSLCATCHANDSTORE(ecvar, std::invalid_argument, lsl_argument_error) \
67+
LSLCATCHANDSTORE(ecvar, std::exception, lsl_internal_error)
68+
69+
#define LSL_STORE_EXCEPTION LSL_STORE_EXCEPTION_IN(nullptr)
70+
71+
/// Catch possible exceptions and return an appropriate error code or `lsl_no_error`
72+
#define LSL_RETURN_CAUGHT_EC \
73+
LSLCATCHANDRETURN(lsl::timeout_error, lsl_timeout_error) \
74+
LSLCATCHANDRETURN(lsl::lost_error, lsl_lost_error) \
75+
LSLCATCHANDRETURN(std::range_error, lsl_argument_error) \
76+
LSLCATCHANDRETURN(std::invalid_argument, lsl_argument_error) \
77+
LSLCATCHANDRETURN(std::exception, lsl_internal_error) \
78+
return lsl_no_error
79+
80+
/// Try to create a new T object and return a pointer to it or `nullptr` if an exception occured.
81+
template <class Type, typename... T> Type *create_object_noexcept(T &&...args) noexcept {
82+
try {
83+
return new Type(std::forward<T>(args)...);
84+
}
85+
LSL_STORE_EXCEPTION_IN(nullptr)
86+
return nullptr;
87+
}

src/lsl_inlet_c.cpp

+13-109
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "lsl_c_api_helpers.hpp"
12
#include "stream_inlet_impl.h"
23

34
extern "C" {
@@ -9,20 +10,9 @@ using namespace lsl;
910

1011
LIBLSL_C_API lsl_inlet lsl_create_inlet(
1112
lsl_streaminfo info, int32_t max_buflen, int32_t max_chunklen, int32_t recover) {
12-
try {
13-
stream_info_impl *infoimpl = info;
14-
lsl_inlet result = new stream_inlet_impl(*infoimpl,
15-
infoimpl->nominal_srate() ? (int)(infoimpl->nominal_srate() * max_buflen)
16-
: max_buflen * 100,
17-
max_chunklen, recover != 0);
18-
return result;
19-
} catch (std::invalid_argument &e) {
20-
LOG_F(WARNING, "Error during construction of a stream_inlet: %s", e.what());
21-
return nullptr;
22-
} catch (std::exception &e) {
23-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
24-
return nullptr;
25-
}
13+
return create_object_noexcept<stream_inlet_impl>(*info,
14+
info->nominal_srate() ? (int)(info->nominal_srate() * max_buflen) : max_buflen * 100,
15+
max_chunklen, recover != 0);
2616
}
2717

2818
LIBLSL_C_API void lsl_destroy_inlet(lsl_inlet in) {
@@ -32,32 +22,14 @@ LIBLSL_C_API void lsl_destroy_inlet(lsl_inlet in) {
3222
}
3323

3424
LIBLSL_C_API lsl_streaminfo lsl_get_fullinfo(lsl_inlet in, double timeout, int32_t *ec) {
35-
if (ec) *ec = lsl_no_error;
36-
try {
37-
return new stream_info_impl(in->info(timeout));
38-
} catch (timeout_error &) {
39-
if (ec) *ec = lsl_timeout_error;
40-
} catch (lost_error &) {
41-
if (ec) *ec = lsl_lost_error;
42-
} catch (std::exception &e) {
43-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
44-
if (ec) *ec = lsl_internal_error;
45-
}
46-
return nullptr;
25+
return create_object_noexcept<stream_info_impl>(in->info(timeout));
4726
}
4827

4928
LIBLSL_C_API void lsl_open_stream(lsl_inlet in, double timeout, int32_t *ec) {
5029
if (ec) *ec = lsl_no_error;
5130
try {
5231
in->open_stream(timeout);
53-
} catch (timeout_error &) {
54-
if (ec) *ec = lsl_timeout_error;
55-
} catch (lost_error &) {
56-
if (ec) *ec = lsl_lost_error;
57-
} catch (std::exception &e) {
58-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
59-
if (ec) *ec = lsl_internal_error;
60-
}
32+
} LSL_STORE_EXCEPTION_IN(ec)
6133
}
6234

6335
LIBLSL_C_API void lsl_close_stream(lsl_inlet in) {
@@ -70,14 +42,7 @@ LIBLSL_C_API double lsl_time_correction(lsl_inlet in, double timeout, int32_t *e
7042
if (ec) *ec = lsl_no_error;
7143
try {
7244
return in->time_correction(timeout);
73-
} catch (timeout_error &) {
74-
if (ec) *ec = lsl_timeout_error;
75-
} catch (lost_error &) {
76-
if (ec) *ec = lsl_lost_error;
77-
} catch (std::exception &e) {
78-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
79-
if (ec) *ec = lsl_internal_error;
80-
}
45+
} LSL_STORE_EXCEPTION_IN(ec)
8146
return 0.0;
8247
}
8348

@@ -87,13 +52,7 @@ LIBLSL_C_API double lsl_time_correction_ex(
8752
try {
8853
double correction = in->time_correction(remote_time, uncertainty, timeout);
8954
return correction;
90-
} catch (timeout_error &) {
91-
if (ec) *ec = lsl_timeout_error;
92-
} catch (lost_error &) {
93-
if (ec) *ec = lsl_lost_error;
94-
} catch (std::exception &) {
95-
if (ec) *ec = lsl_internal_error;
96-
}
55+
} LSL_STORE_EXCEPTION_IN(ec)
9756
return 0.0;
9857
}
9958

@@ -160,18 +119,7 @@ LIBLSL_C_API double lsl_pull_sample_str(
160119
strcpy(buffer[k], tmp[k].c_str());
161120
}
162121
return result;
163-
} catch (timeout_error &) {
164-
if (ec) *ec = lsl_timeout_error;
165-
} catch (lost_error &) {
166-
if (ec) *ec = lsl_lost_error;
167-
} catch (std::invalid_argument &) {
168-
if (ec) *ec = lsl_argument_error;
169-
} catch (std::range_error &) {
170-
if (ec) *ec = lsl_argument_error;
171-
} catch (std::exception &e) {
172-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
173-
if (ec) *ec = lsl_internal_error;
174-
}
122+
} LSL_STORE_EXCEPTION_IN(ec)
175123
return 0.0;
176124
}
177125

@@ -197,18 +145,7 @@ LIBLSL_C_API double lsl_pull_sample_buf(lsl_inlet in, char **buffer, uint32_t *b
197145
memcpy(buffer[k], &tmp[k][0], tmp[k].size());
198146
}
199147
return result;
200-
} catch (timeout_error &) {
201-
if (ec) *ec = lsl_timeout_error;
202-
} catch (lost_error &) {
203-
if (ec) *ec = lsl_lost_error;
204-
} catch (std::invalid_argument &) {
205-
if (ec) *ec = lsl_argument_error;
206-
} catch (std::range_error &) {
207-
if (ec) *ec = lsl_argument_error;
208-
} catch (std::exception &e) {
209-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
210-
if (ec) *ec = lsl_internal_error;
211-
}
148+
} LSL_STORE_EXCEPTION_IN(ec)
212149
return 0.0;
213150
}
214151

@@ -217,18 +154,7 @@ LIBLSL_C_API double lsl_pull_sample_v(
217154
if (ec) *ec = lsl_no_error;
218155
try {
219156
return in->pull_numeric_raw(buffer, buffer_bytes, timeout);
220-
} catch (timeout_error &) {
221-
if (ec) *ec = lsl_timeout_error;
222-
} catch (lost_error &) {
223-
if (ec) *ec = lsl_lost_error;
224-
} catch (std::invalid_argument &) {
225-
if (ec) *ec = lsl_argument_error;
226-
} catch (std::range_error &) {
227-
if (ec) *ec = lsl_argument_error;
228-
} catch (std::exception &e) {
229-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
230-
if (ec) *ec = lsl_internal_error;
231-
}
157+
} LSL_STORE_EXCEPTION_IN(ec)
232158
return 0.0;
233159
}
234160

@@ -297,18 +223,7 @@ LIBLSL_C_API unsigned long lsl_pull_chunk_str(lsl_inlet in, char **data_buffer,
297223
return result;
298224
} else
299225
return 0;
300-
} catch (timeout_error &) {
301-
if (ec) *ec = lsl_timeout_error;
302-
} catch (lost_error &) {
303-
if (ec) *ec = lsl_lost_error;
304-
} catch (std::invalid_argument &) {
305-
if (ec) *ec = lsl_argument_error;
306-
} catch (std::range_error &) {
307-
if (ec) *ec = lsl_argument_error;
308-
} catch (std::exception &e) {
309-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
310-
if (ec) *ec = lsl_internal_error;
311-
}
226+
} LSL_STORE_EXCEPTION_IN(ec)
312227
return 0;
313228
}
314229

@@ -336,18 +251,7 @@ LIBLSL_C_API unsigned long lsl_pull_chunk_buf(lsl_inlet in, char **data_buffer,
336251
return result;
337252
} else
338253
return 0;
339-
} catch (timeout_error &) {
340-
if (ec) *ec = lsl_timeout_error;
341-
} catch (lost_error &) {
342-
if (ec) *ec = lsl_lost_error;
343-
} catch (std::invalid_argument &) {
344-
if (ec) *ec = lsl_argument_error;
345-
} catch (std::range_error &) {
346-
if (ec) *ec = lsl_argument_error;
347-
} catch (std::exception &e) {
348-
LOG_F(ERROR, "Unexpected error in %s: %s", __func__, e.what());
349-
if (ec) *ec = lsl_internal_error;
350-
}
254+
} LSL_STORE_EXCEPTION_IN(ec)
351255
return 0;
352256
}
353257

0 commit comments

Comments
 (0)