The saying "The right tool, for the right job." is one of my most beloved rule of thumb. This especially holds true for programming languages, certain languages are a natural for fit for certain tasks. The fact that the simulations I am running take forever already gives me strong motive to squeeze out the last bit of performance. Hence the choice of C++ as the programming language.
And while C++ certainly allows one to write fast code, it traditionally lacks in the expressiveness department, one of the strong feats of python. This prompted me to wrap parts of my simulation code in python to make it easier to run non-performance critical test code.
However doing so it occurred to me, that c++ is changing fast with the advent of the new standard. To quote Bjarne Stroustrup: "C++11 feels like a new language" and this statement is far from hyperbolic. The leap in evolution is staggering, especially if you consider the rather limited of amount of features added. However those features nicely complement each other and in the end form a coherent whole.
To wrap the code, I use Boost.Python, which uses specialized methods to detect the necessary type information. As an example consider the vector class used in my simulator. It is called Vector3d and represents unsurprisingly a three dimensional vector. To add the type with its constructor taking three doubles and the accessors for the components I added the following code
class_<Vector3d>("Vector3d", init<double, double, double>())
.def("get_x", &Vector3d::getX)
.def("get_y", &Vector3d::getY)
.def("get_z", &Vector3d::getZ);
Clean and succinct. However then I realized that I wanted to add a custom implementation for __repr__ the standard python method to show a representation of the object. Pre C++11 you would either have to subclass vector and add the corresponding methods or add a global function like this
// somewhere outside the module definition
namespace {
namespace WrapVector {
std::string toString(const Vector3d& self) {
return "Vector3d(" +
boost::lexical_cast<std::string>(self.getX()) + ", " +
boost::lexical_cast<std::string>(self.getY()) + ", " +
boost::lexical_cast<std::string>(self.getZ()) + ")";
}
}
}
// inside the module definition
class_<Vector3d>("Vector3d", init<double, double, double>())
.def("get_x", &Vector3d::getX)
.def("get_y", &Vector3d::getY)
.def("get_z", &Vector3d::getZ)
.def("__repr__", &WrapVector::toString);
With C++11 I can solve this problem in a much cleaner, almost pythonic way. My first idea was using lambda functions to create an inline definition of the code. Well nice idea, but Boost.Python does not play nice with lambdas. But it can handle function pointer as you can see above. Luckily lambdas without capture can be implicitly converted to a function pointer. This allowed me to create a wrapper around boost::python::class_ which can directly add lambda functions.
Then the wrapped vector with the custom __repr__ function can be written as
wrap_class<Vector3d>("Vector3d", init<double, double, double>())
.def("get_x", &Vector3d::getX)
.def("get_y", &Vector3d::getY)
.def("get_z", &Vector3d::getZ)
.def_inline("__repr__", [](const Vector3d& self) -> std::string {
return "Vector3d(" +
boost::lexical_cast<std::string>(self.getX()) + ", " +
boost::lexical_cast<std::string>(self.getY()) + ", " +
boost::lexical_cast<std::string>(self.getZ()) + ")";
});
To me it is quite impressive how close this comes to a native implementation in python:
class Vector3d:
def __repr__(self):
return ("Vector3d(" +
str(self.get_x()) + "," +
str(self.get_y()) + "," +
str(self.get_z()) + ")")
For completeness sake, the somewhat lengthy implementation of wrap_class:
// find the function pointer type of a lambda
// adapted from http://stackoverflow.com/a/7943765
template<typename T>
struct lambda_pointer_type
: lambda_pointer_type<decltype(&T::operator())>
{
};
template<typename C, typename R, typename... Args>
struct lambda_pointer_type<R(C::*)(Args...) const>
{
typedef R (*type)(Args...);
};
// return a function pointer for a no-capture lambda
template<typename F>
typename lambda_pointer_type<F>::type lambda_pointer(F f) {
return f;
}
// minimal wrapper around boost::python::class_
template<typename WrappedType>
struct wrap_class : public boost::python::class_<WrappedType>
{
typedef boost::python::class_<WrappedType> parent;
template<typename... Args>
wrap_class(Args... args)
: parent(args...)
{
}
template<typename... Args>
wrap_class<WrappedType>& def(Args... args)
{
parent::def(args...);
return *this;
}
template<typename F>
wrap_class<WrappedType>& def_inline(const char* name, F impl)
{
parent::def(name, lambda_pointer(impl));
return *this;
}
};