Skip to content

Commit aff80a4

Browse files
committed
Improve error reporting by storing the last error message, uniform exception handling and helper functions
1 parent 161c4e9 commit aff80a4

11 files changed

+141
-149
lines changed

include/lsl/common.h

+2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ typedef enum {
132132
lsl_internal_error = -4
133133
} lsl_error_code_t;
134134

135+
/// Return an explanation for the last error
136+
extern LIBLSL_C_API const char *lsl_last_error(void);
135137

136138
/**
137139
* 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

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

src/lsl_inlet_c.cpp

+5-26
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,18 +22,7 @@ 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) {

src/lsl_outlet_c.cpp

+21-93
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "lsl_c_api_helpers.hpp"
12
#include "stream_outlet_impl.h"
23

34
#pragma warning(disable : 4800)
@@ -12,16 +13,8 @@ using namespace lsl;
1213
// boilerplate wrapper code
1314
LIBLSL_C_API lsl_outlet lsl_create_outlet(
1415
lsl_streaminfo info, int32_t chunk_size, int32_t max_buffered) {
15-
try {
16-
stream_info_impl *infoimpl = info;
17-
lsl_outlet result = new stream_outlet_impl(*infoimpl, chunk_size,
18-
infoimpl->nominal_srate() ? (int)(infoimpl->nominal_srate() * max_buffered)
19-
: max_buffered * 100);
20-
return result;
21-
} catch (std::exception &e) {
22-
LOG_F(WARNING, "Unexpected error during construction of stream outlet: %s", e.what());
23-
return nullptr;
24-
}
16+
return create_object_noexcept<stream_outlet_impl>(*info, chunk_size,
17+
info->nominal_srate() ? (int)(info->nominal_srate() * max_buffered) : max_buffered * 100);
2518
}
2619

2720
LIBLSL_C_API void lsl_destroy_outlet(lsl_outlet out) {
@@ -111,52 +104,20 @@ LIBLSL_C_API int32_t lsl_push_sample_ctp(
111104
}
112105

113106
LIBLSL_C_API int32_t lsl_push_sample_v(lsl_outlet out, const void *data) {
114-
try {
115-
out->push_numeric_raw(data);
116-
return lsl_no_error;
117-
} catch (std::range_error &e) {
118-
LOG_F(WARNING, "Error during push_sample: %s", e.what());
119-
return lsl_argument_error;
120-
} catch (std::invalid_argument &e) {
121-
LOG_F(WARNING, "Error during push_sample: %s", e.what());
122-
return lsl_argument_error;
123-
} catch (std::exception &e) {
124-
LOG_F(WARNING, "Unexpected error during push_sample: %s", e.what());
125-
return lsl_internal_error;
126-
}
107+
return lsl_push_sample_vtp(out, data, 0.0, true);
127108
}
128109

129110
LIBLSL_C_API int32_t lsl_push_sample_vt(lsl_outlet out, const void *data, double timestamp) {
130-
try {
131-
out->push_numeric_raw(data, timestamp);
132-
return lsl_no_error;
133-
} catch (std::range_error &e) {
134-
LOG_F(WARNING, "Error during push_sample: %s", e.what());
135-
return lsl_argument_error;
136-
} catch (std::invalid_argument &e) {
137-
LOG_F(WARNING, "Error during push_sample: %s", e.what());
138-
return lsl_argument_error;
139-
} catch (std::exception &e) {
140-
LOG_F(WARNING, "Unexpected error during push_sample: %s", e.what());
141-
return lsl_internal_error;
142-
}
111+
return lsl_push_sample_vtp(out, data, timestamp, true);
143112
}
144113

145114
LIBLSL_C_API int32_t lsl_push_sample_vtp(
146115
lsl_outlet out, const void *data, double timestamp, int32_t pushthrough) {
116+
LSL_DEFAULT_EC;
147117
try {
148118
out->push_numeric_raw(data, timestamp, pushthrough != 0);
149-
return lsl_no_error;
150-
} catch (std::range_error &e) {
151-
LOG_F(WARNING, "Error during push_sample: %s", e.what());
152-
return lsl_argument_error;
153-
} catch (std::invalid_argument &e) {
154-
LOG_F(WARNING, "Error during push_sample: %s", e.what());
155-
return lsl_argument_error;
156-
} catch (std::exception &e) {
157-
LOG_F(WARNING, "Unexpected error during push_sample: %s", e.what());
158-
return lsl_internal_error;
159-
}
119+
} LSL_CATCH_EXCEPTIONS
120+
return *ec;
160121
}
161122

162123
LIBLSL_C_API int32_t lsl_push_sample_str(lsl_outlet out, const char **data) {
@@ -367,21 +328,14 @@ LIBLSL_C_API int32_t lsl_push_chunk_strt(
367328

368329
LIBLSL_C_API int32_t lsl_push_chunk_strtp(lsl_outlet out, const char **data,
369330
unsigned long data_elements, double timestamp, int32_t pushthrough) {
331+
LSL_DEFAULT_EC;
370332
try {
371333
std::vector<std::string> tmp;
372334
for (unsigned long k = 0; k < data_elements; k++) tmp.emplace_back(data[k]);
373335
if (data_elements) out->push_chunk_multiplexed(&tmp[0], tmp.size(), timestamp, pushthrough);
374-
return lsl_no_error;
375-
} catch (std::range_error &e) {
376-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
377-
return lsl_argument_error;
378-
} catch (std::invalid_argument &e) {
379-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
380-
return lsl_argument_error;
381-
} catch (std::exception &e) {
382-
LOG_F(WARNING, "Unexpected error during push_chunk: %s", e.what());
383-
return lsl_internal_error;
384336
}
337+
LSL_CATCH_EXCEPTIONS
338+
return *ec;
385339
}
386340

387341
LIBLSL_C_API int32_t lsl_push_chunk_strtn(
@@ -391,23 +345,16 @@ LIBLSL_C_API int32_t lsl_push_chunk_strtn(
391345

392346
LIBLSL_C_API int32_t lsl_push_chunk_strtnp(lsl_outlet out, const char **data,
393347
unsigned long data_elements, const double *timestamps, int32_t pushthrough) {
348+
LSL_DEFAULT_EC;
394349
try {
395350
if (data_elements) {
396351
std::vector<std::string> tmp;
397352
for (unsigned long k = 0; k < data_elements; k++) tmp.emplace_back(data[k]);
398353
out->push_chunk_multiplexed_noexcept(&tmp[0], timestamps, data_elements, pushthrough);
399354
}
400-
return lsl_no_error;
401-
} catch (std::range_error &e) {
402-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
403-
return lsl_argument_error;
404-
} catch (std::invalid_argument &e) {
405-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
406-
return lsl_argument_error;
407-
} catch (std::exception &e) {
408-
LOG_F(WARNING, "Unexpected error during push_chunk: %s", e.what());
409-
return lsl_internal_error;
410355
}
356+
LSL_CATCH_EXCEPTIONS
357+
return *ec;
411358
}
412359

413360
LIBLSL_C_API int32_t lsl_push_chunk_buf(
@@ -422,21 +369,14 @@ LIBLSL_C_API int32_t lsl_push_chunk_buft(lsl_outlet out, const char **data, cons
422369

423370
LIBLSL_C_API int32_t lsl_push_chunk_buftp(lsl_outlet out, const char **data,
424371
const uint32_t *lengths, unsigned long data_elements, double timestamp, int32_t pushthrough) {
372+
LSL_DEFAULT_EC;
425373
try {
426374
std::vector<std::string> tmp;
427375
for (unsigned long k = 0; k < data_elements; k++) tmp.emplace_back(data[k], lengths[k]);
428376
if (data_elements) out->push_chunk_multiplexed(&tmp[0], tmp.size(), timestamp, pushthrough);
429-
return lsl_no_error;
430-
} catch (std::range_error &e) {
431-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
432-
return lsl_argument_error;
433-
} catch (std::invalid_argument &e) {
434-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
435-
return lsl_argument_error;
436-
} catch (std::exception &e) {
437-
LOG_F(WARNING, "Unexpected error during push_chunk: %s", e.what());
438-
return lsl_internal_error;
439377
}
378+
LSL_CATCH_EXCEPTIONS
379+
return *ec;
440380
}
441381

442382
LIBLSL_C_API int32_t lsl_push_chunk_buftn(lsl_outlet out, const char **data,
@@ -447,24 +387,17 @@ LIBLSL_C_API int32_t lsl_push_chunk_buftn(lsl_outlet out, const char **data,
447387
LIBLSL_C_API int32_t lsl_push_chunk_buftnp(lsl_outlet out, const char **data,
448388
const uint32_t *lengths, unsigned long data_elements, const double *timestamps,
449389
int32_t pushthrough) {
390+
LSL_DEFAULT_EC;
450391
try {
451392
if (data_elements) {
452393
std::vector<std::string> tmp;
453394
for (unsigned long k = 0; k < data_elements; k++) tmp.emplace_back(data[k], lengths[k]);
454395
out->push_chunk_multiplexed(
455396
&tmp[0], timestamps, (std::size_t)data_elements, pushthrough);
456397
}
457-
return lsl_no_error;
458-
} catch (std::range_error &e) {
459-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
460-
return lsl_argument_error;
461-
} catch (std::invalid_argument &e) {
462-
LOG_F(WARNING, "Error during push_chunk: %s", e.what());
463-
return lsl_argument_error;
464-
} catch (std::exception &e) {
465-
LOG_F(WARNING, "Unexpected error during push_chunk: %s", e.what());
466-
return lsl_internal_error;
467398
}
399+
LSL_CATCH_EXCEPTIONS
400+
return *ec;
468401
}
469402

470403
LIBLSL_C_API int32_t lsl_have_consumers(lsl_outlet out) {
@@ -486,11 +419,6 @@ LIBLSL_C_API int32_t lsl_wait_for_consumers(lsl_outlet out, double timeout) {
486419
}
487420

488421
LIBLSL_C_API lsl_streaminfo lsl_get_info(lsl_outlet out) {
489-
try {
490-
return new stream_info_impl(out->info());
491-
} catch (std::exception &e) {
492-
LOG_F(WARNING, "Unexpected error in lsl_get_info: %s", e.what());
493-
return nullptr;
494-
}
422+
return create_object_noexcept<stream_info_impl>(out->info());
495423
}
496424
}

0 commit comments

Comments
 (0)