"C ++ Template Metaprogramming" Appendix A - Pretreatment Metal Programming

xiaoxiao2021-03-06  62

"C Template Metaprogramming"

Appendix A: Pre-processing meta programming

By David Abraham

(http://www.boost.org/people/dave_abrahams.htm)

BY Liu Weipeng (Pongba)

C Louvre (http://blog.9cbs.net/pongba)

Original link (http://www.boost-consulting.com/mplbook)

The template programming is the most abstract abstraction in C , and is also the most powerful ability. The "pretreatment meta-programming" said in this chapter is interested in programming with template meta. I believe that many people don't realize that C / C preprocessors have this ability, just like many people dream when they just learn C templates. I didn't expect that there are so many derivative technologies ...

motivation

Even with template programming and powerful capabilities of Boost Metaprogramming Library are available for us, some C programming tasks still need to repeat the pattern-type code. Chapter 5 Tiny_Size is an example:

Template

Struct tiny_size

: INT_ <3> {};

The repetition mode in the template parameter list of the top main template is thrown [1] whether there is three offset version below, which follows a conventional mode:

Template

Struct tiny_size

: MPL :: INT_ <2> {};

Template

Struct tiny_size

: MPL :: INT_ <1> {};

Template <>

Struct tiny_size

: MPL :: INT_ <0> {};

In this case, there is only a small part of our code with this "mechanical taste", but we have to repeat the rest of the code [2], in some cases (for example, if we implement Large Instead of tiny, "the rest" code may be considerable. When a model repeated two or more, manual writing will easily lead to an error. Perhaps more importantly, the code will become difficult to read, because the important abstract part in the code is actually that pattern, but not follow each code segment of this mode.

Code generation

Let's write your hand! "Mechanical" code should be (and can also be mechanically "generated. For the author of the library, write a program that can generate a piece of code that follows a specific mode, and then faces two options: First, the pre-generated source code file is released, and the second is the release generator itself. Both have a disadvantage. If the customer only got a pre-generated source code, then they are restricted - experience shows that these pre-generated code snipples may be enough today, and tomorrow is not enough! On the other hand, if the customer gets the generator, then they also need a program (for example, an interpreter) that can be used to execute the generated program, and the latter must be integrated into the build process, unless ...

Prepaid

... unless the generated program is "pretreatment element" [3]! And "Explanation)" The programs are the C / C preprocessor, although they are not designed for this purpose. The user can control the code generation process by #define or the -d (compiled in the command line). This avoids the problem of modifying the Build process mentioned above, because the "pre-processing metrorator" interpreter is the pre-processor! For example, we can parameterize the above Tiny_SIZE master template as follows: #include

#ifndef tiny_max_size

# Define tiny_max_size 3 // default maximum size is 3

#ENDIF

Template

Struct tiny_size

: MPL :: INT_

{};

To test this metrorator, you can switch the compiler to the Prerequisites (using the -e option) while ensuring that the root directory of the Boost is in the #include path. For example [4]:

G -p -e -ipath / to / boost_1_32_0 -

I.

Test.cpp

With proper metrorats, we can not only adjust the number of TINY_SIZE template parameters, but also adjust the maximum size of Tiny - as long as #define tiny_max_size is the right value.

Boost Preprocessor Library [MK04] acts as a role in the pre-process meta programming and MPL acts as a role acting in template. It provides a high-order component Framework (for example, boost_pp_enum_params), making the metastogramming task easy to complete - if there is no Framework, meticulous programming may be very painful :-( In this appendix, we don't go The details of the pre-processor work or the general principles of pre-processing meta-programming or a number of details of the BPL library work, but to show you this library at a higher level, so that you can effectively use it, and Explore the remaining parts.

Basic concept of preprocessors

We start discussing template programming in the second chapter - describing metadata (potential template-arguments) and meticulum (class template), and constitutes a general view of compile calculation on the basis of these two basic concepts . In this section, we will introduce the pre-process meta programming in the same way.

Here we introduce may only be reviewed for you, but before you continue, it is necessary to reiterate these basic concepts:

Pretreatment mark (token)

For preprocessors, the most basic unit of data is a pre-processing tag. The pretreatment tag is approximately corresponding to the identifier (Identifier) ​​of C , operator symbol, and literal or the like. It is technically said that the pretreatment markers and formal markers are distinguished (see C standard section 2), but we can ignore our discussion. In fact, this will not be distinguished here.

Macros

Macro has two style. One and object Similar to:

#define Identifier Replacement-List

Here Identifier is the name of the macro, Replacement-List is a sequence of one or more tokens. All Identifier places where Identifier in the later program will be expanded to Replacement-List. The other is the macro of the function style, it is like "a meta-time function":

#define Identifier (A1, A2, ... A) Replacement-list

Here, each AI represents a name of a macro-shaped (parameter). If the macro is used in the later program and gives the appropriate physical parametric, it will be extended to Replacement-List - where the macro-shaped place will be replaced with the user. Macro ginseng [5].

Macro Reed (argument)

definition

Macro Requirements are non-empty sequences of two preprocessing markers:

1. Pre-regulation markers other than talent or parentheses

2. A set of pretreatment markers surrounded by a pair of parentheses

This definition has an important impact on pre-processing meta programming. Note that first, the following two tokens are special:

()

Therefore, a macro ginseng cannot contain unpaired parentheses, or a comma that is not surrounded by parentheses. For example, in the sample code below, the two lines of code behind the definition of foo are Ill-Formed:

#define foo (x) x // unary Identity Macro

Foo (,) // un-parenthesized comma or two empty arguments

Foo ()) // unmatched parenthesis or missing argument

At the same time, it should also be noted that the following TOKENS is not a special-preprocessor's pair of parenthesis, square brackets, and polar brackets.

{} [] <>

So, the following two lines of code are ILL-FORMED:

FOO (std :: pair ) // is interpreted as "," separated two parameters

FOO ({INT x = 1, y = 2; returnix x y;}) //

And if you add a pair of redundant parentheses surrounded by parameters, the code is correct:

Foo ((std :: pair ) // one argument

FOO (({INT X = 1, y = 2; returnix x y;}) // one argument

However, due to the special meaning of commas, it is not possible to remove the parentheses when [6] is not understood without understanding a macro-separated marker sequence [6]. If you write a macro, let it accept macro-ginseng containing any multiple commas (similar to the variable length parameter list in C, then there are two options for users who use the macro:

1. Enclose the unscrupulous token with parentheses and separate the number of TOKEN sequences in which it is another parameter.

2. Encode information into a pre-process period (this chapter will be mentioned later).

BPL library structure

In-depth investment in the category of BPL libraries, here we will give you a deep understanding of the BPL "Tools": You need to use the BPL's electronic document - the INDEX.htm under the Boost_Install / Libs / Preprocessor directory.

Open, you will see the index on the left of the browser, click on the "Headers" link, you will see the structure of the entire library. Most headers are organized under the same subdirectory based on the function. The top-level directory only contains some universal macro's header files and header files corresponding to each subdirectory (this header file only contains the header files in the subdirectory, for example, Boost / PreProcessor / Selection.hpp contains Serance Two header files max.hpp, min.hpp in subdirectory. There is no header file corresponding to any subdirectory declares a macro (with the Boost_pp prefix) with the file name. For example, Max.hpp declares the Boost_pp_max macro. You will notice that a header file will declare an additional macro, which is _d, _r, or _z suffix [7]. For example, the BOOST_PP_MAX_D macro is also declared in Max.hpp. In this chapter we will ignore these macros. If you want to know why they exist and how to optimize the pretreatment speed, you can refer to the Reentrant section of the Topics section of the electronic document.

Basic concept of BPL library

In this section we will discuss the basic concepts of the BPL library and give some simple examples.

repeat

We can use the boost_pp_enum_params macro to generate Class T0, Class T1, ..., Class TN (with a specific mode) duplication, which matches the horizontal repetition concept. There is also a longitudinal repetition concept in the BPL, we will introduce later. Macros that perform horizontal duplicate can be found in the retection subdirectory of the library.

Repeat in horizontal

To use the transverse repeat generation of TINY_SIZE version, we can write this:

#include

#include

#include

#define tiny_print (z, n, data) Data

#define tiny_size (z, n, unused) /

Template /

Struct tiny_size

Boost_pp_enum_params (n, t) /

Boost_pp_comma_if (n) /

BOOST_PP_ENUM (/

Boost_pp_sub (tiny_max_size, n), tiny_print, none /

> /

: MPL :: INT_ {};

Boost_pp_repeat (tiny_max_size, tiny_size, ~)

#undef tiny_size

#undef tiny_print

The code generated from boost_pp_repeat, boost_pp_repeat is a high-order macro, which will repeat the Tiny_SIZE macro, which is its second parameter. Its first parameter indicates the number of repetitions. The third parameter can be arbitrary, it will be transmitted to the macro that is called, which is tiny_size, and tiny_size does not use it, so "~" can be any [8]. Tiny_SIZE Mobs generates a different version of TINY_SIZE every time by boost_pp_repeat call. Tiny_SIZE Macro accepts three parameters:

Z and the _z macro mentioned earlier. It is only used for optimization purposes. We can ignore it.

n means that it is currently repeated as a few times. In the process of repeating Tiny_Size each time, N is 0, 1, 2 ....

Unused, for this example, in each repeating "~".

Typically, boost_pp_repeat passed the user to its parameter into the original post to the called macro (for example, tiny_size). Because the replacement text like Tiny_Size has a few lines, in addition to the last row, other rows ends with a backslash "/". The few lines started to call Boost_pp_enum to generate a comma-separated (template parameter) list, so Tiny_Size will generate a similar code each time the call [9]:

Template

Struct tiny_size <

T1, T2, ... TN-1

... more ...

>

: MPL :: INT_ {};

Boost_pp_comma_if macro If the acceptable parameter is non-zero, it will generate a comma, otherwise it is empty. When N is 0, boost_pp_enum_params (n, t) does not generate, and the boost_pp_comma_if (n) immediately following it is also empty, because "<" is directly kept directly, "is illegal.

Later, boost_pp_enum generates tiny_max_size-n-separated "none". In addition to each repetition, Boost_pp_enum and Boost_pp_repeat have no difference, so its second parameter (here is tiny_print) must be the same as Tiny_Size's format. In this example, tiny_print ignores the number of times the current repetition, but always replaces itself with the second parameter, which is "None".

Boost_pp_sub realizes TOKEN subtraction. Although the preparation instrument itself can be evaluated to the compile period:

#define x 3

...

#if x - 1> 0 // ok

WhatVER

#ENDIF

However, the preparation element can only operate tokens - know this. Typically, when a macro in the BPL needs to accept a numeric parameter, the numerical parameter must be passed in the form of a single token. If we write tiny_max_size-n instead of Boost_pp_sub (tiny_max_size, n), the first parameter of Boost_pp_enum contains three tokens: 3-0 (or 3-1, or 3- 2. What is duplicated in terms of specifically? Boost_pp_sub can generate a single TOKEN, for boost_pp_sub (3, 0), which is generated 3, boost_pp_sub (3, 1) is 2, which is later. Vertical repeat

If you prefer the previous example, the result will be like this:

Template <> STRUCT TINY_SIZE : MPL :: INT_ <0>

{}; Template struct tiny_size :

MPL :: INT_ <1> {}; Template Struct Tiny_size

: MPL :: INT_ <2> {};

These codes are all squeezed in one line - this is the characteristics of horizontal repetition. For some tasks, such as generating a Tiny_Size master template, this does not have any problems. But the generation of the specialization version is different, which will at least lead to two shortcomings:

1. If you do not reorganize the result code, it is difficult to verify our metrorats to do the right thing.

2. Embedded horizontal repetition is larger in different pre-compilers. For Tiny_Size, each of the transverse repeatedly generated versions contains three other horizontal repetitions, two of which are boost_pp_enum_params, and one is boost_pp_enum. If tiny_max_size is 3, the problem may not be large, but in the precompiler currently used, at least one will slow down when tiny_max_size reaches 8 [10].

Solutions to solve these problems are naturally repetitive. Longitudinal repeat can be generated in different lines of code with a specific mode. BPL provides two ways to repeat: local iterations and file iterations.

Local Items

The most direct way to demonstrate local iteration is that in our example, the call to Boost_pp_repeat will be replaced with the following call:

#include

#define boost_pp_local_macro (n) tiny_size (~, n, ~)

#define boost_pp_local_limits (0, tiny_max_size - 1)

#include boost_pp_local_iterate ()

The local iteration will repeat the user-defined boost_pp_local_macro macro, and its parameters are iteration index. Because we have defined Tiny_SIZE macros, just let Boost_pp_macro call it. The iterative section is given by the boost_pp_local_limits macro, which must be expanded to two integers surrounded by parentheses, representing a closed interval, and each integer within the closed interval is sequentially transmitted to Boost_pp_local_macro. Note that the replacement of Boost_PP_LOCAL_LIMITS here can include integrated expressions composed of multiple tokens (for example, Tiny_max_size - 1 contains three tokens), which is rare in the BPL, which is one of them. Finally, the entire iterative process starts from when the entire iterative process begins: starting from the last line of the code segment above, boost_pp_local_iterator () is a macro, which is replaced with a file name, which is located in the BPL library [ 11]. You will be surprised to find that many pre-processor processes repetitive files that are faster than those compared to nested horizontal repeats.

Now, if we throw a new example to the preprocessor, you will get the following results:

Template <> STRUCT TINY_SIZE : MPL :: INT_ <0>

{};

Template Struct tiny_size : MPL ::

INT_ <1> {};

Template Struct Tiny_size

>: MPL :: INT_ <2> {};

The above code is located in different three lines, respectively. The code format has been greatly improved, but it is still not ideal. With the increase of tiny_max_size, it will become more and more difficult to verify that the code is in line with what we mean. If you have tried to use the debugger single step to track a function of the macro generated function, you will understand that what is a depressed experience: the debugger stops the place where the macro is finally called (not a macro definition) So you can't know what code has been generated. Worse, all the code in the generated function is crowded!

File iteration

Obviously, the line between the "sample" code and the generated code is the necessary premise of debugging. The file iteration mode generates a code entity that complies with a mode by repeating the same source file ("Model" code source file, each containing different code entities). File iteration The impact of debugging ability is the same as C template: although different instances of "template" code in the debugger look overlap in the same row [12], but in a sense, we finally can single-step tracking functions The source code.

To take file iteration in our example, we can replace the previously part of the code and the definition of tiny_size to the following:

#include

#define boost_pp_iteration_limits (0, tiny_max_size - 1)

#define boost_pp_filename_1 "tiny_size_spec.hpp"

#include boost_pp_iterate ()

Boost_pp_iteration_limits and boost_pp_local_limits are the same, allow us to provide an iterative closed section. Boost_pp_filename_1 indicates the file name of the repeated #include (later I will show you the file). Suffix _1 means this is the first "template" file for file iteration [13] - if we want to file another "sample" file in tiny_size_spec.hpp, we should use Boost_FileName_2, To avoid confusion. Tiny_size_spec.hpp's content You should be familiar, the most of its definitions of the Tiny_Size macro, only the "/" of the end of each line is not overpassed:

#define n Boost_pp_Isteration ()

Template

Struct tiny_size <

Boost_pp_enum_params (n, t)

Boost_pp_comma_if (n)

Boost_pp_enum (boost_pp_sub (tiny_max_size, n), tiny_print, none)

>

: MPL :: INT_ {};

#undef n

Every time it iterates, BPL libraries will pass the current iterative indicator (Itemion INDEX) to us, "#define n boost_pp_iteration ()" "#define n boost_pp_iteration ()" "#define n boost_pp_iteration ()" it 简 " The syntax of the sample "code is more concise. Note that we did not use "Contains House [14]" in tiny_size_spec.hpp, because the header file is to be included and pre-processed, each time it generates a different Code entity.

Now, the pre-processing result is finally completely reserved structure of the "template" code (see the result code below), which is easy to verify for large Tiny_max_size. For example, when tiny_max_size is 8, the following code is taken from the output of the GCC pretreatment phase:

...

Template

Struct tiny_size <

T0, T1, T2, T3

,

NONE, NONE, NONE, NONE

>

: MPL :: INT_ <4> {};

Template

Struct tiny_size <

T0, T1, T2, T3, T4

,

None, None, None

>

: MPL :: INT_ <5> {};

.

Own

It is more simplified than file iteration, which is the same as the file iteration. For file iteration, we need to introduce a "template" code, even if these code is very simple, you must create a new file (for example, tiny_size_spec.hpp), which is not convenient enough. Fortunately, the BPL library provides a macro that allows us to put the "template" code directly in the file that triggered iterations, in other words, trigger iteration directly in the "template" code file. The key to achieving this ability is boost_pp_is_iterating macro - if we are in iteration, boost_pp_is_iterating is extended to a non-zero value, we can use this value to select different parts in the file - the part or "partial or" " The model "code section. Below is a complete tiny_size.hpp file demonstrating from iteration. Pay special attention to the use of tiny_size_hpp_included "including the whistle" and the location of the use. #ifndef boost_pp_is_iterating

# I iDef tiny_size_hpp_included

# Define tiny_size_hpp_included

# Include

# Include

# Include

# Include

# ImpNDEF TINY_MAX_SIZE

# Define tiny_max_size 3 // default maximum size is 3

# Endif

// Primary Template

Template

Struct tiny_size

: MPL :: INT_

{};

// Generate Specializations

# Define boost_pp_IpectEration_limits (0, tiny_max_size - 1)

# Define boost_pp_filename_1 "tiny_size.hpp" // this file

# Include boost_pp_iterate ()

# Endif // Tiny_Size_HPP_INCLUDED

#ELSE / / BOOST_PP_IS_ITERATING

# Define n Boost_pp_iteration ()

# Define Tiny_Print (Z, N, DATA) DATA

// Specialization Pattern

Template

Struct tiny_size <

Boost_pp_enum_params (n, t)

Boost_pp_comma_if (n)

Boost_pp_enum (boost_pp_sub (tiny_max_size, n), tiny_print, none)

>

: MPL :: INT_ {};

# Undef tiny_print

# Undef n

#ENDIF / / BOOST_PP_IS_ITERATING

More

Regarding the file iteration, we only spend a small part. To get more details, we recommend you to read Boost_PP_ITERATE in the BPL library and the electronic documentation with the macro related to it. And attention to it is that for "code repeat", no technique is "absolute" best: your choice will depend on convenience, verifiable, tonable, compilation, and your own The feeling of logical coherence. Naming Conventions

Note that tiny_size and tiny_print were immediately downloaded immediately after being used, there is no

The header of #include's header. Therefore, they can be seen as a "local" macro definition. Because the pre-regulator is ignored to the existence of the scope, it is necessary to carefully select the name to prevent the name conflict. We recommend using prefixed_lower_case as the name of the local macro, while prefixed_upper_case is a global. The only exception is just a name lowercase letter, you can use it as a local macro name: no other header file will define a global single letter lowercase macro - that is a very bad style.

Arithmetic, logic, comparison operation

As we mentioned earlier, many macros in the BPL pretreatment library require parameters to contain only a single token, and when these parameters are used for mathematical calculations, the use arithmetic expressions are not available ( For example: "A B * C" is not the - translator), in order to rectify the BPL use boost_pp_sub macro to two digital token to subtraction - just like in the tiny_size example. The BPL's "/ arithmetics /" subdirectory contains a macro for non-negative count arithmetic operations:

Arithmetic operation in BPL pretreatment library

expression

result

BOOST_PP_ADD (X, Y)

X Y

BOOST_PP_DEC (X)

X - 1

BOOST_PP_DIV (X, Y)

X / Y

BOOST_PP_INC (X, Y)

X 1

BOOST_PP_MOD (X, Y)

X% Y

BOOST_PP_MUL (X, Y)

x * y

BOOST_PP_SUB (X, Y)

x - y

The "/ logical /" subdirectory contains the following Boolean logic operations:

Logical operation in the BPL pretreatment library

expression

result

BOOST_PP_AND (X, Y)

X && y

BOOST_PP_NOR (X, Y)

! (x || y)

BOOST_PP_OR (X, Y)

X || y

BOOST_PP_XOR (X, Y)

(BOOL) X! = (bool) y? 1: 0

Boost_pp_not (x)

X? 0: 1

Boost_pp_bool (x)

X? 1: 0

The following is a more efficient operation, but their operands must be 0 or 1 (representing a binary bit (bit))

expression

result

BOOST_PP_BITAND (X, Y)

X && y

BOOST_PP_BITNOR (X, Y)

! (x || y)

BOOST_PP_BITOR (X, Y)

X || y

BOOST_PP_BITXOR (X, Y)

(BOOL) X! = (bool) y? 1: 0

BOOST_PP_COMPL (X)

X? 0: 1

Finally, the "/ commit /" subdirectory provides an integer (TOKEN) comparison:

Comparison operation in BPL pretreatment library

expression

result

BOOST_PP_EQUAL (X, Y)

X == y? 1: 0

Boost_pp_not_equal (x, y)

X! = y? 1: 0

BOOST_PP_SSS (X, Y)

X

BOOST_PP_ESS_EQUAL (X, Y)

X <= y? 1: 0boost_pp_greater (x, y)

X> Y? 1: 0

BOOST_PP_GREATER_EQUAL (X, Y)

X> = y? 1: 0

Since we usually need to choose one in several available comparison operations, it should be recognized that boost_pp_equal and boost_pp_not_equal have time complexity to O (1) and other comparison operations are usually slower.

Control structures

In the "/ control /" subdirectory, the BPL library provides a Boost_pp_if (C, T, F) macro, which is the role and MPL :: if_ similar to. In order to explore the meaning of "control", we are generated from a generic function object frame - Boost.Function library - generates code. Bavage version of the function type of each of the number of different parameters (the upper limit of the number of parameters it supports) is prepared by a molar version:

Template struct function; //primary template

Template // arity = 0

Struct Function

Definition Not Shown ...

Template // arity = 1

Struct Function

Definition Not Shown ...

Template // Arity = 2

Struct Function

Definition Not Shown ...

Template // Arity = 3

Struct Function

Definition Not Shown ...

ETC ....

As we have talked about some strategies can be used to generate the above code mode, so this is not a lengthy review. We can use the file iteration method used by Tiny_Size:

#ifndef boost_pp_is_iterating

# IFNDef Boost_Function_HPP_INCLUDED

# Define boost_function_hpp_included

# Include

# Include

# ImpNDEF FUNCTION_MAX_ARITY

# Define function_max_arity 15

# Endif

Template struct function; //primary template

// Generate Specializations

# Define boost_pp_IpectEration_limits (0, Function_Max_arity)

# Define boost_pp_filename_1 "Boost / Function.HPP" // this file

# Include boost_pp_iterate ()

# Endif // boost_function_hpp_included

#ELSE // boost_pp_is_iterating # define n Boost_pp_IpectEration ()

// Specialization Pattern

Template

Struct Function

Definition Not Shown ...

# Undef n

#ENDIF / / BOOST_PP_IS_ITERATING

[BOOST_PP_ENUM_TRAILING_PARAMS used above generates a preamble ",", other aspects, is similar to boost_pp_enum_params when its first parameter is not 0.

Argument Selection

In order to implement interoperability between algorithms in the C standard library - If the Function accepts one or two parameters can inherit the appropriate std :: unary_function or std :: binary_function [15]. Boost_pp_if is a powerful tool when dealing with these special circumstances:

# Include

# Include

// Specialization Pattern

Template

Struct Function

BOOST_PP_IF (

Boost_pp_equal (n, 2),: std :: binary_function

, Boost_pp_if (

Boost_pp_equal (n, 1),: std :: unary_function

, ... EMPTY ARGUMENT ...

)

)

{... class body omitted ...};

Hey, unfortunately, we have encountered problems together: First, you can't pass an empty parameter (... EMPTY Argument ...) to macro. Second, because the spare brackets are not specially treated by the preprocessor, std :: unary_function (or binary_function <...>) special version of the comma is seen as a sign of separation macro parameters, thus The processor complains that there are too many parameters we pass to Boost_pp_if.

Let us first look at the burst_pp_if call. MPL :: EVAL_IF Policies (Select a no-reflection "to call (EVAL, also evaluation)) can be used here, there is no macro similar to MPL :: EVAL_IF in the BPL, but in fact, it does not need MPL :: Eval_if's things: We can solve this problem as long as you add a parentheses behind boost_pp_if.

#define boost_function_unary (): std :: unary_function

#define boost_function_empty () // Nothing

...

, Boost_pp_if (

Boost_pp_equal (n, 1), boost_function_unary

, Boost_Function_empty

) ()

#undef boost_function_empty

#undef boost_function_unary

A "macro that does not generate" is usually useful in many places, so that a ready-made: boost_pp_empty is provided in the bpl 's Facilities. Ok, now we can correct the error above - we can put boost_function_binary (), Boost_Function_unary (), and boost_pp_empty (), have been delayed until the periphery Boost_pp_if call (expand), because std :: binary_function also there is a "comma problem": # include

# Define boost_function_binary (): std :: binary_function

# Define boost_function_unary (): std :: unary_function

// Specialization Pattern

Template

Struct Function

BOOST_PP_IF (

Boost_pp_equal (n, 2), boost_function_binary

, Boost_pp_if (

Boost_pp_equal (n, 1), boost_function_unary

, Boost_pp_empty

)

) ()

{

... Class Body Omitted ...

}

# Undef boost_function_unary

# Undef boost_function_binary

# Undef n

Note that because we happen to use the file iteration, we can also use the value of N directly to use #if:

Template

Struct Function

#if n == 2

: std :: binary_function

#ELIF n == 1

: std :: unary_function

#ENDIF

Boost_pp_if allows us to encapsulate the code logic to a multiplexed macro (with n as parameters), which is consistent with all repetitions:

#define boost_function_base (n) /

Boost_pp_if (boost_pp_equal (n, 2), boost_function_binary /

, Boost_pp_if (boost_pp_equal (n, 1), Boost_Function_unary /

Boost_pp_empty /

) /

) ()

Other selective structures

Boost_pp_identity also belongs to "facilities", it is a "mirror" of boost_pp_empty:

#define boost_pp_idententity (tokens) tokens boost_pp_empty

You can see it as a back tokens that returns to tokens - When the pair of empty brackets are added, the boost_pp_empty is exposed to empty, thereby leaving only tokens. If we want to inherit from MPL :: Empty_Base, you can use boost_pp_identity: // specialization pattern

Template

Struct Function

BOOST_PP_IF (

Boost_pp_equal (n, 2), boost_function_binary

, Boost_pp_if (

Boost_pp_equal (n, 1), boost_function_unary

, Boost_pp_identity (: MPL :: EMPTY_BASE)

)

) ()

{

... Class Body Omitted ...

}

There is also a macro to be aware of Boost_pp_expr_if, which decides whether to expand to its second parameter according to its first parameter (a Boolean value):

#define boost_pp_expr_if (c, tokens) /

Boost_pp_if (c, boost_pp_identity (tokens), boost_pp_empty) ()

For example, boost_pp_expr_if (1, foo) is expanded to FOO, while boost_pp_expr_if (0, foo) is expanded.

Token Paste (token Pasting)

If there is a general way to access the parameter type and return type of all function objects (instead of only one yuan or binary function)! Use a metaFunction "encoded" Signature " It is a feasible solution for an MPL sequence. We only need to give a special version of a Signature class template for each different "yuan".

Template struct signature; // primary template

// Partial Specializations for Boost :: Function

Template

Struct Signature >>

: MPL :: Vector1 {};

Template

Struct Signature >>>>

: MPL :: Vector2 {};

Template

Struct Signature >>>>>

: MPL :: Vector3 {};

...

To generate these specialty versions, simply add the following code "template":

Template

Struct Signature >>>>>>>

: MPL :: Boost_pp_cat (Vector, N) <

R, boost_pp_enum_trailing_params (n, a)

> {};

Boost_pp_cat implements the Token paste - it "sticks" it "sticks" into a single token. Because this is a universal macro, it is located in Cat.HPP (this file is located at the top of the BPL directory tree). Although the preprocessor has a built-in Token Paste Operator "##", it can only be used in macro definitions. If we use it here, there will be no effect at all:

Template

Struct Signature >>

: MPL :: Vector ## 1 {};

Template

Struct Signature >>>>

: MPL :: Vector ## 2 {};

Template

Struct Signature >>>>>

: MPL :: Vector ## 3 {};

...

Also, ## often leads to an unexpected result - because it will work before actually exhibiting:

#define n 10

#define vec (i) Vector ## i

Vec (n) // vectorn

As a comparison, Boost_pp_cat then delays the "splicing" operation to all of its arguments, and then fully evaluate (expand):

#define n 10

#define vec (i) Boost_pp_cat (Vector, i)

VEC (n) // vector10

Data type (Data Types)

The BPL library also provides a data type - you can look at the type sequence of it and the type sequence of the MPL. The BPL data type is saved is macro-ginseng, not the C type.

sequence

The sequence (short-handed SEQ) is a non-empty macro-specific parameter (which is hosted by small brackets in parentheses). For example, the following is a sequence containing three elements:

#define sequence3 (f (12)) (A 1) (foo)

Here is how to use sequences to generate the process of the is_ISTEGRAL template (BOOST.TYPE Traits):

Template

Struct is_integral : MPL :: false_ {};

// a SEQ of Integral Types with unsigned counterparts

#define boost_tt_basic_INTS (Char) (INT) (om)

// generate a seq containing "sign t" and "unsigned t"

#define boost_tt_int_pair (r, data, t) (Signed T) (Unsigned T)

// a seq of all the integral type

#define boost_tt_ints /

(BOOL) (char) /

BOOST_SEQ_FOR_EACH (boost_tt_int_pair, ~, boost_tt_basic_ints)

// generate an is_integral specialization for Type T

#define boost_tt_is_integral_spec (r, data, t) /

Template <> / struct is_integral : MPL :: true_ {};

Boost_seq_for_each (boost_tt_is_integral_spec, ~, boost_tt_ints)

#undef bagost_tt_is_integral_spec

#undef boost_tt_ints

#undef bagost_tt_int_pair

#undef boost_tt_basic_ints

BOOST_SEQ_FOR_EACH is a Higher-Order macro that is similar to Boost_pp_repeat. It uses its first parameter as "function", in order of the elements in the sequence of its third parameter, perform a secondary call.

The sequence is the most efficient, most flexible, and the most easily used BPL data type - provided that you don't need an empty sequence: an empty sequence does not contain any token, so it cannot be passed as a macro-reliance. Other data types mentioned here can be empty.

The stepping sequence is located in the "/ seq /" subdirectory. The table below has been summarized (where T represents sequence (t1) ... (tk). The intent of S, R, D is similar to what the Z parameters mentioned earlier (I now suggest you ignore it) ).

Preprocessing sequence operation

expression

result

BOOST_PP_SEQ_CAT (T)

T0t1 ... TK

BOOST_PP_SEQ_ELEM (n, t)

TN

BOOST_PP_SEQ_ENUM (T)

T0, T1, ... TK

BOOST_PP_SEQ_FILTER (PRED, DATA, T)

T WITHOUT The Elements That Don't Satisfy PRED

BOOST_PP_SEQ_FIRST_N (N, T)

(T0) (T1) ... (TN-1)

BOOST_PP_SEQ_FOLD_LEFT (OP, X, T)

... OP (S, OP (S, OP (S, X, T0), T1), T2) ...

Boost_pp_seq_fold_right (OP, X, T)

... OP (S, OP (S, OP (S, X, TK), TK-1), TK-2) ...

BOOST_PP_SEQ_FOR_EACH (f, x, t)

F (r, x, t1) f (r, x, t1) ... f (r, x, tk)

BOOST_PP_SEQ_FOR_EACH_I (G, X, T)

g (r, x, 0, t0) g (r, x, 1, t1) ... g (R, X, K, TK)

BOOST_PP_SEQ_FOR_EACH_PRODUCT (H, X, T)

CARTESIAN PRODUCT -

See Online DOCS

BOOST_PP_SEQ_INSERT (T, I, tokens)

(T0) (T1) ... (Ti-1) (Ti) (Ti 1) ... (TK)

BOOST_PP_SEQ_POP_BACK (T)

(T0) (T1) ... (TK-1)

BOOST_PP_SEQ_POP_FRONT (T)

(T1) (T2) ... (TK)

Boost_pp_seq_push_back (t, tokens)

(T0) (T1) ... (TK) (TOKENS)

Boost_pp_seq_push_front (t, tokens)

(TOKENS) (T0) (T1) ... (TK)

BOOST_PP_SEQ_REMOVE (T, I)

(T0) (T1) ... (TI-1) (Ti 1) ... (TK)

BOOST_PP_SEQ_REPLACE (T, I, tokens)

(T0) (T1) ... (Ti-1) (Ti 1) ... (TK)

BOOST_PP_SEQ_REST_N (N, T)

(TN) (TN 1) ... (TK)

BOOST_PP_SEQ_REVERSE (T) (TK) (TK-1) ... (T0)

BOOST_PP_SEQ_HEAD (T)

T0

Boost_pp_seq_tail (t)

(T1) (T2) ... (TK)

BOOST_PP_SEQ_SIZE (T)

K 1

Boost_pp_seq_subseq (t, i, m)

(TI) (Ti 1) ... (Ti M-1)

Boost_pp_seq_to_Array (T)

(k 1, (t0, t1, ... tk))

BOOST_PP_SEQ_TO_TUPLE (T)

(T0, T1, ... TK)

BOOST_PP_SEQ_TRANSFORM (f, x, t)

(f (r, x, t0)) (f (r, x, t1)) ... (f (r, x, tk))

It is worth noting that although the length of the sequence is not the upper limit, the operation like Boost_pp_seq_elem accepts value parameters can only accept the number of 256.

Tuples

Tuple is a very simple data type, BPL provides random access and some other basic operations. The form of tuple is: in parentheses, comma-separated macro-specific lists. For example, the following is a TUPLE containing three elements:

#define tuple3 (f (12), A 1, FOO)

BPL's "/ tuple" subdirectory contains the operation of Tuple, supporting up to 256 elements of Tuple. For example, a TUPLE's nth element can be accessed by boost_pp_tuple_lex, as follows:

// Length Index Tuple

BOOST_PP_TUPLE_ELEM (3, 1, Tuple3) // A 1

Note: We must pass the length of the tuple to BOOST_PP_TUPLE_ELEM as the first parameter. In fact, all TUPLE operations require explicitly pointing out the length of TUPLE. There are another four tuple operations herein not introduced - you can search for more details from the online documentation. However, we noticed that the sequence can be converted to tuple - via boost_pp_seq_to_tuple, and non-empty Tuple can also be converted to a sequence - via boost_pp_tuPle_to_seq.

TUPLE is the most powerful ability: its expression and macro-specific list:

#define first_of_3 (A1, A2, A3) A1

#define second_of_three (A1, A2, A3) A2

#define third_of_three (A1, A2, A3) A3

// Uses tuple as an argument list

# Define SELECT (Selector, Tuple) Selector Tuple

SELECT (Third_of_three, tuple3) // foo

Array

Array is a tuple - different is its first element is the length of Tuple (non-negative).

#define array3 (3, tuple3)

Because Array "carries" with its length information, the use interface of Array in the BPL is much simpler than the manipulation of Tuple:

BOOST_PP_ARRAY_ELEM (1, Array3) // A 1

The "/ Array /" subdirectory in the BPL library contains amenities that operate Array - up to 25 elements of Array. The following table summarizes them (where A is (K, (A0, A1, ..., AK-1)).

Array operation in the pretreatment library

expression

result

Boost_pp_Array_Data (a)

(A0, A1, ... AK-1)

BOOST_PP_ARRAY_ELEM (i, a)

ai

Boost_pp_array_insert (A, I, TOKENS) (K 1, (A0, A1, ... AI-1, Tokens, AI, AI 1, ... AK-1))

Boost_pp_Array_POP_BACK (a)

(K-1, (A0, A1, ... AK-2))

BOOST_PP_ARRAY_POP_FRONT (A)

(K-1, (A1, A2, ... AK-1))

Boost_pp_Array_push_back (a, tokens)

(k 1, (A0, A1, ... AK-1, Tokens))

Boost_pp_array_push_front (a, tokens)

(k 1, (Tokens, A1, A2, ... AK-1))

Boost_pp_Array_remove (a, i)

(k-1, (A0, A1, ... AI-1, AI 1, ... AK-1))

Boost_pp_array_replace (a, i, tokens)

(k, (A0, A1, ... AI-1, Tokens, Ai 1, ... AK-1))

Boost_pp_Array_Reverse (a)

(K, (AK-1, AK-2, ... A1, A0))

Boost_pp_Array_Size (a)

k

List

List is a TUPLE containing two elements. The first element is the first element of the List, and the second element is the List of the remaining elements (note, this is a recursive definition - translator), or boost_pp_nil (if There is no remaining element). List's access methods and shipping chains are very similar. Below is a list containing three elements:

#define List3 (f (12), (foo, boost_pp_nil)))

The "/ list /" subdirectory in the BPL contains a facility that manipulates the List. Since these operations are a subset of sequence operations, we are not unsnect - the documentation of the sequence is not difficult to understand the meaning of the List operation.

Similar to the sequence, the LIST does not have a fixed length limit. However, the sequence is that the List can be empty. Typically, in a pre-processing data structure, you rarely need more than 25 elements, and, List is usually slow, and the readability is poor, so not to use List.

[1] Translation: Here, T1, T2, T3 ... Repeat TX mode.

[2] Translation: , , are the so-called "mechanical taste" code. Template <> struct tiny_size and MPL :: INT_ are the rest of the code, in each of the offset versions, these code will be unnecessary, in most cases, this code is mostly, and may have Quite large quantity.

[3] Translation: Only "Template Program" in front, the macro can also write a "pretreatment element program", the goal is to generate code, the executor is a C / C pre-processor, this is this chapter Point. Correspondingly, there is also "pre-processing meta programming". The original article is "preProcessor metaprogramming", which is not translated into "preprocessor meticulum programming" because the latter may cause misleading - "Preprocessor" will "Mer-Programming"? "Pretreatment Metal Programming", "Pretreatment Element Procedure" mean is very clear - "program" or "programming" compiling the pre-treatment period.

[4] GCC -P option is forbidden to include the source file and line number tag in the pre-processing result output. Please refer to the GCC manual for details.

[5] About the macro show, we have ignored many details, I suggest you look at the 16.3 of the C standard. [6] The preparation instrument of C99 can do, and more. The C Standards Committee tends to accept the preparalleser extension in C99 in the next standard of C .

[7] If the suffix is ​​_1st, _2nd, or _3rd macro, they should also be ignored because they will be removed from the library.

[8] "~" Not completely arbitrary, "@" and "$" this is a good choice, but it is unfortunately they do not belong to the basic character set that must be supported by C . The identifier like "ignored" may be a macro itself, which will be launched, resulting in unexpected results.

[9] Note that "/" and the laundry behind it will be removed by the preparation system, so the result code is actually only one line!

[10] Additional pre-compilers can easily handle the nested repetition of 256 * 256.

[11] Demo: Although Boost_pp_local_iterator () is finally replaced with a file name in the BPL, this file will also accept pre-processed, which is equivalent to a code template (this "template" non-C template in pre-processed The corresponding code will be generated according to the user-defined Boost_PP_LOCAL_MACRO macro. In fact, this model is not different from the front horizontal repetition, but it is only a constant include file, because the preparation instrument contains files faster, this is an "optimization." The following "file iteration" is "optimized" on the format of the generated code.

[12] Demo: This sentence "serious" means :-), original "Alth Separate Instances Appe Occupy the Same Source Lines in the debugger, We do since the experience of step." If Direct translation is difficult Understand, in fact, this sentence means that when tracking the code generated by the "template" code generated by the C template or macro, you can only track the "template" code, and cannot track the code generated by the "template" code. Although they have been generated by the preprocessor, no matter where you are actually tracking which "special" code, you always see the "template" code to correspond to it. "

[13] Annotation: Here is a translation, the original is "The trailing 1 indicates that this is the first nesting level of file iteration - should we need to invoke file iteration again from within tiny_size_spec.hpp, we'd need to use BOOST_FILENAME_2 instead. "If the" ... first Nesting Level of File Iteration "... first Nesting Level of File ity" is translated into the first heavy (layer) "", it is inevitable that "the first time #include this' model" file". However, the author's actual expression is: every "sample" file corresponds to a Level, "sample" file itself can also use other "template" files to be iterated (nest), this time in order to put various "templates" Distinguish, only by adding the _n suffix.

[14] Translation: "Contains the whistle" to prevent head files from being used multiplexed multiplexes to cause repetition definition errors, and their general form is:

#ifndef __included_xxx

#define __included_xxx ... // Codes Go Here

#ENDIF

In this way, once the header file is included in a file, __ include_xxx macro is defined, so if the code is attempted again, the code in the file will no longer be processed. Here Tiny_SIZE_SPEC.HPP does not require "contains the whistle" because it is not used by the user directly, but as a "template" code file is used by the BPL library to generate a copy of the code entity, if used The whistle "will make the iterative subsequent steps are invalid.

[15] Although inheritance from std :: unary_function or std :: binary_function may be necessary for interactions between and some old libraries, this may prevent "empty base optimization" Consider the case where the object of the two derived classes is part of the same object (sub-object). For more information on this, please refer to Chapter 9 of the Section of Structure Selection. Typically, direct typef first_argument_type and second_argument_type and result_type are better options.

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

New Post(0)