Lattice Builder
Software Package for Constructing Rank-1 Lattices
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Groups Pages
Implementation Notes

Function Arguments

In Lattice Builder, there are three different types of declarations of function arguments:

void f(const T& x) // if f only reads x
void f(T& x) // if f writes to x
void f(T x) // if f needs a copy of x

Although the first prototype allows f to make a copy of x internally, it is best to leave it to the compiler to do it. For example, the following code will cause two instantiations of objects of type T:

void f(const T& x) { T y = x; y.modify(); }
...
f(T());

whereas the following allows the compiler to instantiate only one object of type T:

void f(T y) { y.modify(); }
...
f(T());

Sequence

In multiple situations, Lattice Builder needs to access arrays of values that can interchangeably be computed on-the-fly or pre-computed. The concept of sequence, in the sense of ordered list rather than infinite mathematical sequence, provides in Lattice Builder a unified interface for both. The elements of a sequence are accessed via immutable iterators (const_iterator) with the standard syntax as used for the standard C++ containers, as in the following example:

MySeq seq; // must implement begin() and end()
for (MySeq::const_iterator it = seq.begin(); it != seq.end(); ++it)
std::cout << *it << std::endl;

Or, using the new C++11 auto keyword and for syntax:

MySeq seq; // must implement begin() and end()
for (const auto& elem : seq)
std::cout << elem << std::endl;

Concrete examples are given in the tutorial in sections Sequences of Generator Values, Sequences of Lattice Definitions and Sequences of Merit Values .

See Also
LatBuilder::GenSeq LatBuilder::GenSeqSeq LatBuilder::LatSeq LatBuilder::MeritSeq
Remarks
The LatBuilder::TextStream namespace provides streaming operators to iterate over sequences with a more compact syntax:
using TextStream::operator<<;
MySeq seq; // must implement begin() and end()
std::cout << seq << std::endl;
Concrete examples are given in the tutorial in section Text Output .

Bridge Sequence

The values in a sequence often need to be mapped to other values, possibly of a different type. For that purpose, Lattice Builder introduces the concept of a bridge sequence, which contains the mapped elements in the same order as the sequence it is based on.

See Also
LatBuilder::BridgeSeq

Static Polymorphism

Traditional object-oriented programming makes use of dynamic polymorphism, where the exact class of an object is resolved at execution time, dynamically. It follows that polymorphic member function calls cannot be inlined, so it is often not advisable to place such a call in the core of a loop with many iterations, at the risk of cumulating inderections due to both polymorphism and function calls.

Some algorithms implemented by Lattice Builder come in several variants which require either polymorphic function calls inside such loops or a large amount of duplicated code with tiny differences. C++ allows to circumvent that problem through the use of static polymorphism, which enables compile-time resolution of the types together with member function inlining.

In some places, static polymorphism is used regardless that dynamic polymorphism could have been used without any notable performance loss, in order to maintain a consistent design. We prefer making common usage patterns of Lattice Builder's classes easier to recognize, even if that means a little bit more verbose code in places.

The most obvious example is the case of sequence classes. Their member functions begin() and end() return iterators; the type of an iterator is often specific to the type of the sequence it is pointing to.

Another example is the size parameter is represented by a different class depending on whether ordinary or embedded lattices are considered. In both cases, the class implements a numPoints() member function, that returns the number of points in the lattice, and that is accessed frequently in some parts of the code. In a traditional object-oriented approach, the two classes would derive from the same abstract base class, and would have required runtime object-type resolution at the call points of numPoints(). With the static polymorphism approach, the numPoints() function can be efficiently inlined by the compiler, thus avoiding both the overhead due to the function call and to runtime object-type resolution.

Concept

Concepts in C++ can be thought of as the static-polymorphism counterpart of dynamic-polymorphism interfaces. Like interfaces, concepts can specify requirements on the members of a class; unlike interfaces, the data types of the arguments and of the return value of a member function can differ across classes implementing the same concept. Furthermore, a concept can require type definitions (typedefs or nested classes) inside of a class.

Although requirements on the types are not explicited with concepts definitions are absent from Lattice Builder, but the code is built around the idiom of concepts.

By convention in Lattice Builder, classes representing the same concept are regrouped in the same namespace.