From 08b5a274615f4e62de0d914588b6f9b0505bb6cb Mon Sep 17 00:00:00 2001 From: Beman Date: Sun, 4 Jan 2015 09:13:15 -0500 Subject: [PATCH] Add examples. --- doc/choosing_approach.html | 100 +++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 15 deletions(-) diff --git a/doc/choosing_approach.html b/doc/choosing_approach.html index 338aa97..abb2836 100644 --- a/doc/choosing_approach.html +++ b/doc/choosing_approach.html @@ -94,8 +94,17 @@ study engineering trade-offs, use endian arithmetic types. They are safe, easy to use, and easy to maintain. Use the -anticipating need design pattern locally around performance hot spots like lengthy loops, -if needed. 

+anticipating need design pattern locally around performance hot spots +like lengthy loops, if needed.

+ +

Background

+ +

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.

Characteristics

@@ -111,12 +120,73 @@ alignment requirements.

types like 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 adds third_party::func(data.v3); + somewhere deep in the code. This causes a silent failure because the usual + invariant that an object of type int32_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:

+
+
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.

+
@@ -177,18 +247,18 @@ needed they are often very useful and workarounds are painful. For example,

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