CUJ: Popularization Knowledge: TypeInt

zhaozj2021-02-16  46

Popular knowledge: TypeInt

Stephen C. Dewhurst

(WQ Note: This is a thing that is shocked by Loki. It is really difficult to translate. Let everyone shocked, I will revise later.)

-------------------------------------------------- ------------------------------

In the nearest series of articles, we design and implement a TypeOf function for C language [Note 1]. Implementation requires that the type of expression in the compile period is converted to a constant value of a certain structure, and this integer value can be decoded back to the original type. The problem we have encountered is that the encoding is limited by the length of the integer value, because in order to achieve the necessary compile performance, the algorithm will be limited to the integer range (32 or 64 bits). And many "reasonable" types require the integer value of hundreds of BITS lengths. ("Unreasonable" type may require a large integer value).

Because the usual use of data abstraction techniques are not available in the compile period, we cannot implement an integer type of extended precision; we are limited to those operating on integer expressions. Our approach is to achieve a compile period arithmetic operation, manipulating a predefined integer set. For example, the left shifts a 4-way group integer value (which is combined by 4 regular expressions C1-C4), we can use the following code:

Typedef Shiftleft SL;

ENUM {

Code1 = sl :: Code1,

Code2 = sl :: Code2,

Code3 = sl :: Code3,

Code4 = sl :: Code4

}

This is feasible, but it is very ugly. We also have to choose a predefined maximum accuracy (4, for this example), and the complexity of the encoding is not affected in the dark.

As mentioned above, the compiler's strength and weakness are determined by the needs of the language being compiled by them. A C language compiler requires only the most basic compile period of arithmetic, but it requires a wide range of capabilities of the capable type. In fact, mostly designed compilers have a "type mathematical" concept, so types are manipulated in compile time with various "type mathematics". For example, when parsing a declaration of a name, the compiler combines the type of name from the declared qualifier and the declaration. When the name is manipulated in the expression, its type will be manipulated at the same time by other "types of mathematics".

This implies that it may be possible to do this: Use the integer value to expand the type, map the type of manipulation operation into a mathematical operation of the compiler to expand the extensive ability of the compiler in this area. We should then be able to apply the compilation algorithm of the extended precision on this "integer / type" abstraction. A portion of the result of extended accuracy can be mapped back to the corresponding complete type if needed.

Mapping integer value to type

Define a simple mapping from C to unsigned binary integers: 0 bits represent a pointer, indicating a point of the CONST. The basic type of the pointer is irrelevant, so we choose CHAR to show. There will be no limited / modified fundamental types will be characterized by a value zero. In order to support the growing typeint, we do not allow the preamble 0 bits (ie, there is no constant pointer) (WQ Note: .

For example, by this simple encoding method, we can use the unsigned binary integer 1011100 (decimal 92) to represent type char * const * * const * const * const * *. Let us call this type similar to the integer type TypeInt. It is easy to convert a small integer value to a Typeint [Note 2]:

Template

Struct Typeint {

Typedef Typename Select <

N & 1,

TypenAme Typeint <(n >> 1)> :: r * const,

TypeName Typeint <(n >> 1)> :: r *

> :: r r;

}

Template <>

Struct Typeint <0>

{Typedef char r;};

The Typeint template body is recursively determined the coding result of the high level of the integer N, then adds a * const (if low as 1) or * (if low is 0). Recursive ends in Typeint to 0.

Converting TypeInt into an integer is also very intuitive, if it corresponds to the integer value to correspond to a predefined integer:

Template

Struct Value

{ENUM {r = 0};

Template

Struct value

{ENUM {r = value :: r * 2};

Template

Struct value

{ENUM {r = value :: r * 2 1};

Here, the main body of the template is used to end recursive, and all "bit" (pointer indicators) have been separated from TypeInt. The offsetting will recursively calculate the equivalent of the integer obtained from the TypeInt, and then add one 0 or 1 bit according to the farthest pointer indicator.

COUT << value :: r> :: r << endl; // 92

Shift TypeInt

Left shift should make up, so we only have to attach the correct number of 0:

Template

Struct lshift

{Typedef Typename Lshift :: R * R;};

Template

Struct Lshift

{typedef t r;};

However, our TypeInt representation does not allow the preamble 0, so we must specialize in handling of type 0 (char) TypeInt:

Template

Struct Lshift

{Typedef char r;};

Finally, we have to provide a dischariracy to eliminate 0-bit TypeInt left shifts. Otherwise, it will occur in the offset between the offensive.

Template <>

Struct Lshift

{Typedef char r;};

In this implementation, the right shift is always completed. Because this implementation is based on the incoming parameters, there is no preamble 0, which means that simply reverse reference Typeint appropriate number [Note 3]:

Template

Struct Rshift {typedef typeename deref :: R>:: R r;

}

Template

Struct Rshift {

TypeDef T r;

}

Bit operation

Let's take a look at the implementation of "bit or" operation. "Bit plus" and other bit operations are similar. In this implementation, we use a compile period Switch ... case to distinguish between one or two parameters:

Template

Struct OR {

Enum {switchval = isptr :: r};

Typedef Typename Orimpl :: R r;

}

Since a zero Typeint indicates a modified CHAR base type, we can construct a switch ... case expression to determine if the bit value of the parameter is 0 (both the pointer, the value is 3), or only A parameter is not 0 (2), or only the second parameter is non-0 (1) (WQ note, the original is only the second argument is zero, the pen is wrong), or both parameters are 0 (0). You might be included, one or two parameters are 0 very simple:

Template

Struct Orimpl <0, a, b> {

Typedef char r; // 0 | 0 == 0

}

Template

Struct Orimpl <2, A, B> {

Typedef a r; // a | 0 == a

}

Template

Struct Orimpl <1, a, b> {

Typedef B R; // 0 | b == B

}

When the two parameters are not 0, it is more interesting:

Template

Struct Orimpl <3, A, B> {

TypedEf Typename Deref :: R DA;

Typedef Typename Deref :: r db;

TypedEf Typename or :: r Temp;

Typedef

Typename Select <

// Either a or b Has A 1 bit

Isconst :: r,

Temp * const,

Temp *

> :: r r;

}

First, we will refer to two parameters to remove their low position and recursively calculate the "bit or" result of the shortened TypeInt. Then add the "bit or" result of the low-end removal.

The Switch ... CASE of the compile period appears to be unnecessary. Another solution is to use thorough specialization. There are nine situations that need to be considered, listed below.

First Argument Second Argument Char Char Char B * Char B * Const A * CHAR A * B * A * B * Const A * Const Char A * Const B * a * const b * const Depending on the table, the template main body and 8 partial Charge is as follows:

Template

Struct or {typedef char r;};

Template

Struct or {typedef Typename or :: r * r;};

Template

Struct or {

TypedEf Typename or :: r * const r;

}

// 5 more Cases ...

Template

Struct or

In the implementation of "bit or", two implementation half a catty. In the implementation of "bit and", some cases can be merged, so achieving thorough specialization will be easier.

Template

Struct and // at Least One of A OR B IS 0

{Typedef char r;};

Template

Struct and

{Typedef Typename and :: r * r;};

Template

Struct and

{Typedef Typename and :: r * r;};

Template

Struct and

{Typedef Typename and :: r * r;};

Template

Struct and

{Typedef Typename and :: r * const r;};

Use and limit

Use the Typeint extended compile period to account for advantage over our earlier mechanism. The initial mechanism is Luo Wei, and is fixed in a given precision:

Typedef Shiftleft SL;

ENUM {

Code1 = sl :: Code1,

Code2 = sl :: Code2,

Code3 = sl :: Code3,

Code4 = sl :: Code4

}

TypeInt is relatively simple, and does not require the precision of the results in advance. Typeint will expand its precision when needed until the compiler's ability to limit:

Typedef lshift :: r code;

Unfortunately, there is a problem. The implementation of TypeInt is based on depth recursive, and many compilers cannot instantiate the recursive templates to sufficient depth. This seriously limits the maximum length of TypeInt. Although the minimum instantiated depth of the C language standard is only 17 layers (in standard Annex B), most compilers can handle at least 60 layers, while some have compiled options to allow up to 600-700 layers, more There are some, you can always instantiate the resource depletion (usually the result is a few thousand layers). We will look for methods in future column articles to process the recursive template instantiated depth restrictions. Complex multiplication

Before the end, let's take a look at the implementation of TypeInt's multiplication. We use the traditional shift to achieve it:

1 1 0 1 // a

x 1 0 0 1 // B

----------------

1 1 0 1

0 0 0 0

0 0 0 0

1 1 0 1

---------------

1 1 1 0 1 0 1 // a * b

The calculation of TYPEINTS is the same, but the readability is relatively poor:

CHAR * const * const * * const

X char * const * * * const

---------------------------------------

CHAR * const * const * * const

charr

charr

Char * const * const * * connection * * *

-------------------------------------------

CHAR * const * const * const * * const * * const

Direct implementation of the shift to equal multiplication is still simple, it is very sad:

Template

Struct mul; // no definition of primary

Template

Struct Mul // a * 0 == 0

{Typedef char r;};

Template

Struct Mul // 0 Digit, Just Shift

笺 笺 笺 笺 笺 笺 笺 / / / <<<<<<<<<<<< 1

{Typedef Typename Mul :: r * r;};

Template

Struct Mul {// 1 Digit, Shift and Add

笺 笺 笺 笺 笺 笺 笺 笺 笺 笺 笺 笺 笺 =

Typedef Typename Mul :: r * TMP;

TypeDef Typename Add :: R r;

}

Template

Struct Mul // to avoid a char * typeint

{typedef a r;}; // no Leading Zeros!

Unfortunately, this algorithm has a secondary (compile period!) Complexity, not the linear complexity of other operations. As a result, when the 35-bit Typeint multiply, on a typical workstation, there is a compelling compilation delay, and at least one lunch break is required for 200-bit TypeInt.

Notice

Next time, we will see the implementation of multiplication and investigate many Template metaprogramming technology, which can dramatically improve performance. Next, the method of solving the depth restriction of the template instantiation will be explored. Note

[1] S.c. Dewhurst, "CommON Knowledge: a Bitwise TypeOf Operator, Parts 1-3," C / C USERS JOURNAL, AUGUST, OCTOBER, AND DECEMBER 2002.

[2] The full implementation of this preliminary typeint facility is available on the author's website:. The reader may find in the same location a reimplementation of the typeof facility that employs typeints for extended- precision compile-time arithmetic. The Select template is borrowed (in modified form) from Andrei Alexandrescu's Loki library. It performs a compile-time if statement, selecting its second argument if the first argument is true, and its third argument if the first argument Is false.

.


New Post(0)