Skip to content

Commit b8fb225

Browse files
More status handling shorthands
1 parent cc382c9 commit b8fb225

File tree

10 files changed

+99
-24
lines changed

10 files changed

+99
-24
lines changed

.clang-format

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ IncludeCategories:
8282
InsertNewlineAtEOF: true
8383
IfMacros:
8484
- mlib_math_catch
85+
- amongoc_if_error
8586
AllowBreakBeforeNoexceptSpecifier: Always
8687
---
8788
# For some reason, Clang sees some files as Objective-C. Add this section just to appease it.

docs/conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,8 @@ class CustomCppLexer(CppLexer):
415415
(r"bson_type_\w+\b", token.Name.Constant),
416416
# Other macro
417417
(r"bson_foreach\w*\b", token.Keyword),
418+
(r"amongoc_declmsg\b", token.Keyword),
419+
(r"amongoc_if_error\b", token.Keyword),
418420
inherit,
419421
],
420422
}

docs/how-to/communicate.example.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ int main(int argc, char const* const* argv) {
7474

7575
amongoc_loop loop;
7676
amongoc_status status = amongoc_default_loop_init(&loop);
77-
if (amongoc_is_error(status)) {
78-
amongoc_declmsg(msg, status);
77+
amongoc_if_error (status, msg) {
7978
fprintf(stderr, "Error setting up the event loop: %s\n", msg);
8079
return 2;
8180
}
@@ -102,8 +101,7 @@ int main(int argc, char const* const* argv) {
102101
amongoc_client_delete(state.client);
103102
amongoc_default_loop_destroy(&loop);
104103

105-
if (amongoc_is_error(status)) {
106-
amongoc_declmsg(msg, status);
104+
amongoc_if_error (status, msg) {
107105
fprintf(stderr, "An error occurred: %s\n", msg);
108106
return 2;
109107
} else {

docs/how-to/communicate.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ Print the Final Result
240240

241241
.. literalinclude:: communicate.example.c
242242
:lineno-match:
243-
:start-at: is_error
243+
:start-at: if_error
244244
:end-before: end.
245245

246246
Finally, we inspect the `amongoc_status` that was produced by our operation and

docs/how-to/looping.example.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ typedef struct {
2020
*
2121
* @param state_ptr Pointer to the `state` for the program
2222
*/
23-
amongoc_emitter loop_step(amongoc_box state_ptr, amongoc_status prev_status, amongoc_box prev_res) {
23+
amongoc_emitter loop_step(amongoc_box state_ptr, //
24+
amongoc_status prev_status,
25+
amongoc_box prev_res) {
2426
(void)prev_res;
2527
(void)prev_status;
2628
// Print our status
@@ -60,7 +62,10 @@ int main(int argc, char const* const* argv) {
6062

6163
// Create a default loop
6264
amongoc_loop loop;
63-
amongoc_default_loop_init(&loop);
65+
amongoc_if_error (amongoc_default_loop_init(&loop), msg) {
66+
fprintf(stderr, "Error initializing event loop: %s\n", msg);
67+
return 1;
68+
}
6469

6570
// Seed the initial sum
6671
state app_state = {.countdown = delay, .loop = &loop, .a = 0, .b = 1};
@@ -77,14 +82,12 @@ int main(int argc, char const* const* argv) {
7782
amongoc_operation_delete(op);
7883
amongoc_default_loop_destroy(&loop);
7984

80-
if (amongoc_is_error(status)) {
81-
amongoc_declmsg(msg, status);
85+
amongoc_if_error (status, msg) {
8286
fprintf(stderr, "error: %s\n", msg);
8387
amongoc_box_destroy(result);
8488
return 2;
85-
} else {
86-
// Get the value returned with `amongoc_just` in `loop_step`
87-
printf("Got final value: %lu\n", amongoc_box_cast(uint64_t, result));
8889
}
90+
// Get the value returned with `amongoc_just` in `loop_step`
91+
printf("Got final value: %lu\n", amongoc_box_cast(uint64_t, result));
8992
return 0;
9093
}

docs/how-to/looping.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,14 @@ program. We check for errors, either printing the error message or printing the
196196
final result:
197197

198198
.. literalinclude:: looping.example.c
199-
:start-at: is_error
199+
:start-at: if_error (status, msg)
200200
:end-at: return 0;
201201
:lineno-match:
202202

203-
We use `amongoc_is_error` to test the final status for an error condition. If it
204-
is an error, we get and print the error message to stderr, and we must destroy
205-
the final result box because it may contain an unspecified value related to the
206-
error, but we don't want to do anything with it.
203+
We use :c:macro:`amongoc_if_error` to test the final status for an error
204+
condition. If it is an error, we get and print the error message to stderr, and
205+
we must destroy the final result box because it may contain an unspecified value
206+
related to the error, but we don't want to do anything with it.
207207

208208
In the success case, we extract the value returned in `amongoc_just` as a
209209
``uint64_t`` and print it to stdout. Note that because the box returned by

docs/learn/connect.example.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ amongoc_box on_connect(amongoc_box userdata, amongoc_status* status, amongoc_box
99
int main(void) {
1010
amongoc_loop loop;
1111
amongoc_status status = amongoc_default_loop_init(&loop);
12-
if (amongoc_is_error(status)) {
13-
amongoc_declmsg(msg, status);
12+
amongoc_if_error (status, msg) {
1413
fprintf(stderr, "Failed to prepare the event loop: %s\n", msg);
1514
return 2;
1615
}
@@ -32,8 +31,7 @@ amongoc_box on_connect(amongoc_box userdata, amongoc_status* status, amongoc_box
3231
// We don't use the userdata
3332
(void)userdata;
3433
// Check for an error
35-
if (amongoc_is_error(*status)) {
36-
amongoc_declmsg(msg, *status);
34+
amongoc_if_error (*status, msg) {
3735
fprintf(stderr, "Error while connecting to server: %s\n", msg);
3836
} else {
3937
printf("Successfully connected!\n");

docs/ref/status.rst

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ Functions & Macros
8383
The defintion of what constitutes an error depends on the
8484
`amongoc_status::category`.
8585

86+
.. seealso:: :c:macro:`amongoc_if_error`
87+
8688

8789
.. function:: bool amongoc_is_cancellation(amongoc_status st)
8890

@@ -130,8 +132,12 @@ Functions & Macros
130132

131133
This function does not dynamically allocate any memory.
132134

133-
.. seealso:: :c:macro:`amongoc_declmsg` for concisely obtaining the message
134-
from a status object.
135+
.. seealso::
136+
137+
- :c:macro:`amongoc_declmsg` for concisely obtaining the message from a
138+
status object.
139+
- :c:macro:`amongoc_if_error` to check for an error and extract the message
140+
in a single line.
135141

136142

137143
.. c:macro:: amongoc_declmsg(MsgVar, Status)
@@ -151,6 +157,34 @@ Functions & Macros
151157
const char* MsgVar = amongoc_message(Status, __buffer, sizeof __buffer)
152158

153159

160+
.. c:macro::
161+
amongoc_if_error(Status, MsgVar, StatusVar)
162+
163+
Create a branch on whether the given status represents an error. This macro
164+
supports being called with two arguments, or with three::
165+
166+
amongoc_if_error (status, msg_varname) {
167+
fprintf(stderr, "Error message: %s\n", msg_varname)
168+
}
169+
170+
::
171+
172+
amongoc_if_error (status, msg_varname, status_varname) {
173+
fprintf(stderr, "Error code %d has message: %s\n", status_varname.code, msg_varname);
174+
}
175+
176+
:param Status: The first argument must be an expression of type `amongoc_status`. This is
177+
the status to be inspected.
178+
:param MsgVar: This argument should be a plain identifier, which will be declared within
179+
the scope of the statement as the :term:`C string` for the status.
180+
:param StatusVar: If provided, a variable of type `amongoc_status` will be declared within
181+
the statment scope that captures the value of the ``Status`` argument.
182+
183+
.. hint::
184+
185+
If you are using ``clang-format``, add ``amongoc_if_error`` to the
186+
``IfMacros`` for your ``clang-format`` configuration.
187+
154188
.. var:: const amongoc_status amongoc_okay
155189

156190
A generic status with a code zero. This represents a generic non-error status.

include/amongoc/status.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,8 +871,32 @@ mlib_extern_c_end();
871871

872872
#define amongoc_okay mlib_parenthesized_expression(*_amongocStatusGetOkayStatus())
873873

874-
#if mlib_is_cxx()
874+
/**
875+
* @brief Branch on whether a status code is an error, and get the string message
876+
* out of it in a single statement.
877+
*
878+
* @param Status An expression of type `amongoc_status`
879+
* @param MsgVar An identifier, which will be declared as a C string for the status' message
880+
*/
881+
#define amongoc_if_error(...) MLIB_ARGC_PICK(_amongoc_if_error, __VA_ARGS__)
882+
#define _amongoc_if_error_argc_2(Status, MsgVar) \
883+
_amongoc_if_error_argc_3((Status), MsgVar, MLIB_PASTE(_amongoc_status_tmp_lno_, __LINE__))
884+
#define _amongoc_if_error_argc_3(Status, MsgVar, StatusVar) \
885+
_amongocIfErrorBlock((Status), \
886+
MsgVar, \
887+
StatusVar, \
888+
MLIB_PASTE(_amongoc_oncevar_lno_, __LINE__), \
889+
MLIB_PASTE(_amongoc_msgbuf_lno_, __LINE__))
890+
// clang-format off
891+
#define _amongocIfErrorBlock(Status, MsgVar, StatusVar, OnceVar, MsgBuf) \
892+
for (int OnceVar = 1; OnceVar; OnceVar = 0) \
893+
for (amongoc_status const StatusVar = (Status); OnceVar; OnceVar = 0) \
894+
if (amongoc_is_error(StatusVar)) \
895+
for (char MsgBuf[128]; OnceVar; OnceVar = 0) \
896+
for (const char* MsgVar = amongoc_message(StatusVar, MsgBuf, sizeof MsgBuf); OnceVar; OnceVar = 0)
897+
// clang-format on
875898

899+
#if mlib_is_cxx()
876900
namespace amongoc {
877901

878902
using status = ::amongoc_status;

src/amongoc/status.test.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,27 @@ TEST_CASE("Status/Okay") {
88
amongoc_status st = amongoc_okay;
99
CHECK(st.code == 0);
1010
CHECK(st.category == &amongoc_generic_category);
11+
12+
bool took_else = false;
13+
amongoc_if_error (st, _) {
14+
FAIL_CHECK("Did not expect an error");
15+
} else {
16+
took_else = true;
17+
}
18+
CHECK(took_else);
1119
}
1220

1321
TEST_CASE("Status/From an Error") {
1422
auto st = amongoc_status::from(std::make_error_code(std::errc::io_error));
1523
CHECK(st.category == &amongoc_generic_category);
1624
CHECK(st.code == EIO);
25+
bool took_err = false;
26+
amongoc_if_error (st, _) {
27+
took_err = true;
28+
} else {
29+
FAIL_CHECK("Did not take the error branch");
30+
}
31+
CHECK(took_err);
1732
}
1833

1934
TEST_CASE("Status/As an Error") {

0 commit comments

Comments
 (0)