Posts Tagged VC++
Simple Class Serialization With JsonCpp
Posted by Daniel in Programming on April 15, 2011
A recent project of mine necessitated that I find a way to share data between a client application written in C++ and a host application written in C#. After talking with the other programmer on the project, we decided that using Json to share data between the applications would be the best solution. This sent me on a search for a decent API for parsing Json data, which led to JsonCpp.
A cursory glance led me to conclude that this API would likely provide me with everything I would need for object serialization. The problem, however, was coming up with a decent design that would allow for full featured serialization. The goals were simple. I needed to be able to serialize and deserialze a class. I would also need access to all data members, including primitives, data structures, and nested classes.
JsonCpp provides us with the capability to do this with relative ease. As such, I hope to provide you all with a basic design to accomplish this. Luckily, we won’t need a deep understanding of JsonCpp to accomplish our goals. As such, I’m not going to overly explain much of the JsonCpp concepts, as the JsonCpp documentation is a good enough resource.
First, let’s consider a simple test class.
class TestClassA { public: TestClassA( void ); virtual ~TestClassA( void ); private: int m_nTestInt; double m_fTestFloat; std::string m_TestString; bool m_bTestBool; };
This class should provide us with a basic framework to test serialization. We’re going to start with primitive data first (granted, std::string isn’t strictly primitive, but it’s primitive enough).
Now that we have a basic test case, lets design a basic interface that all of our serializable classes can inherit from.
class IJsonSerializable { public: virtual ~IJsonSerializable( void ) {}; virtual void Serialize( Json::Value& root ) =0; virtual void Deserialize( Json::Value& root) =0; };
This should be self explanitory. We obviously need to include json.h in order to have access to the Json::Value class. The Json::Value class is actually fairly complex, and as such we aren’t going to spent much time examining it. Suffice to say, we are going to treat it as a sort of map, mapping our metadata tags to the actual values we will be serializing. This will make much more sense once we work on the implementation of the Serialize() and Deserialize() methods in our classes.
So let’s update our updated test class definition.
class TestClassA : public IJsonSerializable { public: TestClassA( void ); virtual ~TestClassA( void ); virtual void Serialize( Json::Value& root ); virtual void Deserialize( Json::Value& root); private: int m_nTestInt; double m_fTestFloat; std::string m_TestString; bool m_bTestBool; };
Now before we progress further on the C++ side of things, let’s cook up some test Json data to work with.
{ "testboolA" : true, "testfloatA" : 3.14159, "testintA" : 42, "teststringA" : "foo" }
Whether you know Json or not, this data structure should be completely self-explanatory. The goal here is going to be to deserialize this data into our class. However, we have one more thing to do.
The last class we need to design here is the actual serializer class.
class CJsonSerializer { public: static bool Serialize( IJsonSerializable* pObj, std::string& output ); static bool Deserialize( IJsonSerializable* pObj, std::string& input ); private: CJsonSerializer( void ) {}; };
This is a simple little “static class”. We’ll make the constructor private this way we can’t actually instantiate it. We will simply deal with the methods directly. This will let us add a second layer of abstraction to JsonCpp.
Alright, our basic design is done. Let’s get into the implementation. Let’s start with our test class.
void TestClassA::Serialize( Json::Value& root ) { // serialize primitives root["testintA"] = m_nTestInt; root["testfloatA"] = m_fTestFloat; root["teststringA"] = m_TestString; root["testboolA"] = m_bTestBool; } void TestClassA::Deserialize( Json::Value& root ) { // deserialize primitives m_nTestInt = root.get("testintA",0).asInt(); m_fTestFloat = root.get("testfloatA", 0.0).asDouble(); m_TestString = root.get("teststringA", "").asString(); m_bTestBool = root.get("testboolA", false).asBool(); }
Remember when I said we were going to look at the Json::Value class like a map? That should make more sense now. The Serialize() method should make sense. We are simply adding the values to our root object as if it was a map. The keys we are using map directly to the metadata in the Json data. The Deserialize() method is only slightly more complex. We need to use the get() method in order to retrieve our data. The get() method takes two parameters: get( <key>, <defaultvalue>). Knowing that, the method should make sense. We simply call get() with each key and place the values from the Json into our members.
Now let’s move on to the CJsonSerializer class implementation.
bool CJsonSerializer::Serialize( IJsonSerializable* pObj, std::string& output ) { if (pObj == NULL) return false; Json::Value serializeRoot; pObj->Serialize(serializeRoot); Json::StyledWriter writer; output = writer.write( serializeRoot ); return true; } bool CJsonSerializer::Deserialize( IJsonSerializable* pObj, std::string& input ) { if (pObj == NULL) return false; Json::Value deserializeRoot; Json::Reader reader; if ( !reader.parse(input, deserializeRoot) ) return false; pObj->Deserialize(deserializeRoot); return true; }
It should be obvious now why we wanted a second level of abstraction from JsonCpp.
Let’s look at the Serialize() method here. We need to pass two parameters, a pointer to the IJsonSerializable object to be serialized, and a reference to a std::string. That string is going to hold the serialized Json data from our class. This is very rudimentary. We simply create an instance of a Json::Value object to act as our root, and pass it to the Serialize() method of the object in question. That Serialize() method will fill the Json::Value object with all of the serialized data. We then create an instance of Json::StyledWriter, and use it to write the Json data to the empty std::string we originally passed.
As for the Deserialize() method, it’s not terribly different aside from the fact that the std::string we pass is going to contain Json instead of being empty. We will create an instance of Json::Reader and use it to parse our input string and fill a Json::Value object for us. We will then use that new Json::Value object and pass it to the IJsonSerializable objects Deserialze() method, which will set the data members based on the Json::Value object’s values.
As you can see, this is all pretty simple. We can now create a simple test case.
TestClassA testClass; std::string input = "{ \"testintA\" : 42, \"testfloatA\" : 3.14159, \"teststringA\" : \"foo\", \"testboolA\" : true }\n"; CJsonSerializer::Deserialize( &testClass, input ); std::cout << "Raw Json Input\n" << input << "\n\n"; std::string output; CJsonSerializer::Serialize( &testClass, output); std::cout << "testClass Serialized Output\n" << output << "\n\n\n";
If everything went according to plan, you should see the following:
Raw Json Input { "testintA" : 42, "testfloatA" : 3.14159, "teststringA" : "foo", "testboolA" : true } testClass Serialized Output { "testboolA" : true, "testfloatA" : 3.14159, "testintA" : 42, "teststringA" : "foo" }
Essentially our test case simply created an “empty” TestClass object. It then deserialized a string of input Json data into the class, filling it’s members. We then create a new empty output string, and deserialize our TestClass object into that string. If the outut is what we expect, then we can assume that basic serialization and deserialization is working!
C++ Vector2D & Rectangle classes
Posted by Daniel in Programming on May 30, 2010
I’n my current C++ class we’ve been playing with the Allegro C++ library to create some 2D games. While Allegro is a fairly powerful library, it’s shortcomings made me miss the Microsoft XNA Framework quite a bit. I suppose I cannot fault Allegro in this regard, but I have grown so accustomed to using Microsoft’s implementation of 2D vectors and rectangles that learning new variants seems convoluted.
Now, before anyone judges me for not wanting to learn a new API, consider this. Allegro has a plethora of 2D methods, but the only vector methods I am able to find are 3D. Fine you say, just zero the Z element and be done with it.
Well sure. But would I rather use a non OOP implementation like this:
float x = <someXValue>; float y = <someYValue>; float z = 0.0f; void normalize_vector_f(float *x, float *y, float *z);
Or would I rather take an OOP approach:
Vector2D vec( <someXValue>, <someYValue> ); vec.Normalize();
Call me crazy, but I prefer the later. As such, I’m going to go ahead and post my Vector2D class here, as well as the Rectangle class I am using. Normally I would cite sources I used for help in writing these classes. However, in the case of the Vector2D class most of the methods used are fairly standard and there is nothing really out of the ordinary.
As for the Rectangle class, there are some cool static methods I added that Microsoft released in their RectangleExtensions class (released under the MSPL). I ported these methods over from C# to C++,and they seem to work correctly, however I haven’t tested them thoroughly and they may have a bug or two.
Anyway, enough ranting. Here’s the code.
Vector2D.h
#ifndef _VECTOR2D_H #define _VECTOR2D_H #pragma once #include <math.h> class Vector2D { public: Vector2D(float x = 0, float y = 0); ~Vector2D() {}; void Rotate( const float angle ); float Magnitude() const; float Normalize(); float DotProduct( const Vector2D& v2 ) const; float CrossProduct( const Vector2D& v2 ) const; static Vector2D Zero(); static float Distance( const Vector2D& v1, const Vector2D& v2); Vector2D& operator= ( const Vector2D& v2 ); Vector2D& operator+= ( const Vector2D& v2 ); Vector2D& operator-= ( const Vector2D& v2 ); Vector2D& operator*= ( const float scalar); Vector2D& operator/= ( const float scalar); const Vector2D operator+( const Vector2D &v2 ) const; const Vector2D operator-( const Vector2D &v2 ) const; const Vector2D operator*( const float scalar ) const; const Vector2D operator/( const float scalar ) const; bool operator== ( const Vector2D& v2 ) const; bool operator!= ( const Vector2D& v2 ) const; public: float x, y; }; #endif
Vector2D.cpp
#include "Vector2D.h" //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- Vector2D::Vector2D( float x, float y ) { this->x = x; this->y = y; } //----------------------------------------------------------------------------- // Purpose: Rotate a vector //----------------------------------------------------------------------------- void Vector2D::Rotate( const float angle ) { float xt = (x * cosf(angle)) - (y * sinf(angle)); float yt = (y * cosf(angle)) + (x * sinf(angle)); x = xt; y = yt; } //----------------------------------------------------------------------------- // Purpose: Get vector magnitude //----------------------------------------------------------------------------- float Vector2D::Magnitude() const { return sqrtf(x * x + y * y); } //----------------------------------------------------------------------------- // Purpose: Convert vector to a unit vector and return previous magnitude //----------------------------------------------------------------------------- float Vector2D::Normalize() { float mag = Magnitude(); if(mag != 0.0) { x /= mag; y /= mag; } return mag; } //----------------------------------------------------------------------------- // Purpose: Dot Product //----------------------------------------------------------------------------- float Vector2D::DotProduct( const Vector2D &v2 ) const { return (x * v2.x) + (y * v2.y); } //----------------------------------------------------------------------------- // Purpose: Cross Product //----------------------------------------------------------------------------- float Vector2D::CrossProduct( const Vector2D &v2 ) const { return (x * v2.y) - (y * v2.x); } //----------------------------------------------------------------------------- // Purpose: Return an empty vector //----------------------------------------------------------------------------- Vector2D Vector2D::Zero() { return Vector2D(0, 0); } //----------------------------------------------------------------------------- // Purpose: Get distance between two vectors //----------------------------------------------------------------------------- float Vector2D::Distance( const Vector2D& v1, const Vector2D& v2) { return sqrtf( pow((v2.x - v1.x), 2 ) + pow((v2.y - v1.y), 2) ); } Vector2D& Vector2D::operator= ( const Vector2D& v2 ) { if (this == &v2) return *this; x = v2.x; y = v2.y; return *this; } Vector2D& Vector2D::operator+= ( const Vector2D& v2 ) { x += v2.x; y += v2.y; return *this; } Vector2D& Vector2D::operator-= ( const Vector2D& v2 ) { x -= v2.x; y -= v2.y; return *this; } Vector2D& Vector2D::operator*= ( const float scalar ) { x *= scalar; y *= scalar; return *this; } Vector2D& Vector2D::operator/= ( const float scalar ) { x /= scalar; y /= scalar; return *this; } const Vector2D Vector2D::operator+( const Vector2D &v2 ) const { return Vector2D(*this) += v2; } const Vector2D Vector2D::operator-( const Vector2D &v2 ) const { return Vector2D(*this) -= v2; } const Vector2D Vector2D::operator*( const float scalar ) const { return Vector2D(*this) *= scalar; } const Vector2D Vector2D::operator/( const float scalar ) const { return Vector2D(*this) /= scalar; } bool Vector2D::operator== ( const Vector2D& v2 ) const { return ((x == v2.x) && (y == v2.y)); } bool Vector2D::operator!= ( const Vector2D& v2 ) const { return !((x == v2.x) && (y == v2.y)); }
Rectangle.h
#ifndef _RECTANGLE_H #define _RECTANGLE_H #pragma once #include "Vector2D.h" class Rectangle { public: Rectangle( int x = 0, int y = 0, int w = 0, int h = 0 ); ~Rectangle( void ) {}; inline int Left( void ) const { return x; } inline int Right( void ) const { return x + w; } inline int Top( void ) const { return y; } inline int Bottom( void ) const { return y + h; } bool Contains( Vector2D& vVec ) const; bool Contains( int x, int y ) const; static Rectangle Empty(); // Static methods below are derived from the RectangleExtensions class // written in C#, released under the MSPL static Vector2D GetIntersectionDepth( const Rectangle& rectA, const Rectangle& rectB ); static Vector2D GetBottomCenter( const Rectangle& rect ); static Vector2D GetCenter( const Rectangle& rect ); static float GetDistance( const Rectangle& rectA, const Rectangle& rectB); static Vector2D GetDirection( const Rectangle& rectA, const Rectangle& rectB); Rectangle& operator= ( const Rectangle& r2 ); bool operator== ( const Rectangle& r2 ) const; bool operator!= ( const Rectangle& r2 ) const; public: int x, y, w, h; }; #endif
Rectangle.cpp
#include "Rectangle.h" #include <stdlib.h> //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- Rectangle::Rectangle( int x, int y, int w, int h ) { this->x = x; this->y = y; this->w = w; this->h = h; } //----------------------------------------------------------------------------- // Purpose: Check if rectangle contains a 2D vector //----------------------------------------------------------------------------- bool Rectangle::Contains( Vector2D& vVec ) const { if ( ( vVec.x >= x )&& ( vVec.x <= x + w ) && ( vVec.y >= y ) && ( vVec.x <= y + h ) ) { return true; } else return false; } //----------------------------------------------------------------------------- // Purpose: Check if rectangle contains a set of coords //----------------------------------------------------------------------------- bool Rectangle::Contains( int x, int y ) const { if ( ( x >= this->x )&& ( x <= this->x + this->w ) && ( y >= this->y ) && ( x <= this->y + this->h ) ) { return true; } else return false; } //----------------------------------------------------------------------------- // Purpose: Return an empty rectangle //----------------------------------------------------------------------------- Rectangle Rectangle::Empty() { return Rectangle(); } //----------------------------------------------------------------------------- // Purpose: Get intersection depth between two rectangles //----------------------------------------------------------------------------- Vector2D Rectangle::GetIntersectionDepth( const Rectangle& rectA, const Rectangle& rectB ) { // Calculate half sizes. float halfWidthA = rectA.w / 2.0f; float halfHeightA = rectA.h / 2.0f; float halfWidthB = rectB.w / 2.0f; float halfHeightB = rectB.h / 2.0f; // Calculate centers. Vector2D centerA(rectA.x + halfWidthA, rectA.y + halfHeightA); Vector2D centerB(rectB.x + halfWidthB, rectB.y + halfHeightB); // Calculate current and minimum-non-intersecting distances between centers. float distanceX = centerA.x - centerB.x; float distanceY = centerA.y - centerB.y; float minDistanceX = halfWidthA + halfWidthB; float minDistanceY = halfHeightA + halfHeightB; // If we are not intersecting at all, return (0, 0). if ( abs(distanceX) >= minDistanceX || abs(distanceY) >= minDistanceY ) return Vector2D::Zero(); // Calculate and return intersection depths. float depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX; float depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY; return Vector2D(depthX, depthY); } //----------------------------------------------------------------------------- // Purpose: Gets the position of the center of the bottom edge of the rectangle. //----------------------------------------------------------------------------- Vector2D Rectangle::GetBottomCenter(const Rectangle& rect) { return Vector2D( (float)(rect.x + rect.w / 2.0f), (float)(rect.y + rect.h) ); } //----------------------------------------------------------------------------- // Purpose: Gets the position of the center point of a rectangle //----------------------------------------------------------------------------- Vector2D Rectangle::GetCenter(const Rectangle& rect) { return Vector2D( (float)(rect.x + rect.w / 2.0f), (float)(rect.y + rect.h / 2.0f) ); } //----------------------------------------------------------------------------- // Purpose: Gets the floating point distance between the center point // of one rectangle and the center point of another. //----------------------------------------------------------------------------- float Rectangle::GetDistance( const Rectangle& rectA, const Rectangle& rectB ) { return Vector2D::Distance(GetCenter(rectA), GetCenter(rectB)); } //----------------------------------------------------------------------------- // Purpose: Gets the unit vector from one rectangle to another //----------------------------------------------------------------------------- Vector2D Rectangle::GetDirection( const Rectangle& rectA, const Rectangle& rectB ) { Vector2D direction = GetCenter(rectA) - GetCenter(rectB); direction.Normalize(); return direction; } Rectangle& Rectangle::operator= ( const Rectangle& r2 ) { if (this == &r2) return *this; x = r2.x; y = r2.y; w = r2.w; h = r2.h; return *this; } bool Rectangle::operator== ( const Rectangle& r2 ) const { return ((w == r2.w) && (h == r2.h)); } bool Rectangle::operator!= ( const Rectangle& r2 ) const { return !((w == r2.w) && (h == r2.h)); }