The Boost C ++ Metaprogramming (Translate)

zhaozj2021-02-08  214

The Boost C Metaprogramming

Library

Aleksey Gurtovoy

Metacommunications

Agurtovoy@meta-comm.com

David Abrahams

Boost Consulting

David.abrahams@rcn.com

?

INTRODUCTION

The programs are typically defined as "generating procedures for other programs"; Parser Generators like YACC is one of the programs; YACC input is a language that meets the extended backus-naur form [ebnf] specification syntax, and output is able to resolve This grammar program (ie this grammar PARSER); Note In this example, the element program (YACC) is written by a language that cannot be directly described by the generated program, and we call Metadata The specification is written by Meta-Language, not by the C language; because the remainder of the user program typically requires a general-purpose programming system, and must interact with the generated Parser, so metadata is translated. It became a C language, which is connected to the system's remainder; Metadata has experienced two translational stages, and the user has to consciously maintain the boundaries between Metadata and program remaining parts.

?

1.1. Native language metaprogramming

The more interesting form of the programs exists in the language like Scheme, where the specification description being generated by the generated program is given by the same language as the metrorator itself; the meta-language defined by the programmer is the program used. A subset of the language syntax, the generation of the generated program can occur and the process of the user program remaining in the same translation phase; this allows the user to transparently transparent in "yuan program", "generated program", and "normal procedures" Switch between and usually do not know this conversion

?

1.2. Metaprogramming IN C

In C , it is almost accidentally, the template mechanism is discovered to provide a rich mechanism for compiled period;

In this section, we will explore the basic mechanisms and common technologies of C meta.

?

1.2.1. Numeric Computations

Non-Type Template Parameters makes the compile period integer operations; for example, the following template calculates the steps of its parameters (compile period):

Template

Struct Factorial

{

Static const unsigned value = n * factorial

:: Value;

}

Template <>

Struct Factorial <0>

{

Static const unsigned value = 1;

}

The above code snippet is called "MetaFunction", which is easy to see the contact between it and the running period calculation: MetaFunction "parameter" is passed as a template parameter, "return value" is a nesting static constant To define; because the Hard Line between the compiled period and the running period in C , the metab program looks different from the corresponding object of its runtime; like the Schame, the C yuan programmer is used with ordinary programs. Language (C ) to write a program, but only one subset of C all language features: those expressions that can be calculated in compile period; compare the above programs with direct runtime definitions: unsigned factorial (unsigned n)

{

RETURN N == 0? 1: n * factorial (n - 1);

}

It is easy to see the similarity of two recursive definitions; in general, recursive is more important to programming for C meta comparisons; compared to language like Lisp is a language, C programmers often need to avoid as much as possible. Recurns; this is not only because of the efficiency problem, but also because "cultural factors": recursion is difficult to understand (for C programmer); though, this C template mechanism is a functional programming language; Similarly, it also eliminates usage of cyclic variables to maintain cycles;

A key to the runtime and compile period multiplier function is the expression of recursive end conditions:

Our Meta-Factorial uses template specialization as a mode matching mechanism to describe behavior when n is equal to 0

The corresponding object in the Operational World will require two separate definitions of the same function (n equal to 0 and N is not equal to 0)

In this metal program, the impact of the second function template (the one of the specialization) is small, but in large yuan programs, the cost of understanding and maintenance of the end conditions will become huge.

Also note that the return value of a C MetaFunction must be "named;" Value "in this example is" Value ", is also the name used by all numerical return values ​​in the MPL; just like we will see, establish a unified return value for MetaFunction The naming mechanism is critical to the powerful functionality of the library.

?

1.2.2. Type Computations

How will we use our Factorial Metafunction? For Example, we can generate an appropriate size of array types to accommodate all arrangements of other types of objects.

// permutation_holder

:: Type is an Array Type Which Can Contain

// All Permutations of a Given T.

// unspecialized template for scalrs

Template

Struct Permutation_Holder

{

TypeDef T Type [1] [1];

}

// Specialization for Array Types

Template

Struct Permutation_Holder

{

TypeDef t type [factorial

:: Value] [N];

}

Here we introduce the concept of "type calculations"

As the Factorial, the Permutation_Holder template is a MetaFunction. However, this is different from the unsigned integer value, permutation_holder acceptance and return (as a nested typedef type) "; because the C type system provides more use Non- Type Template Argument (eg the integers) is far from a rich expression collection, most of the C yuan program consists of type computing

?

1.2.3. Type Sequences

The ability of programmed operation type collection is an important tool for many meaningful C yuan programs

Because this ability MPL supports very well, here we just briefly introduce the foundation

Later, we will re-review the following example and demonstrate how to use MPL to implement

First, we need a method to represent this container; an idea is to store types with structure:

Struct Types

{

Int t1;

Long T2;

Std :: Vector

T3;

}

Unfortunately, this arrangement cannot use C to our compile-based type (similar to Java's Reflection)

There is no way to find out what the name is, even if we assume that the name is given in accordance with the usual method, we have no way to know how many members; solve this problem is to improve the consistency; if we have one Methods You can get the first type of sequence, and the remaining sequence, then we will easily get all members:

Template

Struct cons

{

TypeDef first first;

Typedef rest rest;

}

Struct nil {};

Typedef

CONS

CONS

CONS

NIL

>>> my_types;

The structure described above MY_TYPES is the compiler of the one-way linked list, which is first introduced by czarnecki and eisnecker in [ce98]; now we have adjusted the original structure, at which time the C template mechanism can be one layer Peeling it, let's take a simple metaFunction that completes this feature; suppose users want to find a maximum type in any type of collection; we can use the recurrent MetaFunction that is now familiar with.

?

EXAMPLE 1. 'Largest' MetaFunction

//hoose the larger of two Types

Template <

Typename T1

, Typename T2

, BOOL Choose1 = (Sizeof (T1)> Sizeof (T2)) // Hands Off!

>

Struct Choose_Larger

{

TYPEDEF T1 TYPE;

}

// Specialization for the case where sizeof (t2)> = sizeof (t1)

Template

Struct Choose_Larger

{

Typedef t2 type;

}

// Get The Largest of a cons-list

Template Struct Largest; // Specialization To Peel Apart The Cons List

Template

Struct Largest

>

: choose_larger

:: Type>

{

// Type Inherited from Base

}

// Specialization for loop Termination

Template

Struct Largest

>

{

Typedef first type;

}

int main ()

{

//print the name of the largest of my_types

Std :: cout

<< TypeId (Largest)

:: Type) .name ()

<< std :: endl

;

}

There are several places in this code worth noting:

It uses some ad-hoc, deeper technology, or "Hacks"

The default template parameter choose1 (with "hands off!" Is tagged) is an example; without it, we will need another template to provide Choose_Larger's implementation, or we have to provide the template as parameters after we have to explicitly. Maybe not too bad for this example, but it will make Choose_Larger less, easier to make an error

Another HACK is divided from choose_larger; this is a measure to reduce code, which will prevent programmers to avoid writing "TypeName ... :: TypeName ... :: type type", etc. in the template body.

?

Even such a simple metach program has also used three separate off-shots

Largest MetaFunction uses two specialties; some people may look forward to this means there are two end conditions, but not this: one of them is only used to handle access to sequence elements; these specializes pass a single MetaFunction defines the spread of several C templates to make the code difficult to read; and because they are offset, they are not available for their compilers in C communities. use

?

However, these technologies are grade a very valuable part of any good C yuan programmer's weapon library; through the components commonly used in packages, the internal processing cycle ends, MPL reduces Tricky Hacks and Templates.

?

1.3. Why metaprogramming?

Ask people why people think so make sense; after all, even if there is a little deeper like Factorial Metafunction; to demonstrate how to use how to apply in work, let's take a simple example:

The following code produces an array that accommodates other array elements.

// can't return an Array In C , SO We need this Wrapper

Template

Struct Wrapper

{

Tx;

}

// Return An Array of the N! Permutations of 'in'

Template

Wrapper

:: Type>

All_Permutations (T Const & IN)

{

Wrapper

:: Type> Result;

// copy the unplrmutated array to the first result Element ELEMENT

Unsigned const n = sizeof (t) / sizeof (** result.x);

Std :: Copy (& * IN, & * IN N, Result.x [0]);

// enumerate the permutations

Unsigned const result_size = sizeof (result.x) / sizeof (t);

For (T * DST = Result.x 1; Dst! = Result.x Result_size; DST)

{

T * SRC = DST - 1;

Std :: Copy (* src, * src n, * dst);

Std :: next_permutation (* DST, * DST N);

}

Return Result;

}

Factorial's running period is unused in all_permutations above because the size of the C must be calculated in the compile period; however, there are some substitute programs, how can we avoid meticulous programming, what is the final conclusion?

?

1. We can write the program directly interpret the metadata

In our Factorial example, the array size can be a rule of the running period, so we can directly use the runtime FACTORALIAL function, but this means dynamic allocation, and it is usually expensive; further, you can override YACC Accept a function pointer, this pointer points to the function returns tokens in the restricted stream and the string containing the grammar; however, this method will result in an unacceptable runtime cost: parser or It has to be accepted by uncertain grammar, exploring every resolution, or has to copy the Substantia Table-Generation and optimized YACC for each time you entered in the runtime.

?

2. We can use our own calculations to replace the computation during compiler compilation.

After all, the size of the array passed to all_Permutations has always been a compile period, so it is also known to the user, we can ask the user to explicitly provide the type of result:

Template

Result All_Permutations (t const & infut);

The cost of this method is obvious: We abandon the expression capability (by requiring user explicit specified implementation details) and correctness (by allowing users to specify the result type of errors); any one hand-written parser form Will tell you that this method is not true for YACC.

?

In the same language, the metadata can be expressed in the same language, such as C , the expression ability is further enhanced: the user can directly call the element program, do not need to learn another syntax, do not need to interrupt your normal code flow

?

Therefore, the motivation of meta-programming comes from the following three factors: efficiency, expression ability, correctness; in the traditional program, there is always a pressure, while the expression ability and correctness, while the same is efficient, while In the World Programming World, we waive new weapons: We can transfer the calculations required to express the expression from the runtime period to the compile period?

1.4. Why a metaprogramming library?

Some people may still want to ask why we need a generic library:

?quality

The code for libraries for general purpose is often applicable to the purpose of the user. For the developers of the library, this is a central task; in general, the containers and algorithms given by any C standard library are compared. The container and algorithm given by a large number of specific projects are more flexible and achieve, because the development of the library is based on its own, rather than as other applications.

Optimization and improvement is easier to apply optimized and improved

? Multiplexed

More important than the code provided by any library, a well-designed generic library creates a CONCEPTS and IDIMS Framework, these Concepts and IDioms have established a solution to the problematic thinking of the problem; similar to C STL Gave our Iterator Concepts and Function Object Protocol, Boost MPL provides Type-Iterators and MetaFunction Class Protocol; a consider the Framework for Idioms, which is considered to consider the time to achieve detail with hand working, and let them concentrate Energy on hand issues

?portability

A excellent library can eliminate, cover up ugly platform differences; theoretical ambient library is completely generic, do not consider these problems, but in fact, the support of templates still has a lot of inconsistency, even after four years of standardization This may not be surprised: C template is the most profound, most complicated characteristics, greatly enhanced C meticulous ability

?interesting

The same idioms over again, the same IDioms is tired; it makes the programmer fatigue, reducing productivity; further, when the programmer feels tired, they will write a smaller, filled with bugs. Code; Usually the most useful library is a simple mode extracted from a massive programmer; MPL helps programmers to reduce tired by eliminating the repetition of a large number of sample code;

?

As people can see, the development of MPL is driven by the same, actual, real-world issues in the development of other libraries; maybe this is a sign, indicating that the template programming has completed the final preparation. , Is about to leave the deep anion, enter the verbal topic of each daily programmer

转载请注明原文地址:https://www.9cbs.com/read-2814.html

New Post(0)