Skip to content

Commit

Permalink
Variant conversion enhancements.
Browse files Browse the repository at this point in the history
- ConversionAccess can now access private default constructors (closes #98).
- To/FromVariantConverter now throws error::Conversion exclusively.
- All RPC argument conversion failures are now propagated back to caller
  (fixes #97).
- Added test cases for bad From/ToVariantConverter conversions.
- Enforced Client::LocalSubs non-empty invariant during unsubscribe.
- Added Variant::at accessors (closes #95).
- Updated config.json test/example files for Crossbar 0.13.0.
- Blob is now stored via a pointer within the Variant::Field union, to reduce
  the size of a Variant object.
  • Loading branch information
ecorm committed Mar 28, 2016
1 parent 42d5569 commit ef3541c
Show file tree
Hide file tree
Showing 17 changed files with 505 additions and 195 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
v0.6.2
======
Variant conversion enhancements.

- ConversionAccess can now access private default constructors (closes #98).
- To/FromVariantConverter now throws error::Conversion exclusively.
- All RPC argument conversion failures are now propagated back to caller
(fixes #97).
- Added test cases for bad From/ToVariantConverter conversions.
- Enforced Client::LocalSubs non-empty invariant during unsubscribe.
- Added Variant::at accessors (closes #95).
- Updated config.json test/example files for Crossbar 0.13.0.

v0.6.1
======
Bug fixes.
Expand Down
3 changes: 3 additions & 0 deletions cppwamp/include/cppwamp/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class ConversionAccess
template <typename TObject>
static void convertTo(ToVariantConverter& c, const TObject& obj);

template <typename TObject>
static TObject defaultConstruct();

private:
CPPWAMP_GENERATE_HAS_MEMBER(convert)
CPPWAMP_GENERATE_HAS_MEMBER(convertFrom)
Expand Down
47 changes: 23 additions & 24 deletions cppwamp/include/cppwamp/internal/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,18 +172,21 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
auto kv = readership_.find(sub.id());
if (kv != readership_.end())
{
auto& subMap = kv->second;
if (!subMap.empty())
auto& localSubs = kv->second;
if (!localSubs.empty())
{
auto subKv = subMap.find(sub.slotId({}));
if (subKv != subMap.end())
auto subKv = localSubs.find(sub.slotId({}));
if (subKv != localSubs.end())
{
if (subMap.size() == 1u)
if (localSubs.size() == 1u)
topics_.erase(subKv->second.topic.uri());

subMap.erase(subKv);
if (subMap.empty())
localSubs.erase(subKv);
if (localSubs.empty())
{
readership_.erase(kv);
sendUnsubscribe(sub.id());
}
}
}
}
Expand All @@ -196,19 +199,20 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
auto kv = readership_.find(sub.id());
if (kv != readership_.end())
{
auto& subMap = kv->second;
if (!subMap.empty())
auto& localSubs = kv->second;
if (!localSubs.empty())
{
auto subKv = subMap.find(sub.slotId({}));
if (subKv != subMap.end())
auto subKv = localSubs.find(sub.slotId({}));
if (subKv != localSubs.end())
{
unsubscribed = true;
if (subMap.size() == 1u)
if (localSubs.size() == 1u)
topics_.erase(subKv->second.topic.uri());

subMap.erase(subKv);
if (subMap.empty())
localSubs.erase(subKv);
if (localSubs.empty())
{
readership_.erase(kv);
sendUnsubscribe(sub.id(), std::move(handler));
handler = AsyncTask<bool>();
}
Expand Down Expand Up @@ -591,13 +595,13 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
{
slot(std::move(event));
}
catch (const internal::UnpackError& e)
catch (const Error& e)
{
if (warningHandler_)
{
std::ostringstream oss;
oss << "Received an EVENT with invalid arguments: "
<< e.reason
<< e.args()
<< " (with subId=" << subId
<< " pubId=" << pubId << ")";
warn(oss.str());
Expand Down Expand Up @@ -629,7 +633,8 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
{
this->sendError(WampMsgType::invocation, requestId,
Error("wamp.error.no_such_procedure")
.withArgs("The called procedure does not exist"));
.withArgs("There is no RPC with registration ID " +
std::to_string(regId)));
}
}

Expand Down Expand Up @@ -664,13 +669,7 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
assert(false && "unexpected Outcome::Type");
}
}
catch (internal::UnpackError e)
{
this->sendError(WampMsgType::invocation, reqId,
Error("wamp.error.invalid_argument")
.withArgs(std::move(e.reason)));
}
catch (Error error)
catch (Error& error)
{
yield(reqId, std::move(error));
}
Expand Down
107 changes: 91 additions & 16 deletions cppwamp/include/cppwamp/internal/conversion.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
http://www.boost.org/LICENSE_1_0.txt)
------------------------------------------------------------------------------*/

#include <sstream>
#include <utility>
#include "../error.hpp"
#include "../variant.hpp"
Expand Down Expand Up @@ -119,16 +120,40 @@ FromVariantConverter& FromVariantConverter::operator()(T& value)
@pre The variant is an array.
@pre There are elements left in the variant array.
@pre The element is convertible to the destination type.
@throws error::Access if the variant is not an array.
@throws std::out_of_range if there are no elements left in the
@throws error::Conversion if the variant is not an array.
@throws error::Conversion if there are no elements left in the
variant array.
@throws error::Conversion if the element is not convertible to the
destination type. */
template <typename T>
FromVariantConverter& FromVariantConverter::operator[](T& value)
{
var_.as<Array>().at(index_).to(value);
++index_;
try
{
var_.at(index_).to(value);
++index_;
}
catch (const error::Conversion& e)
{
std::ostringstream oss;
oss << e.what() << ", for array index " << index_;
throw error::Conversion(oss.str());
}
catch (const error::Access&)
{
std::ostringstream oss;
oss << "wamp::error::Conversion: Attemping to access field type "
<< typeNameOf(var_) << "as array";
throw error::Conversion(oss.str());
}
catch (const std::out_of_range&)
{
std::ostringstream oss;
oss << "wamp::error::Conversion: Cannot extract more than " << index_
<< " elements from the array";
throw error::Conversion(oss.str());
}

return *this;
}

Expand All @@ -137,29 +162,77 @@ FromVariantConverter& FromVariantConverter::operator[](T& value)
@pre The variant is an object.
@pre There exists a member with the given key.
@pre The member is convertible to the destination type.
@throws error::Access if the variant is not an object.
@throws std::out_of_range if there doesn't exist a member with the
@throws error::Conversion if the variant is not an object.
@throws error::Conversion if there doesn't exist a member with the
given key.
@throws error::Conversion if the member is not convertible to the
destination type. */
template <typename T>
FromVariantConverter& FromVariantConverter::operator()(const String& key,
T& value)
{
var_.as<Object>().at(key).to(value);
try
{
var_.at(key).to(value);
}
catch (const error::Conversion& e)
{
std::ostringstream oss;
oss << e.what() << ", for object member \"" << key << '"';
throw error::Conversion(oss.str());
}
catch (const error::Access&)
{
std::ostringstream oss;
oss << "wamp::error::Conversion: Attemping to access field type "
<< typeNameOf(var_) << "as object using key \"" << key << '"';
throw error::Conversion(oss.str());
}
catch (const std::out_of_range&)
{
std::ostringstream oss;
oss << "wamp::error::Conversion: Key \"" << key
<< "\" not found in object";
throw error::Conversion(oss.str());
}

return *this;
}

/** @details
The member is converted to the destination type via Variant::to.
@pre The variant is an object.
@pre The member, if it exists, is convertible to the destination type.
@throws error::Conversion if the variant is not an object.
@throws error::Conversion if the existing member is not convertible to the
destination type. */
template <typename T, typename U>
FromVariantConverter& FromVariantConverter::operator()(const String& key,
T& value, U&& fallback)
{
auto& obj = var_.as<Object>();
auto kv = obj.find(key);
if (kv != obj.end())
kv->second.to(value);
else
value = std::forward<U>(fallback);
try
{
auto& obj = var_.as<Object>();
auto kv = obj.find(key);
if (kv != obj.end())
kv->second.to(value);
else
value = std::forward<U>(fallback);
}
catch (const error::Conversion& e)
{
std::ostringstream oss;
oss << e.what() << ", for object member \"" << key << '"';
throw error::Conversion(oss.str());
}
catch (const error::Access&)
{
std::ostringstream oss;
oss << "wamp::error::Conversion: Attemping to access field type "
<< typeNameOf(var_) << "as object using key \"" << key << '"';
throw error::Conversion(oss.str());
}

return *this;
}

Expand All @@ -181,7 +254,7 @@ template <typename TObject>
void ConversionAccess::convertFrom(FromVariantConverter& c, TObject& obj)
{
static_assert(has_member_convertFrom<TObject>(),
"The 'convertFrom' member function has not provided "
"The 'convertFrom' member function has not been provided "
"for this type.");
obj.convertFrom(c);
}
Expand All @@ -190,11 +263,13 @@ template <typename TObject>
void ConversionAccess::convertTo(ToVariantConverter& c, const TObject& obj)
{
static_assert(has_member_convertTo<TObject>(),
"The 'convertTo' member function has not provided "
"for this type.");
"The 'convertTo' member function has not been provided for this type.");
obj.convertTo(c);
}

template <typename TObject>
TObject ConversionAccess::defaultConstruct() {return TObject();}


//------------------------------------------------------------------------------
template <typename TConverter, typename TValue>
Expand Down
26 changes: 16 additions & 10 deletions cppwamp/include/cppwamp/internal/corounpacker.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
------------------------------------------------------------------------------*/

#include <sstream>
#include "varianttraits.hpp"
#include "../peerdata.hpp"

namespace wamp
{

namespace internal
{

//------------------------------------------------------------------------------
struct UnpackCoroError : public Error
{
UnpackCoroError() : Error("wamp.error.invalid_argument") {}
};

//------------------------------------------------------------------------------
template <typename... A>
struct UnpackedCoroArgGetter
Expand All @@ -26,13 +32,13 @@ struct UnpackedCoroArgGetter
{
return args.at(N).to<TargetType>();
}
catch(const error::Conversion&)
catch(const error::Conversion& e)
{
std::ostringstream oss;
oss << "Expected type " << ArgTraits<TargetType>::typeName()
<< " for arg index " << N
<< ", but got type " << typeNameOf(args.at(N));
throw UnpackError(oss.str());
oss << "Type " << typeNameOf(args.at(N))
<< " at arg index " << N
<< " is not convertible to the RPC's target type";
throw UnpackCoroError().withArgs(oss.str(), e.what());
}
}
};
Expand All @@ -54,7 +60,7 @@ void CoroEventUnpacker<S,A...>::operator()(Event&& event)
std::ostringstream oss;
oss << "Expected " << sizeof...(A)
<< " args, but only got " << event.args().size();
throw internal::UnpackError(oss.str());
throw internal::UnpackCoroError().withArgs(oss.str());
}

// Use the integer parameter pack technique shown in
Expand Down Expand Up @@ -99,7 +105,7 @@ void BasicCoroEventUnpacker<S,A...>::operator()(Event&& event)
std::ostringstream oss;
oss << "Expected " << sizeof...(A)
<< " args, but only got " << event.args().size();
throw internal::UnpackError(oss.str());
throw internal::UnpackCoroError().withArgs(oss.str());
}

// Use the integer parameter pack technique shown in
Expand Down Expand Up @@ -146,7 +152,7 @@ Outcome CoroInvocationUnpacker<S,A...>::operator()(Invocation&& inv)
std::ostringstream oss;
oss << "Expected " << sizeof...(A)
<< " args, but only got " << inv.args().size();
throw internal::UnpackError(oss.str());
throw internal::UnpackCoroError().withArgs(oss.str());
}

// Use the integer parameter pack technique shown in
Expand Down Expand Up @@ -221,7 +227,7 @@ Outcome BasicCoroInvocationUnpacker<S,R,A...>::operator()(Invocation&& inv)
std::ostringstream oss;
oss << "Expected " << sizeof...(A)
<< " args, but only got " << inv.args().size();
throw internal::UnpackError(oss.str());
throw internal::UnpackCoroError().withArgs(oss.str());
}

// Use the integer parameter pack technique shown in
Expand Down
Loading

0 comments on commit ef3541c

Please sign in to comment.