CHAPTER 15

image

Extending Financial Libraries

C++ is an expressive language that can be used to develop some of the most sophisticated software, including the high-performance applications that are routinely used in banks and other financial institutions. However, it is sometimes beneficial to combine and extend C++ libraries using interpreted languages that can simplify the creation of prototypes and other noncritical applications. A number of such interpreted languages are used for the purpose of connecting pre-compiled libraries. Among them, Python and Lua are among the most common interpreted languages employed in the financial industry.

In this chapter, I show how to use popular scripting and extension languages such as Lua and Python to interact with C++ libraries. The solutions and algorithms discussed in the next few sections allow you to reuse many of the same C++ components presented in previous chapters as part of applications developed in other languages. In some cases, you will also be able to use code that has been created in external languages in your own C++ applications.

Following are some of the topics discussed in this chapter:

  • Extending C++ with Python: the Python language offers great features for the development of server-side applications. If you want to use C++ libraries as part of other services, Python might be the best way to integrate different libraries.
  • Extending C++ with Lua: Lua is a relatively new language that is outstanding in its simple implementation of dynamic features. It is also used as an extension language that can be embedded into your own larger C++ applications.

Exporting C++ Stock Handling Code to Python

Generate code that provides the ability to export to Python a C++ stock handing class.

Solution

Python is a popular language that has been used in several domains, including web applications, scientific data exploration, and finance. One of the greatest strengths of Python is its ability to cleanly bring together large collections of programming libraries. A very important reason for that interoperability is the simple mechanism used by Python to interface with different languages, especially C and C++.

In this section you will learn about the extension mechanism of Python, and how it can be accessed from your C++ code. I have provided a financial example for this process, where you will give access to a Stock class originally designed and implemented in C++.

Python is available as open source on most operating systems. The main web site where you can find the source code and binaries for most operating systems is http://python.org. You will also find Python pre-installed on many UNIX-like operating systems, such as Linux and Mac OS X. The main mechanism for library extension in Python is the module. A module is used to export data and code to Python source files or other modules. The keyword import can be used to load an existing module into memory. For example,

import sys

This is a command used to load the sys module, which gives access to system-dependent functions. While modules can be created in Python itself, as C++ programmers our main interest is in creating modules using C++. This is possible with the Python module creating API (application programming interface), which is available for C and C++, and included with most Python installations.

Since the extension mechanism is written in C (for compatibility with existing C libraries for Python), it is necessary to create a number of C functions that encapsulate your original C++ classes in order to achieve interoperability with the Python environment.

As an example of how this process works, consider the Stock class, which can be used to model a single stock. Listing 15-1 shows the public interface for this class.

Listing 15-1. Interface for Stock Class

class Stock {
public:
    Stock(const std::string &ticker, double price, double div);
    Stock(const Stock &p);
    ~Stock();
    Stock &operator=(const Stock &p);

    std::string ticker();
    double price();
    void setPrice(double price);
    double dividend();
    void setDividend(double div);
    double dividendYield();
}

The extension API for Python is a set of header files and libraries that expose the Python environment to other applications written in C or C++. To export a class such as Stock to Python using the extension API, we need to create a few functions that will receive and return the requests sent by the Python interpreter. This is done in the Stock_Py.cpp file, which contains a list of functions that deal with each member of the Stock class.The first function of interest is the stock_create function, which is the defined in the following way:

PyObject *stock_create(PyObject *self, PyObject *args)

It is common for functions called directly from Python to have a signature where two Python objects are received and a Python object is returned. The first argument to such a function is the Python object that is the target of the call (similar to the this pointer in C++) whenever the call is made using the syntax object.function(). The second parameter is a Python list that stores all the arguments passed to the function.

The first thing this function does is to retrieve the parameters passed as arguments. This can done in the Python API by calling the function PyArg_ParseTuple, which is responsible for checking the arguments and copying their values into data objects. The first parameter of this function is the object representing the list of arguments. The second parameter is a string that defines the types of each data element in the argument list. Finally, the remaining arguments are pointers to the locations where the data should be stored.

if (!PyArg_ParseTuple(args, "sdd", &ticker, &price, &dividend))
        return NULL;

If this function is not successful it will return false, which causes the stock_create to return NULL. Returning NULL indicates to the interpreter that the called function failed for some reason.

Once the input data has been validated, the next step is to create an object of class Stock and initialize it properly. The result is stored in a Python capsule object that is created with the function PyCapsule_New. This function call takes as the last parameter the name of a destructor function, which in this case is just stock_destructor, a function that calls the destructor for the Stock object. After the Python capsule has been created, the new Python object is then returned as the result of the function.

For an example of a function that just returns a single data object, consider stock_ticker, the function that gives access to the Stock::ticker() member function:

PyObject *stock_ticker(PyObject *self, PyObject *args)
{
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!", &PyCapsule_Type, &obj))
        return NULL;

    Stock *stock = getStock(obj);
    return Py_BuildValue("s", stock->ticker().c_str());
}

In this function, the first step is to validate the input arguments, which uses the PyArg_ParseTuple function. The PyArg_ParseTuple function receives as arguments the container of the data and a string that determines the type of each argument, followed by pointers to variables where the data will be stored. The object to be retrieved in this case is of type PyCapsule, as defined in the remaining arguments (which give the object type and a pointer to an object variable). Once the argument is retrieved, you can use the getStock function to fetch the Stock pointer. Finally, the ticker() member function is called. To return the data to the Python interpreter, you need to convert the result into a Python object. This is done with the Py_BuildValue function, which uses a format string to determine the type of its remaining argument. Other functions are similar, and their main work is to retrieve data from the argument list and to convert the results into Python objects.

Image Note  Even if a function that is exposed to Python returns no result, you’re still required to return a valid Python object. In that case, you can use Py_None, which represents a standard Python object that means “no data.”

Finally, we have the initstock function. It calls the Py_InitModule function from the Python API to determine the functions exposed in this module. The Py_InitModule function receives as parameters the name of the module and an array that contains a list of all functions, their names, and descriptions. When this information is passed to the Python interpreter, it becomes available to Python developers whenever the stock module is imported.

Complete Code

Listing 15-2 shows the complete code for the Stock class and its associated Python glue code.

Listing 15-2. Class Stock Interface and Implementation

//
//  Stock.h

#ifndef __FinancialSamples__Stock__
#define __FinancialSamples__Stock__

#include <string>

class Stock {
public:
    Stock(const std::string &ticker, double price, double div);
    Stock(const Stock &p);
    ~Stock();
    Stock &operator=(const Stock &p);

    std::string ticker();
    double price();
    void setPrice(double price);
    double dividend();
    void setDividend(double div);
    double dividendYield();

private:
    std::string m_ticker;
    double m_currentPrice;
    double m_dividend;
};

#endif /* defined(__FinancialSamples__Stock__) */

//
//  Stock.cpp

#include "Stock.h"

Stock::Stock(const std::string &ticker, double price, double div)
: m_ticker(ticker),
m_currentPrice(price),
m_dividend(div)
{
}

Stock::Stock(const Stock &p)
: m_ticker(p.m_ticker),
m_currentPrice(p.m_currentPrice),
m_dividend(p.m_dividend)
{
}

Stock::~Stock()
{
}

Stock &Stock::operator=(const Stock &p)
{
    if (this != &p)
    {
        m_ticker = p.m_ticker;
        m_currentPrice = p.m_currentPrice;
        m_dividend = p.m_dividend;
    }
    return *this;
}

double Stock::price()
{
    return m_currentPrice;
}

void Stock::setPrice(double price)
{
    m_currentPrice = price;
}

double Stock::dividend()
{
    return m_dividend;
}

void Stock::setDividend(double div)
{
    m_dividend = div;
}

double Stock::dividendYield()
{
    return  m_dividend / m_currentPrice;
}

std::string Stock::ticker()
{
    return m_ticker;
}

//
//  Stock_Py.cpp

#include "Stock_Py.h"
#include "Stock.h"

#include <Python.h>
#include <pycapsule.h>

#include <stdio.h>

namespace {

void stock_destructor(PyObject *capsule)
{
    printf("calling destructor ");
    Stock *stock = reinterpret_cast<Stock*>(PyCapsule_GetPointer(capsule, NULL));
    delete stock;
}

PyObject *stock_create(PyObject *self, PyObject *args)
{
    char *ticker;
    double price;
    double dividend;
    if (!PyArg_ParseTuple(args, "sdd", &ticker, &price, &dividend))
        return NULL;

    printf("ticker: %s, price: %lf, dividend: %lf ", ticker, price, dividend);

    Stock *stock = new Stock(ticker, price, dividend);

    PyObject* stockObj = PyCapsule_New(stock, NULL,  stock_destructor);
    return stockObj;
}

Stock *getStock(PyObject *obj)
{
    if (!PyCapsule_CheckExact(obj))
        printf("error: not a stock object ");
    fflush(stdout);
    return reinterpret_cast<Stock*>(PyCapsule_GetPointer(obj, NULL));
}

PyObject *returnNone()
{
    Py_INCREF(Py_None);
    return Py_None;
}

PyObject *stock_ticker(PyObject *self, PyObject *args)
{
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!", &PyCapsule_Type, &obj))
        return NULL;

    Stock *stock = getStock(obj);
    return Py_BuildValue("s", stock->ticker().c_str());
}

PyObject *stock_price(PyObject *self, PyObject *args)
{
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!", &PyCapsule_Type, &obj))
        return NULL;

    Stock *stock = getStock(obj);
    return Py_BuildValue("d", stock->price());
}

PyObject *stock_setPrice(PyObject *self, PyObject *args)
{
    double price;
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!d", &PyCapsule_Type, &obj, &price))
        return NULL;

    Stock *stock = getStock(obj);
    if (!stock)
        return NULL;
    stock->setPrice(price);
    return returnNone();
}

PyObject *stock_dividend(PyObject *self, PyObject *args)
{
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!", &PyCapsule_Type, &obj))
        return NULL;

    Stock *stock = getStock(obj);
    if (!stock)
        return NULL;
    return Py_BuildValue("d", stock->dividend());
}

PyObject *stock_setDividend(PyObject *self, PyObject *args)
{
    double dividend;
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!d", &PyCapsule_Type, &obj, &dividend))
        return NULL;

    Stock *stock = getStock(obj);

    stock->setDividend(dividend);
    return returnNone();
}

PyObject *stock_dividendYield(PyObject *self, PyObject *args)
{
    PyObject *obj;
    if (!PyArg_ParseTuple(args, "O!", &PyCapsule_Type, &obj))
        return NULL;
    Stock *stock = getStock(obj);
    return Py_BuildValue("d", stock->dividendYield());
}

PyMethodDef stockMethods[] = {
    {"new",  stock_create, METH_VARARGS, "Create a new stock object."},
    {"ticker",  stock_ticker, METH_VARARGS, "get ticker for a stock object."},
    {"price",  stock_price, METH_VARARGS, "get price for stock."},
    {"setPrice",  stock_setPrice, METH_VARARGS, "set price for a stock object."},
    {"dividend",  stock_dividend, METH_VARARGS, "get dividend for stock."},
    {"setDividend",  stock_setDividend, METH_VARARGS, "set dividend for a stock object."},
    {"dividendYield",  stock_dividendYield, METH_VARARGS, "get dividend yield for stock."},
    {NULL, NULL, 0, NULL}
};

}

PyMODINIT_FUNC initstock()
{
    Py_InitModule("stock", stockMethods);
}

#
# stock-setup.py

from distutils.core import setup, Extension

setup(name="stock", version="1.0",
      ext_modules=[Extension("stock", ["Stock.cpp", "Stock_Py.cpp"])])

Running the Code

The process of building a Python extension module is a little different from what you did for other C++ applications described in this book. The process requires the creation of a loadable module, which has a different form in each platform, such as a dll (on Windows) or shared object file on many UNIX systems. To simplify this process, the Python developers created a tool that uses a setup.py file to perform the build automatically. Using setup.py, you don’t need to figure out particular build information such as including directories and linking libraries. As you can see in Listing 15-1, the file stock-setup.py describes the extension, with the source files necessary to build the module. The building process (executed on a Mac OS X system) is shown as follows:

$ python stock-setup.py build_ext -i
running build_ext
building 'stock' extension
cc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -arch i386 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch x86_64 -arch i386 -pipe -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c Stock.cpp -o build/temp.macosx-10.9-intel-2.7/Stock.o
cc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -arch i386 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch x86_64 -arch i386 -pipe -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c Stock_Py.cpp -o build/temp.macosx-10.9-intel-2.7/Stock_Py.o
c++ -bundle -undefined dynamic_lookup -arch x86_64 -arch i386 -Wl,-F. build/temp.macosx-10.9-intel-2.7/Stock.o build/temp.macosx-10.9-intel-2.7/Stock_Py.o -o /Users/carlosoliveira/code/FinancialSamples/FinancialSamples/stock.so

Once the module is compiled, it can be easily loaded into a Python script or iterative session. Following is a transcript of a sample use of the stock module:

$ python
Python 2.7.5 (default, Mar 9 2014, 22:15:05)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import stock
>>> a = stock.new('IBM',1,1)
ticker: IBM, price: 1.000000, dividend: 1.000000
>>> stock.setPrice(a, 105)
>>> stock.price(a)
105.0
>>> stock.setDividend(a, 2.2)
>>> stock.dividend(a)
2.2
>>> stock.dividendYield(a)
0.020952380952380955

Exporting C++ Classes Directly to Python

Write C++ code to export existing classes into Python applications.

Solution

In the section “Exporting C++Stock Handling Code to Python,” you saw how C++ code can be exported to Python using the module mechanism. Through the external Python API, it is possible to expose functions and classes that were previously created using C++. However, the external API also imposes the creation of glue code that is not only a boring task but also an error-prone job, which could be better done by a computer.

To simplify some of the issues raised by the external Python API, a new boost library was developed. The boost::python library uses a template-based mechanism to automatically create the integration code required by Python. In this way, developers can more easily expose classes, variables, and function using a set of C++ templates, while avoiding repetitive tasks such as converting data from and to Python objects. Following are a few advantages of using boost to export C++ code to Python

  • Avoid boilerplate: a lot of the code necessary to export C++ classes into Python modules is simple and repetitive. Using a template solution makes it easy to reduce or totally remove much of the boilerplate code needed by the Python external API.
  • Provide type safety: in Python, objects have all the same type PyObject. While there is runtime checking for the correct type in Python, you should be able to use C++ compile-time checking whenever possible. With boost::python, C++ types are used, and conversion into Python is done automatically and only when needed.
  • Reduce programming effort: using the boost library, you can leverage a lot of code that has been developed to solve the specific problem of exporting C++ classes to Python. By using the Python API directly, you may encounter problems that have already been solved in boost::python. As in other areas of C++ programming, the ideal is to reuse good libraries and designs instead of reinventing existing solutions.

Despite the advantages of boost::python, there are also some reasons why you may want to avoid it and use the Python API directly.

  • Size of the project: sometimes you need it to export only a single class or function to Python for a special use. In that case, it may be just as easy to stick to the Python API and skip boost::python.
  • Boost integration: if your project doesn’t use boost, or if you’re not allowed to incorporate other boost libraries into your code, then it would be difficult to use this solution for Python integration.
  • Special needs of the project: while the templates in boost::python are very flexible, you may have some additional requirements for the types you’re exporting. In that case, only the underlying Python extension API may provide the flexibility needed by your project.

Using the boost::python library is straightforward, and you just need to look at some examples and its reference to understand how to quickly export classes. In the code presented in Listing 15-3, I will show how to do this for a single example, the Matrix class from Chapter 5.

The boost::python library is installed along with other boost libraries, so if boost is already installed in your system you should be ready to use it. The main header file for the library is <boost/python.hpp>, which gives access to all macros and templates necessary to export C++ classes.

The main macro in the library is BOOST_PYTHON_MODULE. This macro is needed to generate the boilerplate code that the Python runtime expects. In the scope that follows the macro, you can declare the classes, functions, and other types that will be visible from Python.

To export C++ classes, the main facility provided is the class_ template. As you would expect, this template is used to perform the hard work of defining types and their properties using the underlying Python API. The template parameter for class_ is the class name. The constructor requires a name to be used by Python, and a default constructor. Constructors are defined using the init template, with the parameters added as template parameters.

Attached to the main class_ template, you will find calls to the def member function, which is used to define new members to the class. Every time you call def, the class_ template generates additional code to handle calls from Python code into a given member function. So, for example, you have the following definition:

def("subtract", &MatrixP::subtract)

Here, the subtract member function is defined with the name listed as the first argument, and the destination of the call listed as the second argument.

Complete Code

You can see the complete code for the Matrix module in Listing 15-3. The setup.py file at the end of the listing can be used to build the module.

Listing 15-3. Matrix Module and Associated setup.py File

//
//  Matrix_Py.h

#ifndef __FinancialSamples__Matrix_Py__
#define __FinancialSamples__Matrix_Py__

#include <iostream>

#include "Matrix.h"

class MatrixP : public Matrix {
public:
    MatrixP(int a);
    MatrixP(int a, int b);
    MatrixP(const MatrixP &p);
    ~MatrixP();
    void set(int a, int b, double v);
    double get(int a, int b);
};

#endif /* defined(__FinancialSamples__Matrix_Py__) */

//
//  Matrix_Py.cpp

#include "Matrix_Py.h"

// include this header file for access to boost::python templates and macros
#include <boost/python.hpp>

// add the using clause to reduce namespace clutter
using namespace boost::python;

MatrixP::MatrixP(int a)
: Matrix(a)
{

}

MatrixP::MatrixP(int a, int b)
: Matrix(a, b)
{

}

MatrixP::MatrixP(const MatrixP &p)
: Matrix(p)
{

}

MatrixP::~MatrixP()
{
}

void MatrixP::set(int a, int b, double v)
{
    (*this)[a][b] = v;
}

double MatrixP::get(int a, int b)
{
    return (*this)[a][b];
}

// this macro generates all the boilerplate required by the Python API
BOOST_PYTHON_MODULE(matrix)
{

    // defines a new class to be exported
    class_<MatrixP>("Matrix",
                   init<int>())    // the init form defines a constructor

        // another constructor with two int parameters
        .def(init<int, int>())

        // here are some standard functions (name first, member function second)
        .def("add", &MatrixP::add)
        .def("subtract", &MatrixP::subtract)
        .def("multiply", &MatrixP::multiply)
        .def("numRows", &MatrixP::numRows)
        .def("trace", &MatrixP::trace)
        .def("transpose", &MatrixP::transpose)
        .def("set", &MatrixP::set)
        .def("get", &MatrixP::get)
    ;
}

#
# matrix-setup.py
#
# python code to build the matrix module
from distutils.core import setup, Extension

# you need to include include and library paths for the boost::python library
setup(name="matrix", version="1.0",
      ext_modules=[Extension("matrix", ["Matrix.cpp", "matrix_Py.cpp"],
                             include_dirs=["/opt/local/include/"],
                             library_dirs=["/opt/local/lib/"],
                             libraries=["boost_python-mt"])])

Running the Code

To compile the code, you can follow a procedure similar to the one described in the previous section. This means that you can use the Python build system to compile the extension (usually into a .so or .dll format). To do this, you need to create a setup file, which in our case is listed as matrix-setup.py. Notice that the libraries key is also listed in the matrix-setup.py file. This key tells the build system to link against the boost_python-mt library. You may also need to change the include_dirs and the library_dirs keys to the location where boost is installed in your system. This following is the result of running the setup file through Python.

$ python matrix-setup.py build_ext -i
running build_ext
building 'matrix' extension
cc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -arch i386 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch x86_64 -arch i386 -pipe -I/opt/local/include/ -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c Matrix.cpp -o build/temp.macosx-10.9-intel-2.7/Matrix.o
c++ -bundle -undefined dynamic_lookup -arch x86_64 -arch i386 -Wl,-F. build/temp.macosx-10.9-intel-2.7/Matrix.o build/temp.macosx-10.9-intel-2.7/matrix_Py.o -L/opt/local/lib/ -lboost_python-mt -o /Users/carlosoliveira/code/FinancialSamples/FinancialSamples/matrix.so

After this process is finished, you should have a file called matrix.so (or matrix.dll, if you’re building on a Windows system). You can load it with an import statement like the following:

$ python
Python 2.7.5 (default, Mar 9 2014, 22:15:05)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import matrix
>>> m = matrix.Matrix(5,5)
>>> m.set(2,2,4)
>>> m.get(2,2)
4.0

Using Lua as an Extension Language

Use Lua as an extension library for classes written in C++.

Solution

Lua is a scripting language that was designed to provide extension mechanisms for C and C++ code and to work as an embedded language for other applications. In this respect, it has been very successful, with a large number of software products that currently use Lua to implement extension modules based on existing C and C++ class libraries. Examples of such uses can be found in computer games, image-processing packages, and software for the financial industry.

The success of Lua is linked to its simple system, which tries to mix as closely as possible with the C and C++ environment. With this goal in mind, Lua offers only the basic mechanisms necessary to build a dynamic, garbage-collected runtime system. These features of the language have made it an easy choice for the creation of programmatic extensions to large-scale application code bases.

You can download Lua in its source form from the main web site http://lua.org. The easiest way to integrate Lua into a C++ project is to add the source files directly. You can also decide to create a separate library containing the Lua interpreter and link it to your application, using information made available in the Lua documentation. To use Lua as an extension language, the first step is to import and initialize the Lua runtime engine. This can be done using the Lua C API, which is part of the standard installation of the language. The main header file, lua.h, gives developers access to the main features of the runtime engine, as well as to Lua’s standard library.

Following is the main function for the example application, where you can see the sequence of operations necessary to load Lua into your program:

int main (void) {
    char buff[256];

    lua_State *L = luaL_newstate();
    int error;

    // load some of the (C) libraries included with Lua
    luaopen_base(L);
    luaopen_table(L);
    luaopen_io(L);
    luaopen_string(L);
    luaopen_math(L);

    // load LuaOption object
    LuaWrapper<LuaOption>::Register(L);

    while (fgets(buff, sizeof(buff), stdin) != NULL) {
        error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
        lua_pcall(L, 0, 0, 0);
        if (error) {
            cerr << lua_tostring(L, -1) << endl;
            lua_pop(L, 1);  // remove error from Lua stack
        }

    lua_close(L);
    return 0;
}

Most of the functions in this API use the lua_State structure as a parameter. As you can see in the aforementioned example, you can create a new lua_State object using the luaL_newstate function. Once this initialization step has been completed, you can load some of the libraries included with the Lua runtime. It is possible to choose subsets of the library using functions such as luaopen_string and luaopen_math, which load Lua functions to handle strings and math operations, respectively.

The next step is to load any user-defined libraries that you might need into Lua data tables. The Lua language is organized in such a way that its dynamic information is stored in a stack, that is, a first-in/first-out data structure. There is a global stack, where the equivalent of global variables is stored. You can use this stack to store new tables as necessary. To store individual values, you push them into the stack using functions such as lua_pushstring, lua_pushnumber, or lua_pushclosure (for functions). You can read data from the top of the stack using functions such as lua_tonumber.

Another thing that can be done with the Lua runtime is to directly call one of the Lua functions. To call a function, you need to push the name of the function you want to call into the stack, followed by the required parameters. Next, you need to call the function lua_pcall, which performs the call. You can see an example for lua_pcall at the end of the main function presented previously. Finally, you can access the results of the function, which on return are stored at the top of the stack.

While this initially seems to be a lot of work, it can be done easily because of the generic nature of the Lua extension API. I provide an example of how to access a C++ class from Lua code using the Option class, which contains just two data members: a ticker (string) and the strike price (double). The original class is accessed from Lua using the class LuaOption. The reason this is necessary has to do with the fact that Lua can interact only with functions that receive a parameter of type lua_State. Each method of LuaOption retrieves the data from the stack, calls the corresponding method in the Option class, and returns the results in the stack. Finally, the LuaOption class is registered with the help of the template LuaWrap.

Complete Code

The example in Listing 15-4 shows how to use the Lua API to embed an extension language into your application. The only class exposed in this example is the Option class. The class LuaOption is a simple wrapper for Option, and it is responsible for converting parameters from and to Lua types. The main function has the ability of loading a Lua file and calling any functions contained in it.

Listing 15-4. Class Option and Its Lua Wrapper LuaOption

//
//  Option.h

#ifndef __FinancialSamples__Option__
#define __FinancialSamples__Option__

#include <string>

class Option {
public:
    Option(const std::string &ticker, double strike);
    Option(const Option &p);
    ~Option();
    Option &operator=(const Option &p);

    std::string ticker();
    double strike();

    void setTicker(const std::string &);
    void setStrike(double);

private:
    std::string m_ticker;
    double m_strike;

};

#endif /* defined(__FinancialSamples__Option__) */

//
//  Option.cpp

#include "Option.h"

Option::Option(const std::string &ticker, double strike)
: m_ticker(ticker),
  m_strike(strike)
{
}

Option::Option(const Option &p)
: m_ticker(p.m_ticker),
  m_strike(p.m_strike)
{
}

Option::~Option()
{
}
.
Option &Option::operator=(const Option &p)
{
    if (this != &p)
    {
        m_ticker = p.m_ticker;
        m_strike = p.m_strike;
    }
    return *this;
}

std::string Option::ticker()
{
    return m_ticker;
}

double Option::strike()
{
    return m_strike;
}

void Option::setTicker(const std::string &s)
{
    m_ticker = s;
}

void Option::setStrike(double val)
{
    m_strike = val;
}

//
//  LuaOption.h

#ifndef __FinancialSamples__LuaOption__
#define __FinancialSamples__LuaOption__

#include "LuaWrap.h"

class Option;

#include <string>

class LuaOption {
public:
    LuaOption(lua_State *l);
    void setObject(lua_State *l);
.
    static const char className[];
    static LuaWrapper<LuaOption>::RegType methods[];

    // Lua functions should receive lua_State and return int
    int ticker(lua_State *l);
    int strike(lua_State *l);

    int setTicker(lua_State *l);
    int setStrike(lua_State *l);
private:
    Option *m_option;
};

#endif /* defined(__FinancialSamples__LuaOption__) */

//
//  LuaOption.cpp

#include "LuaOption.h"
#include "Option.h"

#include <lauxlib.h>

const char LuaOption::className[] = "Option";

LuaOption::LuaOption(lua_State *L)
{
    m_option = (Option*)lua_touserdata(L, 1);
}

void LuaOption::setObject(lua_State *L)
{
    m_option = (Option*)lua_touserdata(L, 1);
}

int LuaOption::ticker(lua_State *L)
{
    lua_pushstring(L, m_option->ticker().c_str());
    return 1;
}

int LuaOption::strike(lua_State *L)
{
    lua_pushnumber(L, m_option->strike());
    return 1;
}

int LuaOption::setTicker(lua_State *L)
{
    m_option->setTicker((const char*)luaL_checkstring(L, 1));
    return 0;
}
.
int LuaOption::setStrike(lua_State *L)
{
    m_option->setStrike((double)luaL_checknumber(L, 1));
    return 0;
}

#define method(class, name) {#name, &class::name}
LuaWrapper<LuaOption>::RegType LuaOption::methods[] = {
    method(LuaOption, ticker),
    method(LuaOption, strike),
    method(LuaOption, setTicker),
    method(LuaOption, setStrike),
    {0,0}
};

//
//  LuaWrapper.h
// original code from luna wrapper example (from http://lua-users.org/wiki/LunaWrapper)

#ifndef __FinancialSamples__Luna__
#define __FinancialSamples__Luna__

#include <lua.h>

template<class T> class LuaWrapper {
public:
    static void Register(lua_State *L) {
        lua_pushcfunction(L, &LuaWrapper<T>::constructor);
        lua_setglobal(L, T::className);

        luaL_newmetatable(L, T::className);
        lua_pushstring(L, "__gc");
        lua_pushcfunction(L, &LuaWrapper<T>::gc_obj);
        lua_settable(L, -3);
    }

    static int constructor(lua_State *L) {
        T* obj = new T(L);

        lua_newtable(L);
        lua_pushnumber(L, 0);
        T** a = (T**)lua_newuserdata(L, sizeof(T*));
        *a = obj;
        luaL_getmetatable(L, T::className);
        lua_setmetatable(L, -2);
        lua_settable(L, -3); // table[0] = obj;

        for (int i = 0; T::methods[i].name; i++) {
            lua_pushstring(L, T::methods[i].name);
            lua_pushnumber(L, i);
            lua_pushcclosure(L, &LuaWrapper<T>::thunk, 1);
            lua_settable(L, -3);
        }
        return 1;
    }.
    static int thunk(lua_State *L) {
        int i = (int)lua_tonumber(L, lua_upvalueindex(1));
        lua_pushnumber(L, 0);
        lua_gettable(L, 1);

        T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
        lua_remove(L, -1);
        return ((*obj)->*(T::methods[i].mfunc))(L);
    }

    static int gc_obj(lua_State *L) {
        T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
        delete (*obj);
        return 0;
    }

    struct RegType {
        const char *name;
        int(T::*mfunc)(lua_State*);
    };
};

#endif /* defined(__FinancialSamples__LuaWrapper__) */

//
//  LuaMain.cpp

#include "LuaMain.h"

#include <iostream>
.
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

using std::cout;
using std::cerr;
using std::endl;

int main (void) {
    char buff[256];

    lua_State *L = luaL_newstate();
    int error;

    // load some of the (C) libraries included with Lua
    luaopen_base(L);
    luaopen_table(L);
    luaopen_io(L);
    luaopen_string(L);
    luaopen_math(L);

    // load LuaOption object
    LuaWrapper<LuaOption>::Register(L);

    while (fgets(buff, sizeof(buff), stdin) != NULL) {
        error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
        lua_pcall(L, 0, 0, 0);
        if (error) {
            cerr << lua_tostring(L, -1) << endl;
            lua_pop(L, 1);  // remove error from Lua stack
        }
    }

    lua_close(L);
    return 0;
}.

Running the Code

.You can build the code in Listing 15-4 using any standards-compliant C++ compiler. The only additional step is that you will have to add the path for the Lua include files and libraries in the compiler configuration. For example, in my system I compiled the Lua files in the directory /code/lua-5.2.3/src/, so this code can be built as follows:

$ c++ -o luatest Option.cpp LuaOption.cpp  LuaMain.cpp -I/Users/code/lua-5.2.3/src 
      -L/Users /code/lua-5.2.3/src/ -llua

Conclusion

Extension languages such as Python and Lua have become very popular in the last few years. They provide the ability to quickly develop applications that are composed of existing components. Thanks to C++ flexibility, however, as you have seen, it is possible to create C++ libraries that can be easily integrated with these languages.

Initially, you have seen how to use Python as an extension language for C++ classes. The class presented as an example can be accessed directly from Python code by simply using the Python external API. You have seen how to convert data from and into the data structures maintained by Python. In a second coding example you learned how to use the boost::python library, which provides a more concise way to export C++ data types to Python. I have discussed some of the advantages and disadvantages of each method.

Lua is another language that has grown in popularity in the last few years. With its small footprint, Lua is an ideal candidate for the position of extension language for libraries written in C++. Due to its simplicity and modularity, you can easily embed the Lua interpreter in a C++ application. In this chapter, you saw a C++ coding technique that shows how to easily integrate Lua into your applications.

Using your C++ code as an external library is one of the many ways you can connect with other tools and environments. Another option is to integrate your financial C++ code into existing scientific programming tools. Two of the most often used scientific tools for data analysis are R and Maxima. In Chapter 16 you will learn more about these tools and how to integrate C++ into the workflow provided by these applications.

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

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