C++11, C++14, and C++17 For The Impatient
C++11, C++14, and C++17 For The Impatient
Impatient: Opportunities in
Computational Finance
Daniel J. Duffy
Datasim Finance, e-mail: [email protected]
Avi R. Palley
Quantitative Developer, Baruch/Quantnet C++ TA
Abstract and early adopters in the 1990s were organizations in telecommunications, embed-
This is the first article in a mini series of two articles on applying C++11 to com- ded systems, medical devices, and computer-aided design (CAD), as well as first-
putational finance. Here we focus on the new syntax and features that improve and generation option pricing risk management systems. The rise in popularity contin-
enhance the efficiency, reliability, and usability of C++ as a language for application ued well into the late 1990s as major vendors such as Microsoft, Sun, and IBM began
development. We introduce the C++ building blocks in the form of data types, con- to endorse object-oriented technology and C++. It was also in this period that the
tainers, and polymorphic function types that allow developers to design applications Java programming language appeared, which in time became a competitor to C++.
based on a combination of the object (-oriented), generic, and functional program- C++ remains one of the most important programming languages at the moment
ming styles. In the second article we will introduce a language-independent defined of writing. It is evolving to support new hardware such as multicore processors,
process based on domain architectures (Duffy, 2004) to decompose a software system GPUs (graphics processing units), and heterogeneous computing environments.
into loosely coupled subsystems, each of which has a single responsibility and having It also has a number of mathematical libraries that are useful in computational
well-defined interfaces to and from other subsystems. Having created a design blue- finance.
print (similar to an architectural drawing), we then implement the subsystems using
the multiparadigm programming features in C++. We then design and implement C++ as a multiparadigm programming language
a Monte Carlo option pricing framework in C++11 by mapping a specific domain We give an overview of the programming paradigms that C++ supports. In general, a
architecture to C++11. We also show how the same design blueprint can be imple- programming paradigm is a way to classify programming languages according to the
mented in C#. style of computer programming. Features of various programming languages deter-
mine which programming paradigms they belong to; as a result, some languages
Keywords fall into only one paradigm, while others fall into multiple paradigms. C++ is in this
C++11, C++14, computational finance, functional programming, Monte Carlo, sense a multiparadigm programming language because it supports the following
option pricing styles.
38 WILMOTT magazine
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
TECHNICAL PAPER
• Functional: it treats computation as the evaluation of mathematical functions the second article when we create a framework for option pricing using the Monte
and avoids changing-state and mutable data. It is a declarative programming Carlo method.
paradigm; this means that programming is done with expressions and declara- A more extensive discussion of all these topics is given in Duffy (2017).
tions instead of statements. The output value of a function depends only on its
input arguments.
I Smart memory management and move semantics
The generic programing style is becoming more important and pronounced in
C++, possibly at the expense of the pure object-oriented model which is based on class Using smart pointers in code
hierarchies and subtype (dynamic) polymorphism. In this sense, template code tends to We discuss three pointer classes in C++. In the next three subsections we focus on
perform better at run-time while many errors are caught at compile-time in contrast to the syntax of each one and give some simple examples to show what they do.
object-oriented code, where the errors tend to be caught by the linker or even at run-time.
The most recent style that C++ has (limited) support for is functional program- Class std::shared_ptr
ming. This style predates both structured and object-oriented programming. Functional This smart pointer class implements the concept of shared ownership. A resource or
programming has its origins in lambda calculus, a formal system developed in the 1930s object (a piece of memory on the heap) is shared among a number of shared point-
to investigate computability, function definition, function application, and recursion. ers. Only when the resource is no longer needed is it deleted. This is when the related
Many functional programming languages can be viewed as elaborations on the lambda reference count becomes zero.
calculus. C++ supports the notion of lambda functions. A lambda function in C++ is We discuss shared pointers. First, we show how to create empty shared pointers
an unnamed function but it has all the characteristics of a normal function. Here is an and shared pointers that are coupled to heap resources. We also show how a shared
example of defining a stored lambda function and then calling it as a normal function: pointer gives up ownership of one resource and how it becomes owner of another
resource. We can always see how many shared pointers own a resource by using the
member function use_count():
In the above cases the last owner of the resource is responsible for destroying the
In this case we see that the lambda function has a formal input string argument and it resource and by default this is achieved by a call of the operator delete.
uses a so-called captured variable cVar. Lambda functions are simple but powerful
and we shall show how they can be used in computational finance. Class std::unique_ptr
C++11 is a major improvement on C++03 and it has a number of features that Whereas std::shared_ptr allows a resource to be shared among several shared
facilitate the design of software systems based on a combination of structured analy- pointers in the case of std::unique_ptr, there is only one transferable owner of
sis and object-oriented technology. In general, we have a defined process to decom- a resource. In this case we speak of exclusive or strict ownership. Its main added value
pose a system into loosely coupled subsystems (Duffy, 2004). We then implement is in avoiding resource leaks (for example, missing calls to delete when using raw
each subsystem in C++11, as we shall see in later articles. pointers) and for this reason is can be called an exception-safe pointer. Its main mem-
This article is organized as follows: in Section I we introduce smart memory ber functions are as follows.
management in C++11, which resolves many of the problems that developers expe-
rienced with native pointers in previous versions of the language. Their application • Constructors (similar to those in std::shared_ptr).
• Assign a unique pointer.
in code promotes robustness and reliability. In Section II we discuss a number of
• Release; return a pointer to the resource and release ownership.
useful low-level features that avoid some shortcomings in C++03. Section III is
• Reset; replace the resource.
devoted to a bird’s-eye view of functional programming and how C++ supports
• Operator overloading (==, !=, <, and so on).
it using lambda functions and universal function wrappers. Tuples (generaliza-
tions of std::pair ) and variant data types are discussed in Section IV. Finally, The interface is similar to that of std::shared_ptr, which means that code
in Sections V and VI we discuss how to model stochastic differential equations will be easy to understand. Finally, std::unique_ptr succeeds auto_ptr, the
^
(SDEs) using the multiparadigm features in C+11. We shall continue in this vein in latter being considered deprecated.
WILMOTT magazine 39
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
Our first example entails creating a unique pointer in a scope. Under normal Type alias and alias templates
circumstances, when the pointer goes out of scope, the corresponding resource is Developers are already familiar with the typedef specifier, which allows aliases to
cleaned up but in this case we (artificially) throw an exception before the end of the be defined for existing types. The specifier cannot be used to change the meaning of
scope is reached. What happens? When we run the code we see that the resulting an existing type name and hence it does not represent the declaration of a new type.
exception is caught and the resource is automatically destroyed: A typedef has effect in the scope in which it is visible. An example is
This code also works when we use shared pointers instead of unique pointers. In But there is hope! C++11 offers the possibility to define an alias for class tem-
C++14 we can create unique pointers using std::make_unique() for non-array plates and non-template classes alike. The term is sometimes called alias tem-
types: plate or template typedef. The above example can now be formulated as
follows:
We can also use this technique in combination with user-defined types with long
std::weak_ptr names and with types having many template parameters in order to make it easier to
The third smart pointer class holds a non-owning (weak) reference to an object work with them. Let us take the simple example
(resource) that is managed by a shared pointer. It must be converted to a shared
pointer in order to access the resource. It is a helper class to std::shared_ptr
and it is needed when the latter’s behavior does not work as intended, namely:
40 WILMOTT magazine
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
TECHNICAL PAPER
• Default constructor.
• Copy constructor, which corresponds to member-wise construction of
non-static data members. It is generated only if the class does not have a user-
defined copy constructor.
• Copy assignment operator. Generation of this function in a class with a user-
defined copy constructor is deprecated.
These functions are generated only if they are needed. For example, a default
constructor is generated only if a class declares no constructors whatsoever.
Generated special member functions are implicitly public and inline. In C++11 the
club of special member functions has been extended to include the move constructor
and the move assignment operator.
We use the keyword default to let the compiler generate the body of a con-
structor. At the other extreme, we may wish to suppress the use of special member
functions. The standard approach in C++03 is to declare them as private and
to not define them. C++11 uses the specifier delete to mark these functions as
so-called deleted functions, which is an improvement on C++03 for a number of
reasons:
We could have used explicit return types, but the compiler is clever and it knows
what the correct type should be, as the following checks show based on the C++ type
traits library:
Some test code shows what does and does not compile:
We can also use the auto specifier to make life easier when working with user-
defined types:
WILMOTT magazine 41
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
Finally, we give an example of a deleted non-member function. In this case we We use uniform initialization as follows:
define some overloaded print functions that are deleted when the input argument is a
double or a bool:
We see from class Person that we can initialize a class’s member data using
braced initialization, even though the class does not have the appropriate constructors.
We can use this technique to initialize the members of an aggregate object. where record is a data collection and func is a function that operates on each
Consider the classes element of that collection. Higher-order functions are useful in code refactoring
projects because their use reduces the amount of code repetition. We shall see how to
simulate higher-order functions in C++ by the use of function objects (functors) and
lambda functions.
42 WILMOTT magazine
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
TECHNICAL PAPER
arguments). It is then not possible to call the function in the traditional sense
but what we get is a function with a given arity, which can later be called by
instantiating all the remaining delayed arguments. This is a useful feature
because we can create new functions from existing ones by binding one or
more arguments to specific values.
First, we see that the presence of the brackets [] announces a new lambda func-
tion having a given return type. Second, a lambda function has input arguments
as well as so-called captured variables that belong to the lambda function’s closure.
These variables correspond to the lambda function’s state. It is possible to capture
variables by value or by reference, depending on whether we wish to copy by value or
by reference into the body of the lambda function. We can specify the choice by using
the following list as exemplar:
We see that these functions accept a double as input argument and they return
a double. In order to subsume these functions in a polymorphic function wrapper
as it were, we define
WILMOTT magazine 43
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
Lambda functions and classes: Capturing member data (like strike, expiration) pertaining to the first and second assets, respectively,
while the third element contains the value of the correlation between these
It is possible to define lambda functions in a class and it is also possible to capture its
assets.
member data. The rules are
• Mathematical properties of differential equations encapsulated in reusable tuples.
[this] captures the this pointer only • Essential defining parameters pertaining to numerical processes, for example
[=] captures the this pointer and local variables by value step size, tolerances, and maximum number of iterations.
[&] captures the this pointer and local variables by reference. • Any non-trivial application that needs to be configured from various sources.
To this end, we use tuples that client code reads without having to know where
Inside the body of the lambda function we access the members in the usual way. the data comes from.
We take an example of a class to calculate the trigonometric sine of a vector with
input values. To this end, we transform the input vector using a lambda function. The We give a simple example of how to create nested tuples to model people and
objective is to use a lambda function to effect the transformation: their whereabouts (it can be seen as a simple database):
Variadic tuples
In general, a variadic template is one that takes a variable (arbitrary) number of input
arguments. In particular, tuples can be defined as having a variable number of elements:
This declaration states that this class can take any number of typenames as its
Extending the code to tuples with more than two elements is straightforward. template parameters. Incidentally, the number of arguments can be zero. If we wish
to forbid the definition of a template with zero arguments, then we can modify the
Tuple nesting definition as follows (in which case the tuple must have at least one element):
A useful feature in C++ is that we can define fixed-size recursive and composite
structures. In other words, we can define tuples that contain other tuples as well as
other types as elements. This feature can be applied to creating configuration data in
an application. This data can be created by dedicated factory objects and the data is Variadic templates also apply to functions, for example free functions:
then processed in an application at a suitable entry point in the code.
Some typical applications spring to mind:
• Modeling data for rainbow and multi-asset options by a tuple with three ele-
ments. The first and second elements are themselves tuples containing the data
44 WILMOTT magazine
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
TECHNICAL PAPER
We can now get the current value in a variant by calling the get() function:
Since the function is recursive, we know that we need to define its very last action
as it were (this is the equivalent of tail recursion). To this end, we employ template
specialization:
WILMOTT magazine 45
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
This approach reduces code bloat, especially at the server side, because only a single 1. Create multiple instances of SDE<>; create a specific instance of SDE<>
class is needed. We first describe some supporting syntax and shorthand notation to based on some user choice.
make the resulting code more readable: 2. Create the functions that are the components of the SDE<> instances in step 1.
3. Create the data that the functions in step 2 need.
Each of these steps can be executed by dedicated factory objects that are in fact
instances of creational design patterns as described by Gamma et al. (1995). We give
an initial example of code in which steps 1, 2, and 3 are amalgamated into one code
block. We can refine this process even more when we use the Builder design pattern
(Gamma et al., 1995) to configure a complete application. We take an example of a
function that creates an arithmetic SDE (notice that the data is hard-coded but the
We are now in a position to post the class that models the SDE; we note that it code can be generalized). We use an alias for the factory class:
makes use of functions and tuples:
Some examples of how to interact with this class are when creating SDEs for geo-
metric Brownian motion (GBM) and constant elasticity of variance (CEV) models:
46 WILMOTT magazine
15418286, 2017, 90, Downloaded from https://onlinelibrary.wiley.com/doi/10.1002/wilm.10606 by Cochrane Netherlands, Wiley Online Library on [11/05/2023]. See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online Library for rules of use; OA articles are governed by the applicable Creative Commons License
TECHNICAL PAPER
Avi R. Palley works as a quantitative developer and is TA for the highly successful
C++ Programming for Financial Engineering and the Advanced C++11/C++14 and
Multidisciplinary Applications online certification courses (offered by Quantnet.com
and originated by Daniel J. Duffy). He has a Masters in Financial Engineering from Baruch
VI Emerging multiparadigm design patterns: College, New York.
Summary
In the previous section we applied modern C++ syntax to design a small framework
to model SDEs as a typical example that we can generalize. We have avoided subtype References
polymorphism and instead took an approach based on creating a single class com- Demming, R. and Duffy, D. 2010. Introduction to the Boost C++ Libraries, Volume I.
posed of universal function wrappers. This is similar to interface programming in lan- Amsterdam: Datasim Press.
guages that support interfaces (such as C# and Java, for example). In general, an inter- Demming, R. and Duffy, D. 2012. Introduction to the Boost C++ Libraries, Volume II.
face is a pure specification or protocol and it describes a collection of abstract methods. Amsterdam: Datasim Press.
An interface may not contain non-abstract methods nor may it contain member Duffy, D. J. 2004. Domain Architectures. Chichester, UK: Wiley.
data. In this sense it is very different from an abstract class in C++ (even one without Duffy, D. 2017. Financial Instrument Pricing Using C++, 2nd edn. Chichester, UK: Wiley.
Duffy, D. and Kienitz, J. 2009. Monte Carlo Frameworks. Chichester, UK: Wiley.
non-abstract member functions and member data). C++ does not support interfaces;
Gamma, E., Helm, R., Johnson, R., and Vlissides, J. 1995. Design Patterns. Boston, MA:
however, we can emulate them by creating (variadic) tuples whose elements are uni-
Addison-Wesley.
versal function wrappers. In this case we define our interface emulator as
^
W
WILMOTT magazine 47