Serializing struct as Mathematica Association¶
structs can be packed to or unpacked from Mathematica Association or list or any other mathematica construct. It means you can write part of your code as Mathematica package (.m) that takes an association as input. At the same time use structs on your C++ side.
struct point{ std::pair<int, int> location; std::string name; double elevation; point(): location(std::make_pair(0, 0)), name(""), elevation (0.0f){} point (std::pair<int, int> loc, const std::string& name_, double elevation_): location(loc), name(name_), elevation(elevation_){} }; value result; point pti(std::make_pair(1, 1), "Hallo", 100.0f); shell << Evaluate(pti); shell >> result; point pto = cast<point>(result);
struct point will be serialized to an Association and deserialized from that association upon cast. The mapping of the member variables needs to be declared by using macro MATHEMATICA_ASSOCIATE and MATHEMATICA_PROPERTY as shown in the following example. The data types of the properties are to be declared in the same sequence as the member variables with MATHEMATICA_ASSOCIATE. the property names will become the key of the Rule inside the mathematica Association.
MATHEMATICA_ASSOCIATE(point, std::pair<int, int>, std::string, double){ MATHEMATICA_PROPERTY(0, location) MATHEMATICA_PROPERTY(1, name) MATHEMATICA_PROPERTY(2, elevation) };
The point struct pti will be serialized as the following Mathematica construct
Association[Rule["location", List[1, 1]], Rule["name", "Hallo"], Rule["elevation", 100]]
Nesting of structs are also handled similarly (no extra work) As shown in the complete example below.
#include <iostream> #include <mathematica++/mathematica++.h> MATHEMATICA_DECLARE(Evaluate) using namespace mathematica; // { declare the structs struct point{ std::pair<int, int> location; std::string name; double elevation; point(): location(std::make_pair(0, 0)), name(""), elevation (0.0f){} point (std::pair<int, int> loc, const std::string& name_, double elevation_): location(loc), name(name_), elevation(elevation_){} }; struct circle{ point center; int radius; circle(): center(point()), radius(0){} circle(const point& c, int r): center(c), radius(r){} }; // } declarations // { declare the associations MATHEMATICA_ASSOCIATE(point, std::pair<int, int>, std::string, double){ MATHEMATICA_PROPERTY(0, location) MATHEMATICA_PROPERTY(1, name) MATHEMATICA_PROPERTY(2, elevation) }; MATHEMATICA_ASSOCIATE(circle, point, int){ MATHEMATICA_PROPERTY(0, center) MATHEMATICA_PROPERTY(1, radius) }; // } association declarations int main(){ connector shell; value result; point pti(std::make_pair(1, 1), "Hallo", 100.0f); circle ci(pti, 5); // circle as input (serialized) shell << Evaluate(ci); shell >> result; std::cout << result << std::endl; circle co = cast<circle>(result); // circle as output (deserialized) return 0; }
the above will create an Association
Association[Rule["center", Association[Rule["location", List[1, 1]], Rule["name", "Hallo"], Rule["elevation", 100]]], Rule["radius", 5]]
structs are also allowed to be inside std::vector seamlessly. In the next snippet sensor_deployment struct we have a vector of circles shown before which will be serialized as Mathematica List inside a Rule of an Association. The above example will serialize to the following mathematica construct
Association[ Rule["sensors", List[ Association[ Rule["center", Association[ Rule["location", List[4, 5]], Rule["name", "c1"], Rule["elevation", 300] ] ], Rule["radius", 4] ], Association[ Rule["center", Association[ Rule["location", List[5, 6]], Rule["name", c2], Rule["elevation", 300] ] ], Rule["radius", 5] ] ]], Rule["start", DateObject[List[2018, 10, 3, 14, 58, 37], Instant, Gregorian, 5.5]], Rule["duration", 100], Rule["job", 1] ]
Serializing C++ objects as Mathematica Objects¶
struct sensor_deployment{ std::vector<circle> sensors; boost::posix_time::ptime start; long duration; long job; };
Following is the association declaration for this struct
MATHEMATICA_ASSOCIATE(sensor_deployment, std::vector<circle>, boost::posix_time::ptime, long, long){ MATHEMATICA_PROPERTY(0, sensors) MATHEMATICA_PROPERTY(1, start) MATHEMATICA_PROPERTY(2, duration) MATHEMATICA_PROPERTY(3, job) };
In the above example we use ptime which has not yet been mapped. Also we do not want that to be serialized as an Association. Instead we want that to be serialized as DateObject in mathematica. So we use the following typemap.
MATHEMATICA_TYPEMAP(boost::posix_time::ptime, (boost::tuple<boost::tuple<unsigned, unsigned, unsigned, unsigned, unsigned, unsigned>, std::string, std::string, double>)){ mathematica::m operator()(const class_type& obj){ auto date = obj.date(); auto time = obj.time_of_day(); return mathematica::m("DateObject")(List((int)date.year(), (int)date.month(), (int)date.day(), time.hours(), time.minutes(), time.seconds())); } class_type operator()(const capture_type& captured){ int y, m, d, h, n, s; boost::tie(y, m, d, h, n, s) = boost::get<0>(captured); class_type ptime = class_type(boost::gregorian::date(y, m, d), boost::posix_time::time_duration(h, n, s)); return ptime; } };
MATHEMATICA_TYPEMAP expects two function call operator overloads. One to serialize and another one to deserialize.
A tuple type shown bellow is provided as capture_type that is used to primarily captured the the mathematica returned expression which is read by by the second function call overload.
boost::tuple<boost::tuple<unsigned, unsigned, unsigned, unsigned, unsigned, unsigned>, std::string, std::string, double> // DateObject[List[2018, 10, 3, 14, 58, 37], Instant, Gregorian, 5.5]
Serializing struct as mathematica List¶
Previously we have serialized struct point as mathematica association. Now we serialize it as List using MATHEMATICA_SEQUENCE and MATHEMATICA_ELEMENT
MATHEMATICA_SEQUENCE(point, std::pair<int, int>, std::string, double){ MATHEMATICA_ELEMENT(0, location) MATHEMATICA_ELEMENT(1, name) MATHEMATICA_ELEMENT(2, elevation) };
The above will serialize as
List[List[1, 1], "Hallo", 100]
Serializing templated objects¶
While specifying serialization rules for templated types the expansion of the above mentioned macros are needed to be used. Following Serialized an std::pair<U, V> to Association[Rule["first", value1], Rule["second", value2]]
namespace mathematica{ template <typename U, typename V> struct association<std::pair<U, V>>: dictionary<association<std::pair<U, V>>, std::pair<U, V>, U, V>{ static auto detail(property<0>){return std::make_pair("first", &std::pair<U, V>::first);} static auto detail(property<1>){return std::make_pair("second", &std::pair<U, V>::second);} }; }
mathematica++ serializes std::pair<U, V> as mathematica List with the following specification.
namespace mathematica{ template <typename U, typename V> struct association<std::pair<U, V>>: sequence<association<std::pair<U, V>>, std::pair<U, V>, U, V>{ static auto detail(property<0>){return &std::pair<U, V>::first;} static auto detail(property<1>){return &std::pair<U, V>::second;} }; }
below is an example of serialization of an user defined templated struct point_2d with GeoPosition using mathematica::typemap
template <typename T> struct point_2d{ T x; T y; }; namespace mathematica{ template <typename T> struct association<point_2d<T>>: mathematica::typemap<association<point_2d<T>>, point_2d<T>, boost::tuple<boost::tuple<T, T>>>{ mathematica::m operator()(const point_2d<T>& obj){ return GeoPosition(List(obj.y, obj.x)); } point_2d<T> operator()(const boost::tuple<boost::tuple<T, T>>& captured){ boost::tuple<double, double> pt_data = boost::get<0>(captured); point_2d<T> pt; std::clog << pt << std::endl; pt.y = boost::get<0>(pt_data); pt.x = boost::get<1>(pt_data); return pt; } }; }