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
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
Template
Struct value
{ENUM {r = value
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
Shift TypeInt
Left shift should make up, so we only have to attach the correct number of 0:
Template
Struct lshift
{Typedef Typename Lshift
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
}
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
}
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
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 {Typedef Typename and :: r * r;}; Template {Typedef Typename and :: r * r;}; Template {Typedef Typename and :: r * r;}; Template {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 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 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 } 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:. .