Pan-type programming: type string (Typelists) and applications

zhaozj2021-02-16  51

Wild programming: type string (Typelists) and app Andrei AlexandRescu

It's hard to say how this started. I personally only say: Start me in the USENET newsgroup comp.lang.c . Moderated (by way of advice) See some posts, there is a weird template recursive. Later, when it encountered huge difficulties in the realization of the ABSTRACT FACTORY, some structure appeared in my mind. Others have also been independent of similar work because various types of strings are everywhere. Czarnecki and Eisnecker have introduced a type string that actually contains constant integer values ​​in [1]. The MPL library also defines a type string, and there are some algorithms for operation type strings. Modem C Design also introduces a simple type string implementation and uses it in at least five generic components (Functor, Visitor, AbstractFactory, StaticDispatcher, Tuple), is not two automatic class hierarchics (GenScatterHierarchy and GenlineArchy) ). The applicability of the type string is now certified. Know that type skewers will definitely improve your technology and help you better understand the current C libraries now. This article has again introduced the type string and fun program that can solve practical problems. You can't ask the type string as a Christmas husband asked it to Christmas gifts, but if you absorb any new knowledge of C like a block sponge, you can still find a lot of interesting things, so, continue to read. So what is a type string? Is the type string is also a strange strange template monster? In fact, type strings are too simple, nothing, C community does not use the type string so much unhealried. Template struct type; typef h head; typef t tail;} This is all of the type strings, really don't spend any strength. But with this small piece of code you can build a type string of any length. The technology used is the tail composition of LISP for a long time ago (Tail Composition)

TypeDef Typelist > floating_point_types;

You can do this because the type string is a type so that it can be used as a type of a large type string. According to conventions, a type string can only appear in another tail position of another type, and cannot be a head position; thus makes the operation of the type string easier, but does not reduce flexibility. We almost have to complete, we only need to define an auxiliary type, this auxiliary type as the end flag of the type string (similar to the NIL of Lisp). We call it null_typelist:

Class null_typelist {};

The correct style of the type string of this saving floating point type is:

TypeDef Typelist >> floating_point_types;

Line-shaped generation type string: three ways do not need to see any good eyes, as long as several members, the established type strings will become very ugly. All of these: Nested template instances, in order to avoid and >> Operators confused in adjacent> must be split, it makes the type string too difficult to use several methods for processing this problem, Loki [3] uses macro definitions The definition is as follows:

#define Typelist_1 (T1) TYPELIST (T1, Null_Typelist> #define Typelist_2 (T1, T2) Typelist #define Typelist_3 (T1, T2, T3 Typelist # Define Typelist_50 ...

Just like you can see, each macro definition relies on one. Loki defines the "constructor" of the type string with up to 50 members. More than 50, you either define a new macro definition, either manually connect the type string. I should know that the use of macro is not a good solution. Text conversion is very powerful, but the C preprocessor is too simple. When you think you have used it, it will always bite you behind it. In this example, the problem is that you cannot use these macros to be used with touchpads, even the simplest touchpad. for example:

TypedEf Typelist_2 (Vector , vector ) Storage_choices;

The preprocessor can handle parentheses, but cannot handle angle . In this case, Typelist_2 macro text contains 4 parameters for the pre-processor - (!) Vector , (3) Vector , not think Two two of you want. Worse, some preprocessors will not report an error - only quietly ignoring the parameters (3) and (4) and generate a unused macro show to make your headache. A better solution is to use templates to generate type strings. The following is an idea implementation:

Template strulist, null_typelist null_typelist> {typedef typefelist type;}

Template Struct Cons {Typelist > Type;}

Template Stuct Cons {Typelist > TYPE;}

Template Struct Con {Typelist >> TYPE;

The above example supports up to four types, you can use cons:

TypeDef Cons :: Type Floating_point_types;

The CONS template is based on partial specilization. When you instantiate CONS , first filled the default value to the fourth parameter of CONS, so it is actually cons , then, the compiler put this Examples and four defined specialty versions match. The first special version does not match because it requires T2 to be null_typelist, but the actual T2 here is Double, the same, the second specialization version does not match because T3 is wrong. The third special version is a valid candidate object because it accepts T1, T2, T3 as any type. This is not over, the compiler also checks the fourth special version, this special version can also match because it accepts T1, T2, T3, T4 as any type, so T4 is null_typelist no problem. But slowly, when you say "Compile During error", the third translation version has been selected because it is more applicable than the fourth. The algorithm that determines "more applicable" is quite complex, but intuitively, a partial special version of a touchpad is more suitable than another more general version (formerly a smaller scope of application). When using a practical parameter string to match the partial test version of several templates, the most priding versions of the parameters can be accepted. In our example, the third Cons version is selected because it matches cons instance, and it is more thased than the fourth. This is because the fourth accepts any type of corresponding T4, and the third is to accept T4 is null_typelist, so it is more special. You can extend CONS to accept any number of types, but unfortunately, you cannot extend the upper limit of cons. Either you use certain pre-processors again. The third method uses a function method to see below: Template struct cons;

Template struct cons {typef typefiPelist type;};

Template Struct Cons {Typelist > Type;};

Template Struct Cons {Typelist >> TYPE;};

Template Struct Cons {Typelist >> Type;

Now if you define a macro:

#define Typelist (a) cons :: type; you can write this:

TypeDef Typelist (Float, Double, Long Double) FLOATING_POINT_TYPE;

If you can succeed, people believe that two pairs of parentheses are so beautiful, this is really good. Ok, I know that I need to explain it. The first thing, void (*) (t1) is a C type (I have no misplaced words!): A function pointer accepts a parameter of T1, returns Void. This type is more familiar with VOID (* FUN) (T1), which actually contains a structure of the name. The CONS Template class is used to receive a function pointer for one, two, three or four parameters to pick up. Then Typelist macro opens this structure: Typelist (Float, Double, long double) becomes cons :: type is actually Typelist >> Now make a summary, the above solution is no perfect; only the language itself supports the variable template parameters, you will get clear, convenient, scalable, easy The advantage of use. The remainder of this article we will use the second method - the one of CONS <...> :: Type. It is time to start using the type string. Previous article [4] introduced how to calculate the length of the type string. Let's take a look at a new application of type strings for practical problems.

Improved (Ad-Hoc Visitation) Visitor Design Pattern provides a clear solution for complex issues. But its defect limits its application. Essentially, the visitor mode provides you with a safe way to increase the operation of the class level, without modifying this class hierarchy itself, the price is: When the class level changes, you have to change all of these operations. To know the details, look at the famous "design model" [5] or specially speaking C "Modern C Design" [3]. A rarely mentioned and access-related issues is: You must support access modes in the class level. This support refers to an accept member function in this support. In other words, if you can't rewrite a class hierarchy that does not support access, you can't access it. If you do not support access, and if you want to add an action in the class level, you don't rewrite it. Then you are destined to use the annoying Switch-ON-Type method. In the real world, it is very common: you use the base class in the old class library to increase your own inheritance class. Imagine, for example, there is a class level with documentItem as the root, real type to Textarea, VectorGraphics, and Bitmap. These are inherited directly from DocumentItem. In order to add actions SomeOperation, you can do this:

void SomeOperation (DocumentItem * p) {if (TextArea * pTextArea = dynamic_cast