Appendix B

Programming Language

Sean Parent and Bjarne Stroustrup

This appendix defines the subset of C++ used in the book. To simplify the syntax, we use a few library facilities as intrinsics. These intrinsics are not written in this subset but take advantage of other C++ features. Section B.1 defines this subset; Section B.2 specifies the implementation of the intrinsics.

B.1 Language Definition

Syntax Notation

An Extended Backus-Naur Form designed by Niklaus Wirth is used. Wirth [1977, pages 822–823] describes it as follows:

The word identifier is used to denote nonterminal symbol, and literal stands for terminal symbol. For brevity, identifier and character are not defined in further detail.

Image

Repetition is denoted by curly brackets, i.e., {a} stands for Image |a|aa|aaa |.... Optionality is expressed by square brackets, i.e., [a] stands for a | Image . Parentheses merely serve for grouping, e.g., (a|b)c stands for ac|bc. Terminal symbols, i.e., literals, are enclosed in quote marks (and, if a quote mark appears as a literal itself, it is written twice).

Lexical Conventions

The following productions give the syntax for identifiers and literals:

Image

Comments extend from two slashes to the end of the line:

comment    = "//" {character} eol.

Basic Types

Three C++ types are used: bool has values false and true, int has signed integer values, and double has IEEE 64-bit floating-point values:

basic_type = "bool" | "int" | "double".

Expressions

Expressions may be either runtime or compile time. Compile-time expressions may evaluate to either a value or a type.

Expressions are defined by the following grammar. Operators in inner productions—those appearing lower in the grammar—have a higher order of precedence than those in outer productions:

Image

The || and && operators designate Image (disjunction) and Image (conjunction), respectively. The operands must be Boolean values. The first operand is evaluated prior to the second operand. If the first operand determines the outcome of the expression (true for ||, or false for &&), the second operand is not evaluated, and the result is the value of the first operand. Prefix ! is ¬ (negation) and must be applied to a Boolean value.

== and != are, respectively, equality and inequality operators and return a Boolean value.

<, >, <=, and >= are, respectively, less than, greater than, less or equal, and greater or equal, also returning a Boolean value.

+ and - are, respectively, addition and subtraction; prefix - is additive inverse.

*, /, and % are, respectively, multiplication, division, and remainder.

Postfix . (dot) takes an object of structure type and returns the member corresponding to the identifier following the dot. Postfix () takes a procedure or object on which the apply operator is defined and returns the result of invoking the procedure or function object with the given arguments. When applied to a type, () performs a construction using the given arguments; when applied to a type function, it returns another type. Postfix [] takes an object on which the index operator is defined and returns the element whose position is determined by the value of the expression within the brackets.

Prefix const is a type operator returning a type that is a constant version of its operand. When applied to a reference type, the resulting type is a reference to a constant version of the reference base type.

Postfix & is a type operator returning a reference type of its operand.

Enumerations

An enumeration generates a type with a unique value corresponding to each identifier in the list. The only operations defined on enumerations are those of regular types: equality, relational operations, inequality, construction, destruction, and assignment:

enumeration    = "enum" identifier "{" identifer_list "}" ";".
identifer_list = identifier {"," identifier}.

Structures

A structure is a type consisting of a heterogeneous tuple of named, typed objects called data members. Each data member is either an individual object or an array of constant size. In addition, the structure may include definitions of constructors, a destructor, member operators (assignment, application, and indexing), and local typedefs. A structure with an apply operator member is known as a function object. Omitting the structure body allows a forward declaration.

Image

A constructor taking a constant reference to the type of the structure is a copy constructor. If a copy constructor is not defined, a member-by-member copy constructor is generated. A constructor with no arguments is a default constructor. A member-by-member default constructor is generated only if no other constructors are defined. If an assignment operator is not defined, a member-by-member assignment operator is generated. If no destructor is supplied, a member-by-member destructor is generated. Each identifier in an initializer list is the identifier of a data member of the structure. If a constructor contains an initializer list, every data member of the structure is constructed with a constructor matching[1] the expression list of the initializer; all these constructions occur before the body of the constructor is executed.

Procedures

A procedure consists of its return type or, when no value is returned, void, followed by its name and parameter list. The name may be an identifier or an operator. A parameter expression must yield a type. A procedure signature without a body allows a forward declaration.

Image

Only the listed operators can be defined. A definition for the operator != is generated in terms of ==; definitions for the operators >, <=, and >= are generated in terms of <. When a procedure is called, the value of each argument expression is bound to the corresponding parameter, and the body of the procedure is executed.

Statements

Statements make up the body of procedures, constructors, destructors, and member operators:

Image

A simple statement, which is often a procedure call, is evaluated for its side effects. An assignment applies the assignment operator for the type of the object on the left-hand side. The first expression for a construction is a type expression giving the type to be constructed. A construction without an initialization applies the default constructor. A construction with a parenthesized expression list applies the matching constructor. A construction with an equal sign followed by an expression applies the copy constructor; the expression must have the same type as the object being constructed.

The return statement returns control to the caller of the current function with the value of the expression as the function result. The expression must evaluate to a value of the return type of the function.

The conditional statement executes the first statement if the value of the expression is true; if the expression is false and there is an else clause, the second statement is executed. The expression must evaluate to a Boolean.

The switch statement evaluates the expression and then executes the first statement following a case label with matching value; subsequent statements are executed to the end of the switch statement or until a break statement is executed. The expression in a switch statement must evaluate to an integer or enumeration.

The while statement repeatedly evaluates the expression and executes the statement as long as the expression is true. The do statement repeatedly executes the statement and evaluates the expression until the expression is false. In either case, the expression must evaluate to a Boolean.

The compound statement executes the sequence of statements in order.

The goto statement transfers execution to the statement following the corresponding label in the current function.

The break statement terminates the execution of the smallest enclosing switch, while, or do statement; execution continues with the statement following the terminated statement.

The typedef statement defines an alias for a type.

Templates

A template allows a structure or procedure to be parameterized by one or more types or constants. Template definitions and template names use < and > as delimiters.[2]

Image

When a template_name is used as a primary, the template definition is used to generate a structure or procedure with template parameters replaced by corresponding template arguments. These template arguments are either given explicitly as the delimited expression list in the template_name or, for procedures, may be deduced from the procedure argument types.

A template structure can be specialized, providing an alternative definition for the template that is considered when the arguments match before the unspecialized version of the template structure.

When the template definition includes a constraint, the template argument types and values must satisfy the Boolean expression following requires.

Intrinsics

pointer(T) is a type constructor that returns the type pointer to T. If x is an object of type T, addressof(x) returns a value of type pointer(T) referring to x. source, sink, and deref are unary functions defined on pointer types. source is defined for all pointer types and returns a corresponding constant reference; see Section 6.1. sink and deref are defined for pointer types to nonconstant objects and return corresponding nonconstant references; see Section 9.1. reinterpret_cast is a function template that takes a reference type and an object (passed by reference) and returns a reference of the reference type to the same object. The object must also have a valid interpretation with the reference type.

B.2 Macros and Trait Structures

To allow the language defined in Section B.1 to compile as a valid C++ program, a few macros and structure definitions are necessary.

Template Constraints

The requires clause is implemented with this macro:[3]

#define requires(...)

Intrinsics

pointer(T) and addressof(x) are introduced to give us a simple linear notation and allow simple top-down parsing. They are implemented as

#define pointer(T) T*

template<typename T>
pointer(T) addressof(T& x)
{
    return &x;
}

Type Functions

Type functions are implemented by using a C++ technique called a trait class. For each type function—say, ValueType—we define a corresponding structure template: say, value_type<T>. The structure template contains one typedef, named type by convention; if appropriate, a default can be provided in the base structure template:

template<typename T>
struct value type
{
    typedef T type;
};

To provide a convenient notation, we define a macro[4] that extracts the typedef as the result of the type function:

#define ValueType(T) typename value_type< T >::type

We refine the global definition for a particular type by specializing:

template<typename T>
struct value_type<pointer(T)>
{
    typedef T type;
};

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.122.68