Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion doc/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

# git master {#master}

* [43](https://github.com/HBPVis/Servus/pull/47):
* [53](https://github.com/HBPVis/Servus/pull/53):
Add notifyDeserialized and notifySerialize functions in serializable
* [47](https://github.com/HBPVis/Servus/pull/47):
Use standard '&' separator instead of ',' for KV pairs in URI query [RFC6570]

# Release 1.3 (07-04-2016)
Expand Down
91 changes: 75 additions & 16 deletions servus/serializable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,96 @@

namespace servus
{

class Serializable::Impl
{
public:
void notifyDeserialized() const
{
if( deserialized )
deserialized();
}

void notifySerialize() const
{
if( serialize )
serialize();
}

Serializable::DeserializedCallback deserialized;
Serializable::SerializeCallback serialize;
};

Serializable::Serializable()
: _impl( new Serializable::Impl( ))
{}

Serializable::~Serializable()
{
delete _impl;
}

uint128_t Serializable::getTypeIdentifier() const
{
return make_uint128( getTypeName( ));
}

Serializable::ChangeFunc Serializable::setUpdatedFunction(
const ChangeFunc& func )
bool Serializable::fromBinary( const Data& data )
{
return fromBinary( data.ptr.get(), data.size );
}

bool Serializable::fromBinary( const void* data, const size_t size )
{
if( _fromBinary( data, size ))
{
_impl->notifyDeserialized();
return true;
}
return false;
}

Serializable::Data Serializable::toBinary() const
{
const ChangeFunc old = _updated;
_updated = func;
return old;
_impl->notifySerialize();
return _toBinary();
}

Serializable::ChangeFunc Serializable::setRequestedFunction(
const ChangeFunc& func )
bool Serializable::fromJSON( const std::string& json )
{
const ChangeFunc old = _requested;
_requested = func;
return old;
if( _fromJSON( json ))
{
_impl->notifyDeserialized();
return true;
}
return false;
}

void Serializable::notifyUpdated() const
std::string Serializable::toJSON() const
{
if( _updated )
_updated();
_impl->notifySerialize();
return _toJSON();
}

void Serializable::notifyRequested() const
void Serializable::registerDeserializedCallback(
const DeserializedCallback& callback )
{
if( _requested )
_requested();
if( _impl->deserialized && callback )
throw( std::runtime_error(
"A DeserializedCallback is already registered. "
"Only one is supported at the moment" ));

_impl->deserialized = callback;
}

void Serializable::registerSerializeCallback(
const SerializeCallback& callback )
{
if( _impl->serialize && callback )
throw( std::runtime_error( "A SerializeCallback is already registered. "
"Only one is supported at the moment" ));

_impl->serialize = callback;
}

}
65 changes: 38 additions & 27 deletions servus/serializable.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ namespace servus
class Serializable
{
public:
virtual ~Serializable() {}
Serializable();
virtual ~Serializable();

/** @name Serialization methods */
//@{
Expand All @@ -64,10 +65,8 @@ class Serializable
* Update this serializable from its binary representation.
* @return true on success, false on error.
*/
bool fromBinary( const Data& data )
{ return _fromBinary( data.ptr.get(), data.size ); }
bool fromBinary( const void* data, const size_t size )
{ return _fromBinary( data, size ); }
SERVUS_API bool fromBinary( const Data& data );
SERVUS_API bool fromBinary( const void* data, const size_t size );

/**
* Get a binary representation of this object.
Expand All @@ -77,49 +76,55 @@ class Serializable
*
* @return the binary representation of this object.
*/
Data toBinary() const { return _toBinary(); }
SERVUS_API Data toBinary() const;

/**
* Update this serializable from its JSON representation.
* @return true on success, false on error.
*/
bool fromJSON( const std::string& json ) { return _fromJSON( json ); }
SERVUS_API bool fromJSON( const std::string& json );

/** @return the JSON representation of this serializable. */
std::string toJSON() const { return _toJSON(); }
SERVUS_API std::string toJSON() const;
//@}

/** @name Change Notifications */
//@{
/** Function for change notification. */
/** Callbacks for change notifications. */
#ifdef SERVUS_USE_CXX03
typedef boost::function< void() > ChangeFunc;
typedef boost::function< void() > DeserializedCallback;
typedef boost::function< void() > SerializeCallback;
#else
typedef std::function< void() > ChangeFunc;
typedef std::function< void() > DeserializedCallback;
typedef std::function< void() > SerializeCallback;
#endif

/**
* Set a new function called after the object has been updated.
* Register a function called after the object has been updated remotely
* (via a subscriber, a http server, loading from file...).
* Only one callback is supported at the moment, to deregister the callback,
* call this function with a 'nullptr' (or 0) parameter.
*
* @return the previously set function.
* @throw if a DeserializedCallback is already registered and the specified
* callback is not 'nullptr' (or 0)
*/
SERVUS_API ChangeFunc setUpdatedFunction( const ChangeFunc& func );
SERVUS_API void registerDeserializedCallback(
const DeserializedCallback& callback );

/**
* Set a new function called when a request has been received.
* Register a function to be called when the serializable object is about
* to be serialized.
* Only one callback is supported at the moment, to deregister the callback,
* call this function with a 'nullptr' (or 0) parameter.
*
* Invoked before the object is published.
* @return the previously set function.
* @throw if a SerializedCallback is already registered and the specified
* callback is not 'nullptr' (or 0)
*/
SERVUS_API ChangeFunc setRequestedFunction( const ChangeFunc& func );

/** @internal used by ZeroEQ to invoke updated function */
SERVUS_API void notifyUpdated() const;
/** @internal used by ZeroEQ to invoke updated function */
SERVUS_API void notifyRequested() const;
SERVUS_API void registerSerializeCallback(
const SerializeCallback& callback );
//@}

protected:
private:
/**
* @name API for serializable sub classes.
*
Expand All @@ -138,9 +143,15 @@ class Serializable
{ throw std::runtime_error( "JSON serialization not implemented" ); }
//@}

private:
ChangeFunc _updated;
ChangeFunc _requested;
Serializable( const Serializable& );
Serializable& operator=( const Serializable& );
#ifdef SERVUS_USE_CXX11
Serializable( Serializable&& ) = delete;
Serializable& operator=( Serializable&& ) = delete;
#endif

class Impl;
Copy link
Copy Markdown

@hernando hernando Apr 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is future binary compatibility so important to do this?

Impl* _impl;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to deactivate or implement the assignment operator and copy constructor, otherwise will lead to double free/missing free of _impl.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem for move if C++11

};

}
Expand Down
103 changes: 103 additions & 0 deletions tests/serializable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* Copyright (c) 2013-2016, Jafet.VillafrancaDiaz@epfl.ch
*
* This file is part of Servus <https://github.com/HBPVIS/Servus>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3.0 as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#define BOOST_TEST_MODULE servus_serializable
#include <boost/test/unit_test.hpp>

#include <servus/uint128_t.h>
#include <servus/serializable.h>

void dummyFunction(){}

class SerializableObject : public servus::Serializable
{
public:
std::string getTypeName() const final { return "test::serializable"; }

servus::uint128_t getTypeIdentifier() const final
{
return servus::make_uint128( getTypeName( ));
}

private:
bool _fromBinary( const void*, const size_t ) { return true; }
bool _fromJSON( const std::string& ) { return true; }
};


BOOST_AUTO_TEST_CASE(serializable_types)
{
SerializableObject obj;
BOOST_CHECK_EQUAL( obj.getTypeName(), "test::serializable" );
BOOST_CHECK_EQUAL( servus::make_uint128( obj.getTypeName( )),
obj.getTypeIdentifier( ));
}

BOOST_AUTO_TEST_CASE(serializable_registerSerialize)
{
SerializableObject obj;
servus::Serializable::SerializeCallback callback( dummyFunction );

obj.registerSerializeCallback( callback );
BOOST_CHECK_THROW( obj.registerSerializeCallback( callback ),
std::runtime_error ); // callback already registered

BOOST_CHECK_NO_THROW( obj.registerSerializeCallback( 0 ));
BOOST_CHECK_NO_THROW( obj.registerSerializeCallback( callback ));

BOOST_CHECK_THROW( obj.registerSerializeCallback( callback ),
std::runtime_error ); // callback already registered
}

BOOST_AUTO_TEST_CASE(serializable_registerDeserialized)
{
SerializableObject obj;
servus::Serializable::DeserializedCallback callback( dummyFunction );

obj.registerDeserializedCallback( callback );
BOOST_CHECK_THROW( obj.registerDeserializedCallback( callback ),
std::runtime_error ); // callback already registered

BOOST_CHECK_NO_THROW( obj.registerDeserializedCallback( 0 ));
BOOST_CHECK_NO_THROW( obj.registerDeserializedCallback( callback ));

BOOST_CHECK_THROW( obj.registerDeserializedCallback( callback ),
std::runtime_error ); // callback already registered
}

BOOST_AUTO_TEST_CASE(serializable_binary)
{
SerializableObject obj;

// fromBinary implemented
BOOST_CHECK_NO_THROW( obj.fromBinary( new float[3], 3 ));

// default toBinary (unimplemented)
BOOST_CHECK_THROW( obj.toBinary(), std::runtime_error );
}

BOOST_AUTO_TEST_CASE(serializable_json)
{
SerializableObject obj;

// fromJson implemented
BOOST_CHECK_NO_THROW( obj.fromJSON( std::string( "testing..." )));

// default toJson (unimplemented)
BOOST_CHECK_THROW( obj.toJSON(), std::runtime_error );
}