Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 29.  Arithmetic Types


29.1. Class Definition

Before we get stuck into the minutiae, we'll just take a look at the class definition. I decided, in order to avoid confusion with the two extant member variables lowerVal and upperVal, to use protected inheritance and derive a new type UInteger64. This is so we can cheat and use a union of uinteger64 and a uint64_t in order to perform the arithmetic. This relies on the correct layout of the two members in the structure, as follows:[1]

[1] Naturally there are implications for such a type in conversion to and from a byte stream, but they're the same for a built-in 64-bit integer.

Listing 29.1.


struct uinteger64


{


#if defined(ACMELIB_LITTLE_ENDIAN)


  uint32_t  lowerVal;


  uint32_t  upperVal;


#elif defined(ACMELIB_BIG_ENDIAN)


  uint32_t  upperVal;


  uint32_t  lowerVal;


#else


# error Need to discriminate further . . .


#endif /* endian */


};



This allows us to do the following monstrosity, which, as I said, is merely for expediency for this specific type, rather than a technique one would use for large integers in general:

Listing 29.2.


class UInteger64


  : protected uinteger64


{


public:


  typedef union


  {


    uint64_t    i;


    uinteger64  s;


  } ui64_union;


  ui64_union &get_union_()


  {


    uinteger64  *p1 = this;


    ui64_union  *p2 = reinterpret_cast<ui64_union*>(p1);


    return *p2;


  }


  ui64_union const &get_union_() const;


  . . .



It's not the way you'd start out and design a large integer class from scratch, where you'd not have the option of using a higher fundamental type in anything larger than 64 bits.

The test harness for this chapter consists of a template function within which a variety of numeric operations are performed. We'll go through them all, and see the ramifications for the design of our class.


      Previous section   Next section