From 08b5a274615f4e62de0d914588b6f9b0505bb6cb Mon Sep 17 00:00:00 2001
From: Beman
A dealing with endianness usually implies a program portability or a data +portability requirement, and often both. That means real programs dealing with +endianness are usually complex, so the examples shown here would really be +written as multiple functions spread across multiple translation units. They +would involve interfaces that can not be altered as they are supplied by +third-parties or the standard library.
int
or unsigned short
to hold values. That
breaks the implicit invariant that the C++ language rules apply. The usual
language rules only apply if the endianness of the object is currently set to the native endianness for the platform. That can
-make it very hard to reason about complex logic flow, and result in difficult to
+make it very hard to reason about logic flow, and result in difficult to
find bugs.
+For example:
+ ++struct data_t // big endian +{ + int32_t v1; // description ... + int32_t v2; // description ... + ... additional character data members (i.e. non-endian) + int32_t v3; // description ... +}; + +data_t data; + +read(data); +big_to_native_inplace(data.v1); +big_to_native_inplace(data.v2); + +... + +++v1; +third_party::func(data.v2); + +... + +native_to_big_inplace(data.v1); +native_to_big_inplace(data.v2); +write(data); ++The programmer didn't bother to convert
+data.v3
to native + endianness because that member isn't used. A later maintainer needs to pass +data.v3
to the third-party function, so addsthird_party::func(data.v3);
+ somewhere deep in the code. This causes a silent failure because the usual + invariant that an object of typeint32_t
holds a value as + described by the C++ core language does not apply.
Endian buffer and arithmetic types hold values internally as arrays of characters with an invariant that the endianness of the array never changes. -That makes these types easier to use and programs easier to maintain.
+That makes these types easier to use and programs easier to maintain. +Here is the same example, using an endian arithmetic type:
++@@ -177,18 +247,18 @@ needed they are often very useful and workarounds are painful. For example,struct data_t +{ + big_int32_t v1; // description ... + big_int32_t v2; // description ... + ... additional character data members (i.e. non-endian) + big_int32_t v3; // description ... +}; + +data_t data; + +read(data); + +... + +++v1; +third_party::func(data.v2); + +... + +write(data); ++A later maintainer can add
+third_party::func(data.v3)
and it + will just-work.
Non-portable code like this:
-+
struct S {
-
- uint16_t a; // big endian
- uint32_t b; // big endian
- } __attribute__ ((packed));struct S { + uint16_t a; // big endian + uint32_t b; // big endian +} __attribute__ ((packed));+
Can be replaced with portable code like this:
-+
struct S {
-
- big_uint16_ut a;
- big_uint32_ut b;
- };
struct S { + big_uint16_ut a; + big_uint32_ut b; +};+ @@ -293,7 +363,7 @@ arithmetic approach.
Last revised: -02 January, 2015
+03 January, 2015© Copyright Beman Dawes, 2011, 2013, 2014
Distributed under the Boost Software License, Version 1.0. See www.boost.org/ LICENSE_1_0.txt