# C serialization API survey

**URL:** <https://forum.opencyphal.org/t/c-serialization-api-survey/943>\
**Category:** Nunavut\
**Created:** [September 18, 2020, 5:35pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943 "2020-09-18T17:35:54Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 18, 2020, 5:35pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/1 "2020-09-18T17:35:54Z")

</div>

What do people perfer for error handling in the C serialization routines we’re working on in Nunavut:

## Option 1

```auto
typedef struct
{
    uint32_t size_bits;
    int32_t status; // Negative values are errors
} SerializationResult;

static inline SerializationResult serialize_foo(const foo* in_instance, uint32_t offset, uint8_t* const out_buffer)

```

## Option 2

```auto

// Returns negative on errors.
// Sets out_size_bits to the bits serialized.
static inline int32_t serialize_foo(const foo* in_instance, uint32_t offset, uint8_t* const out_buffer, uint32_t* out_size_bits)

```

## Option 3

```auto

// Returns size written. Returns 0 on error although 0 is not an error (check out_status).
// Sets out_status negative on errors. Optional. Set to 0 to ignore errors; you have been warned.
static inline uint32_t serialize_foo(const foo* in_instance, uint32_t offset, uint8_t* const out_buffer, int32_t* out_status)

```

## Not an Option

```auto

// Returns negative on errors. Positive values are the size_bits written.
// This makes the return value an implicit union of int32_t and uint31_t which should tell you why this isn't an option.
static inline int32_t serialize_foo(const foo* in_instance, uint32_t offset, uint8_t* const out_buffer)

```

_Poll ([view on site](https://forum.opencyphal.org/t/c-serialization-api-survey/943/1))_

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 18, 2020, 5:39pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/2 "2020-09-18T17:39:47Z")

</div>

paging @pavel.kirienko, @david.lenfesty, and @TSC21

---

<div class="post-metadata">

**Author:** ![pavel.kirienko](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/pavel.kirienko/32/9_2.png) [@pavel.kirienko](https://forum.opencyphal.org/u/pavel.kirienko)\
**Post date:** [September 18, 2020, 6:02pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/3 "2020-09-18T18:02:30Z")

</div>

Can you please expand a bit why _Not an Option_ is not an option?

> This makes the return value an implicit union of int32\_t and uint31\_t which should tell you why this isn’t an option.

Are you concerned about the range of the return value being limited by the sign bit? Or are you concerned about the type safety? Type-wise this is certainly not great but the expressivity of C is limited so there’s only so much one can do.

Also, is my understanding correct that we only have two error states:

1. The destination buffer is too small.
2. The structure breaks DSDL contracts: an array is too large or a union tag is wrong.

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 18, 2020, 6:17pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/4 "2020-09-18T18:17:17Z")

</div>

It breaks the same prohibitions against unions found in most high-reliability standards. What you have in that example is a tagged union:

```auto
typedef struct
{
    uint8_t is_error : 1;
    union
    {
        int32_t error;
        uint32_t size;
    } value;

} ResultType;

```

so given a method like this:

```auto
#define BAD_ERROR -2

static inline ResultType doit()
{
    ResultType result;
    result.is_error = 1;
    result.value.error = BAD_ERROR;
    return result;
}

```

… you consume the result like:

```auto
int main()
{

    ResultType result = doit();
    if (result.is_error)
    {
        if (result.value.error == BAD_ERROR)
        {
            // wow, that was bad!
            return -1;
        }
        else
        {
            // Some other error
            return -1;
        }
    }
    else
    {
        // no error
        return result.value.size;
    }
}

```

this is the same logic if the result is a signed int:

```auto
int main()
{

    int32_t result = doit();
    if (result < 0)
    {
        if (result == BAD_ERROR)
        {
            // wow, that was bad!
            return -1;
        }
        else
        {
            // Some other error
            return -1;
        }
    }
    else
    {
        // no error
        return (uint32_t)result;
    }
}

```

ergo, the “not an option” option describes a tagged union albeit one that is very instruction efficient but you also have the oddity that the uint32\_t is actually a uint31\_t and must be sure not to use that last bit for size information. This the compiler can’t help you with. I’d rather use more instructions and another register and avoid all the edge cases that the implicit tagged union raises.

---

<div class="post-metadata">

**Author:** ![pavel.kirienko](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/pavel.kirienko/32/9_2.png) [@pavel.kirienko](https://forum.opencyphal.org/u/pavel.kirienko)\
**Post date:** [September 18, 2020, 6:32pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/5 "2020-09-18T18:32:55Z")

</div>

OK, I chose Option 2 because it is more in-line with what one coding in C would expect, I think.

Do we need to distinguish between the two (did I miss anything?) error states, or can we simply reduce the error state to a bool? I don’t expect an application to choose a specific error handling strategy based on the error code.

Are we also using a bool error state for deserialization?

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 18, 2020, 6:37pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/6 "2020-09-18T18:37:59Z")

</div>

Okay, For Option 2 I think the out variable is optional (if you don’t care about the bits read or written you can ignore…at your own peril).

I’d rather keep the error condition as a signed machine word. This means we get success checks in the implicit bool conversion and we have a more future-proof API if we did need to surface more discrete error conditions that the application might have to handle. Given the nature of our target architectures there will be no instruction or memory advantage to the BOOL.

---

<div class="post-metadata">

**Author:** ![pavel.kirienko](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/pavel.kirienko/32/9_2.png) [@pavel.kirienko](https://forum.opencyphal.org/u/pavel.kirienko)\
**Post date:** [September 18, 2020, 6:39pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/7 "2020-09-18T18:39:48Z")

</div>

I am least concerned about performance here; the main thing is the clarity of our API. Okay, I am OK to keep a signed integer.

---

<div class="post-metadata">

**Author:** ![david.lenfesty](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/david.lenfesty/32/488_2.png) [@david.lenfesty](https://forum.opencyphal.org/u/david.lenfesty)\
**Post date:** [September 18, 2020, 6:53pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/8 "2020-09-18T18:53:17Z")

</div>

My 2 cents:

I very much dislike 3, maybe it’s just flashbacks from using `errno`, but I firmly believe that error conditions should be encoded as the return value, not a seperate check.

I prefer 2 to 1 mostly because that’s the pattern I typically see/use in C, and we don’t have the advantage of a strong type system here that would make a new type very useful (a la Rust’s `Result` type).

And yeah, there are two categories of errors but there are 4 error values at the moment, and it may be useful to distinguish those in some applications.

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 18, 2020, 7:00pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/9 "2020-09-18T19:00:28Z")

</div>

Modified proposal for the serialization support header:

```auto

#define NUNAVUT_SUCCESS 0

/// Nunavut returns 0 for success and > 0 for any failure. It is always adequate to
/// check that error_value != 0 to detect errors.
///
/// Nunavut serialization will never define more than 127 errors and the reserved
/// error numbers are [1,127].
///

```

---

<div class="post-metadata">

**Author:** ![pavel.kirienko](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/pavel.kirienko/32/9_2.png) [@pavel.kirienko](https://forum.opencyphal.org/u/pavel.kirienko)\
**Post date:** [September 18, 2020, 7:11pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/10 "2020-09-18T19:11:13Z")

</div>

Would that still be necessary for option 2, where one can simply compare against zero (`!= 0`)? Also, function-like macros are incompatible with MISRA.

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 18, 2020, 8:53pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/11 "2020-09-18T20:53:54Z")

</div>

Posix-like it is then. I’ve updated my previous post.

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 19, 2020, 6:27pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/12 "2020-09-19T18:27:38Z")

</div>

As per the subsequent confustion: “POSIX-like” means:

- 0 : SUCCESS
- != 0 : something else

For example:

```auto
// ++edit, I'm a moron
if (0 == pthread_mutex_lock(&mux))
{
    // SUCCESS
}
else
{
    // NOPE
}

```

That said, I was wrong to suggest that POSIX uses unsigned errno values. They are signed.

---

<div class="post-metadata">

**Author:** ![aentinger](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/aentinger/32/1428_2.png) [@aentinger](https://forum.opencyphal.org/u/aentinger)\
**Post date:** [September 21, 2020, 9:26am UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/13 "2020-09-21T09:26:53Z")

</div>

I don’t want to be negativ here (especially since I’m not contributing anything to the code generation PR) but I really can’t bring myself to like “POSIX-like” return values as defined in @scottdixon 's post above. Defining `0` as `SUCCESS` means you can’t use the implicit to bool-conversion. Using the `!` negation operator in the “usual way” leads to a semantically wrong result.

```auto
if (!pthread_mutex_lock(&muc))
{
  /* This is the success case :( */
}

```

Although I don’t like implicit conversion myself and generally try to avoid it, but the statement

```auto
if (0 == pthread_mutex_lock(&mux))

```

looks just horrible. Personally, I’d go for a signed return value with negative values encoding an error:

```auto
if (pthread_mutex_lock(&mux) < 0))
{
  /* error */
}
else
{
  /* success */
}

```

---

<div class="post-metadata">

**Author:** ![pavel.kirienko](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/pavel.kirienko/32/9_2.png) [@pavel.kirienko](https://forum.opencyphal.org/u/pavel.kirienko)\
**Post date:** [September 21, 2020, 9:36am UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/14 "2020-09-21T09:36:22Z")

</div>

> [@aentinger](#):
>
> Personally, I’d go for a signed return value with negative values encoding an error:

That would appear to be exactly what we’ve agreed upon and it’s freshly implemented in [Add C serialization support by davidlenfesty · Pull Request #115 · OpenCyphal/nunavut · GitHub](https://github.com/UAVCAN/nunavut/pull/115/commits/d37b0f04e5b6611d4624a04086a17364e4a2377f).

**EDIT:** Except for this bit. This is new:

```auto
/// Nunavut serialization does not define positive error values but may do so in the
/// future. Reserved positive error values are [1,127].

```

If we keep the “may do so in the future” then `<0` would be impossible to use. Also, I mentioned this earlier but consider this: libcanard will never be able to use positive error codes because positive values represent correct results, so using positive errors in Nunavut will be a departure from that convention. I suspect that it might be better to remove the statement about the possibility of positive error codes appearing in the future.

---

<div class="post-metadata">

**Author:** ![scottdixon](https://forum.opencyphal.org/user_avatar/forum.opencyphal.org/scottdixon/32/39_2.png) [@scottdixon](https://forum.opencyphal.org/u/scottdixon)\
**Post date:** [September 21, 2020, 5:15pm UTC](https://forum.opencyphal.org/t/c-serialization-api-survey/943/15 "2020-09-21T17:15:11Z")

</div>

> [@pavel.kirienko](#):
>
> I suspect that it might be better to remove the statement about the possibility of positive error codes appearing in the future.

Okay.
