diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 1eaab6fc..c0879ccd 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,5 +1,9 @@ # zcbor v. 0.9.99 +* [Recommended] A new macro `ZCBOR_CAST_FP` has been added for casting function pointers for use in the zcbor API. + The macro will first check that the function pointer has one of the supported signatures. + You are recommended to use it on any function pointer that is passed as an argument to a zcbor function. + # zcbor v. 0.9.0 diff --git a/include/zcbor_common.h b/include/zcbor_common.h index d9c12bfb..70c2ca18 100644 --- a/include/zcbor_common.h +++ b/include/zcbor_common.h @@ -530,6 +530,55 @@ static inline size_t zcbor_flags_to_bytes(size_t num_flags) size_t strnlen(const char *, size_t); +/** Add some additional safety when casting to zcbor_decoder_t or zcbor_encoder_t + * + * This will fail if trying to cast functions from the zcbor API that should not be used + * as zcbor_decoder_t or zcbor_encoder_t, such as zcbor_decode_int() (3 arguments), or + * zcbor_int32_put() (result is not a pointer). + * + * _Generic is assumed to be supported in newer copilers, even when using the C99 flag. + * + * To add support for casting a custom function foo_decode() with its own result struct, + * you can do something like the following: + * + * #define ZCBOR_CAST_FP_CUSTOM(func) _Generic((func), \ + * bool(*)(zcbor_state_t *, foo_t *): ((zcbor_decoder_t *)func), \ + * bool(*)(zcbor_state_t *, const foo_t *): ((zcbor_encoder_t *)func), \ + * default: ZCBOR_CAST_FP(func)) + * + * @param func Function pointer to cast. + * @retval The result of the macro will be func, cast to either zcbor_decoder_t or + * zcbor_encoder_t depending on whether the result argument is const. + */ +#define ZCBOR_CAST_FP(func) _Generic((func), \ + bool(*)(zcbor_state_t *, void *): func, \ + bool(*)(zcbor_state_t *, int8_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, int16_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, int32_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, int64_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, uint8_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, uint16_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, uint32_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, uint64_t *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, bool *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, float *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, double *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, struct zcbor_string *): ((zcbor_decoder_t *)func), \ + bool(*)(zcbor_state_t *, const void *): func, \ + bool(*)(zcbor_state_t *, const int8_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const int16_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const int32_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const int64_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const uint8_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const uint16_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const uint32_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const uint64_t *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const bool *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const float *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const double *): ((zcbor_encoder_t *)func), \ + bool(*)(zcbor_state_t *, const struct zcbor_string *): ((zcbor_encoder_t *)func) \ +) + #ifdef __cplusplus } #endif diff --git a/include/zcbor_decode.h b/include/zcbor_decode.h index 90319d21..483bbdbd 100644 --- a/include/zcbor_decode.h +++ b/include/zcbor_decode.h @@ -187,6 +187,8 @@ bool zcbor_unordered_map_start_decode(zcbor_state_t *state); * keys in the map until it returns true, at which point * @ref zcbor_unordered_map_search will return true. * For example, a zcbor_*_pexpect() function. + * Please use @ref ZCBOR_CAST_FP to cast the function + * since it will also check for compatibility. * @param[inout] state The current state of decoding. Must be currently decoding * the contents of a map, and pointing to one (any) of the * keys, not one of the values. If successful, the @p state @@ -316,6 +318,8 @@ bool zcbor_any_skip(zcbor_state_t *state, void *unused); * an array of result variables. * Should not be an _expect() function, use * _pexpect() instead. + * Please use @ref ZCBOR_CAST_FP to cast the function + * since it will also check for compatibility. * @param[out] result Where to place the decoded values. Must be an array * of at least @p max_decode elements. * @param[in] result_len The length of each result variable. Must be the @@ -336,6 +340,8 @@ bool zcbor_multi_decode(size_t min_decode, size_t max_decode, size_t *num_decode * * @param[out] present Whether or not the data was present and successfully decoded. * @param[in] decoder The decoder to attempt. + * Please use @ref ZCBOR_CAST_FP to cast the function + * since it will also check for compatibility. * @param[out] result The result, if present. * * @return Should always return true. diff --git a/include/zcbor_encode.h b/include/zcbor_encode.h index 9bd9383c..2a8203e8 100644 --- a/include/zcbor_encode.h +++ b/include/zcbor_encode.h @@ -168,6 +168,8 @@ bool zcbor_list_map_end_force_encode(zcbor_state_t *state); * The input pointer is moved @p input_len bytes for * each call to @p encoder, i.e. @p input refers to an * array of input variables. + * Please use @ref ZCBOR_CAST_FP to cast the function + * since it will also check for compatibility. * @param[in] input Source of the encoded values. Must be an array of * at least @p max_encode elements. * @param[in] input_len The length of the input variables. Must be the diff --git a/tests/unit/test1_unit_tests/src/main.c b/tests/unit/test1_unit_tests/src/main.c index ca7f7f6f..5812ab99 100644 --- a/tests/unit/test1_unit_tests/src/main.c +++ b/tests/unit/test1_unit_tests/src/main.c @@ -276,8 +276,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error) zassert_false(zcbor_map_start_encode(state_e, 0), NULL); zassert_false(zcbor_map_end_encode(state_e, 0), NULL); zassert_false(zcbor_list_end_encode(state_e, 1), NULL); - zassert_false(zcbor_multi_encode(1, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)14, 0), NULL); - zassert_false(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)15, 0), NULL); + zassert_false(zcbor_multi_encode(1, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){14}, 0), NULL); + zassert_false(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){15}, 0), NULL); zassert_mem_equal(&state_backup, state_e, sizeof(state_backup), NULL); @@ -325,8 +325,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error) zassert_true(zcbor_map_start_encode(state_e, 0), NULL); zassert_true(zcbor_map_end_encode(state_e, 0), NULL); zassert_true(zcbor_list_end_encode(state_e, 1), NULL); - zassert_true(zcbor_multi_encode(1, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)14, 0), NULL); - zassert_true(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)15, 0), NULL); + zassert_true(zcbor_multi_encode(1, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){14}, 0), NULL); + zassert_true(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){15}, 0), NULL); ZCBOR_STATE_D(state_d, 3, payload, sizeof(payload), 50, 0); state_d->constant_state->stop_on_error = true; @@ -374,8 +374,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error) zassert_false(zcbor_map_start_decode(state_d), NULL); zassert_false(zcbor_map_end_decode(state_d), NULL); zassert_false(zcbor_list_end_decode(state_d), NULL); - zassert_false(zcbor_multi_decode(1, 1, &(size_t){1}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)14, 0), NULL); - zassert_false(zcbor_present_decode(&(bool){true}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)15), NULL); + zassert_false(zcbor_multi_decode(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){14}, 0), NULL); + zassert_false(zcbor_present_decode(&(bool){true}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){15}), NULL); zassert_mem_equal(&state_backup, state_d, sizeof(state_backup), NULL); zassert_mem_equal(&constant_state_backup, state_d->constant_state, sizeof(constant_state_backup), NULL); @@ -420,8 +420,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error) zassert_true(zcbor_map_start_decode(state_d), NULL); zassert_true(zcbor_map_end_decode(state_d), NULL); zassert_true(zcbor_list_end_decode(state_d), NULL); - zassert_true(zcbor_multi_decode(1, 1, &(size_t){1}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)14, 0), NULL); - zassert_true(zcbor_present_decode(&(bool){1}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)15), NULL); + zassert_true(zcbor_multi_decode(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){14}, 0), NULL); + zassert_true(zcbor_present_decode(&(bool){1}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){15}), NULL); /* Everything has been decoded. */ zassert_equal(state_e->payload, state_d->payload, NULL); @@ -1181,10 +1181,10 @@ ZTEST(zcbor_unit_tests, test_pexpect) void decode_inner_map(zcbor_state_t *state, void *result) { zassert_true(zcbor_unordered_map_start_decode(state), NULL); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_bool_pexpect, state, &(bool){false}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_bool_pexpect), state, &(bool){false}), NULL); zassert_true(zcbor_undefined_expect(state, NULL), NULL); zcbor_elem_processed(state); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_tstr_expect, state, &(struct zcbor_string){"hello", 5}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_tstr_expect), state, &(struct zcbor_string){"hello", 5}), NULL); zassert_true(zcbor_tstr_expect(state, &(struct zcbor_string){"world", 5}), NULL); zcbor_elem_processed(state); bool ret = zcbor_unordered_map_end_decode(state); @@ -1263,7 +1263,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map) /* Test empty map */ zassert_true(zcbor_unordered_map_start_decode(state_d), NULL); - zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){2}), NULL); + zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){2}), NULL); zassert_equal(ZCBOR_ERR_ELEM_NOT_FOUND, zcbor_peek_error(state_d), "err: %d\n", zcbor_peek_error(state_d)); ret = zcbor_unordered_map_end_decode(state_d); zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d)); @@ -1271,7 +1271,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map) /* Test single entry map */ zassert_true(zcbor_unordered_map_start_decode(state_d), NULL); - ret = zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){1}); + ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){1}); zassert_true(ret, "err: %d\n", zcbor_peek_error); zassert_true(zcbor_int32_expect(state_d, 1), NULL); ret = zcbor_unordered_map_end_decode(state_d); @@ -1281,15 +1281,15 @@ ZTEST(zcbor_unit_tests, test_unordered_map) /* Test that looping stops both if it starts at the very start and very end of the map. * Also test ZCBOR_ERR_MAP_MISALIGNED. */ zassert_true(zcbor_unordered_map_start_decode(state_d2), NULL); - zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){3}), NULL); - ret = zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){2}); + zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){3}), NULL); + ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){2}); zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d2)); - zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){1}), NULL); + zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){1}), NULL); zassert_equal(zcbor_peek_error(state_d2), ZCBOR_ERR_MAP_MISALIGNED, NULL); zassert_true(zcbor_int32_expect(state_d2, 2), NULL); zassert_true(zcbor_array_at_end(state_d2), NULL); - zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){3}), NULL); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){1}), NULL); + zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){3}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){1}), NULL); zassert_true(zcbor_int32_expect(state_d2, 1), NULL); ret = zcbor_unordered_map_end_decode(state_d2); zassert_true(ret, NULL); @@ -1300,7 +1300,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map) zassert_false(zcbor_unordered_map_start_decode(state_d3), NULL); #else zassert_true(zcbor_unordered_map_start_decode(state_d3), NULL); - zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d3, &(int32_t){2}), NULL); + zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d3, &(int32_t){2}), NULL); #endif zassert_equal(zcbor_peek_error(state_d3), ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE, NULL); #endif @@ -1313,17 +1313,17 @@ ZTEST(zcbor_unit_tests, test_unordered_map) #ifndef ZCBOR_CANONICAL zcbor_elem_processed(state_d); // Should do nothing because no elements have been discovered. #endif - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){1}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){1}), NULL); zassert_true(zcbor_int32_expect(state_d, 1), NULL); ret = zcbor_unordered_map_end_decode(state_d); zassert_false(ret, NULL); zassert_equal(ZCBOR_ERR_ELEMS_NOT_PROCESSED, zcbor_peek_error(state_d), NULL); /* Cause a restart of the map */ - zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){3}), NULL); + zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){3}), NULL); ret = zcbor_unordered_map_end_decode(state_d); zassert_false(ret, NULL); zassert_equal(ZCBOR_ERR_ELEMS_NOT_PROCESSED, zcbor_peek_error(state_d), NULL); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){2}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){2}), NULL); zassert_true(zcbor_int32_expect(state_d, 2), NULL); ret = zcbor_unordered_map_end_decode(state_d); zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d)); @@ -1332,18 +1332,19 @@ ZTEST(zcbor_unit_tests, test_unordered_map) /* Test a large map, including nesting. */ state_d->constant_state->manually_process_elem = true; zassert_true(zcbor_unordered_map_start_decode(state_d), NULL); - ret = zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){-1}); + ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){-1}); zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d)); zassert_true(zcbor_tstr_decode(state_d, &str_result1), NULL); zcbor_elem_processed(state_d); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_bool_pexpect, state_d, &(bool){true}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_bool_pexpect), state_d, &(bool){true}), NULL); zassert_true(zcbor_tstr_decode(state_d, &str_result2), NULL); zcbor_elem_processed(state_d); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){2}), NULL); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){2}), NULL); zassert_true(zcbor_int32_decode(state_d, &int_result1), NULL); zcbor_elem_processed(state_d); - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){1}), NULL); + ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){1}); + zassert_true(ret, "%s\n", zcbor_error_str(zcbor_peek_error(state_d))); zassert_true(zcbor_tstr_decode(state_d, &str_result3), NULL); zcbor_elem_processed(state_d); @@ -1383,7 +1384,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map) zcbor_elem_processed(state_d); for (size_t i = 34; i > 2; i--) { - zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &i), "i: %d, err: %d\n", i, zcbor_peek_error(state_d)); + zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &i), "i: %d, err: %d\n", i, zcbor_peek_error(state_d)); zassert_false(zcbor_int32_expect(state_d, i), NULL); zassert_true(zcbor_int32_expect(state_d, -1 * i), NULL); zcbor_elem_processed(state_d);