Skip to content

Commit ef3541c

Browse files
committed
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. - Blob is now stored via a pointer within the Variant::Field union, to reduce the size of a Variant object.
1 parent 42d5569 commit ef3541c

17 files changed

+505
-195
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
v0.6.2
2+
======
3+
Variant conversion enhancements.
4+
5+
- ConversionAccess can now access private default constructors (closes #98).
6+
- To/FromVariantConverter now throws error::Conversion exclusively.
7+
- All RPC argument conversion failures are now propagated back to caller
8+
(fixes #97).
9+
- Added test cases for bad From/ToVariantConverter conversions.
10+
- Enforced Client::LocalSubs non-empty invariant during unsubscribe.
11+
- Added Variant::at accessors (closes #95).
12+
- Updated config.json test/example files for Crossbar 0.13.0.
13+
114
v0.6.1
215
======
316
Bug fixes.

cppwamp/include/cppwamp/conversion.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ class ConversionAccess
195195
template <typename TObject>
196196
static void convertTo(ToVariantConverter& c, const TObject& obj);
197197

198+
template <typename TObject>
199+
static TObject defaultConstruct();
200+
198201
private:
199202
CPPWAMP_GENERATE_HAS_MEMBER(convert)
200203
CPPWAMP_GENERATE_HAS_MEMBER(convertFrom)

cppwamp/include/cppwamp/internal/client.hpp

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -172,18 +172,21 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
172172
auto kv = readership_.find(sub.id());
173173
if (kv != readership_.end())
174174
{
175-
auto& subMap = kv->second;
176-
if (!subMap.empty())
175+
auto& localSubs = kv->second;
176+
if (!localSubs.empty())
177177
{
178-
auto subKv = subMap.find(sub.slotId({}));
179-
if (subKv != subMap.end())
178+
auto subKv = localSubs.find(sub.slotId({}));
179+
if (subKv != localSubs.end())
180180
{
181-
if (subMap.size() == 1u)
181+
if (localSubs.size() == 1u)
182182
topics_.erase(subKv->second.topic.uri());
183183

184-
subMap.erase(subKv);
185-
if (subMap.empty())
184+
localSubs.erase(subKv);
185+
if (localSubs.empty())
186+
{
187+
readership_.erase(kv);
186188
sendUnsubscribe(sub.id());
189+
}
187190
}
188191
}
189192
}
@@ -196,19 +199,20 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
196199
auto kv = readership_.find(sub.id());
197200
if (kv != readership_.end())
198201
{
199-
auto& subMap = kv->second;
200-
if (!subMap.empty())
202+
auto& localSubs = kv->second;
203+
if (!localSubs.empty())
201204
{
202-
auto subKv = subMap.find(sub.slotId({}));
203-
if (subKv != subMap.end())
205+
auto subKv = localSubs.find(sub.slotId({}));
206+
if (subKv != localSubs.end())
204207
{
205208
unsubscribed = true;
206-
if (subMap.size() == 1u)
209+
if (localSubs.size() == 1u)
207210
topics_.erase(subKv->second.topic.uri());
208211

209-
subMap.erase(subKv);
210-
if (subMap.empty())
212+
localSubs.erase(subKv);
213+
if (localSubs.empty())
211214
{
215+
readership_.erase(kv);
212216
sendUnsubscribe(sub.id(), std::move(handler));
213217
handler = AsyncTask<bool>();
214218
}
@@ -591,13 +595,13 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
591595
{
592596
slot(std::move(event));
593597
}
594-
catch (const internal::UnpackError& e)
598+
catch (const Error& e)
595599
{
596600
if (warningHandler_)
597601
{
598602
std::ostringstream oss;
599603
oss << "Received an EVENT with invalid arguments: "
600-
<< e.reason
604+
<< e.args()
601605
<< " (with subId=" << subId
602606
<< " pubId=" << pubId << ")";
603607
warn(oss.str());
@@ -629,7 +633,8 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
629633
{
630634
this->sendError(WampMsgType::invocation, requestId,
631635
Error("wamp.error.no_such_procedure")
632-
.withArgs("The called procedure does not exist"));
636+
.withArgs("There is no RPC with registration ID " +
637+
std::to_string(regId)));
633638
}
634639
}
635640

@@ -664,13 +669,7 @@ class Client : public ClientInterface, public Peer<TCodec, TTransport>
664669
assert(false && "unexpected Outcome::Type");
665670
}
666671
}
667-
catch (internal::UnpackError e)
668-
{
669-
this->sendError(WampMsgType::invocation, reqId,
670-
Error("wamp.error.invalid_argument")
671-
.withArgs(std::move(e.reason)));
672-
}
673-
catch (Error error)
672+
catch (Error& error)
674673
{
675674
yield(reqId, std::move(error));
676675
}

cppwamp/include/cppwamp/internal/conversion.ipp

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
http://www.boost.org/LICENSE_1_0.txt)
66
------------------------------------------------------------------------------*/
77

8+
#include <sstream>
89
#include <utility>
910
#include "../error.hpp"
1011
#include "../variant.hpp"
@@ -119,16 +120,40 @@ FromVariantConverter& FromVariantConverter::operator()(T& value)
119120
@pre The variant is an array.
120121
@pre There are elements left in the variant array.
121122
@pre The element is convertible to the destination type.
122-
@throws error::Access if the variant is not an array.
123-
@throws std::out_of_range if there are no elements left in the
123+
@throws error::Conversion if the variant is not an array.
124+
@throws error::Conversion if there are no elements left in the
124125
variant array.
125126
@throws error::Conversion if the element is not convertible to the
126127
destination type. */
127128
template <typename T>
128129
FromVariantConverter& FromVariantConverter::operator[](T& value)
129130
{
130-
var_.as<Array>().at(index_).to(value);
131-
++index_;
131+
try
132+
{
133+
var_.at(index_).to(value);
134+
++index_;
135+
}
136+
catch (const error::Conversion& e)
137+
{
138+
std::ostringstream oss;
139+
oss << e.what() << ", for array index " << index_;
140+
throw error::Conversion(oss.str());
141+
}
142+
catch (const error::Access&)
143+
{
144+
std::ostringstream oss;
145+
oss << "wamp::error::Conversion: Attemping to access field type "
146+
<< typeNameOf(var_) << "as array";
147+
throw error::Conversion(oss.str());
148+
}
149+
catch (const std::out_of_range&)
150+
{
151+
std::ostringstream oss;
152+
oss << "wamp::error::Conversion: Cannot extract more than " << index_
153+
<< " elements from the array";
154+
throw error::Conversion(oss.str());
155+
}
156+
132157
return *this;
133158
}
134159

@@ -137,29 +162,77 @@ FromVariantConverter& FromVariantConverter::operator[](T& value)
137162
@pre The variant is an object.
138163
@pre There exists a member with the given key.
139164
@pre The member is convertible to the destination type.
140-
@throws error::Access if the variant is not an object.
141-
@throws std::out_of_range if there doesn't exist a member with the
165+
@throws error::Conversion if the variant is not an object.
166+
@throws error::Conversion if there doesn't exist a member with the
142167
given key.
143168
@throws error::Conversion if the member is not convertible to the
144169
destination type. */
145170
template <typename T>
146171
FromVariantConverter& FromVariantConverter::operator()(const String& key,
147172
T& value)
148173
{
149-
var_.as<Object>().at(key).to(value);
174+
try
175+
{
176+
var_.at(key).to(value);
177+
}
178+
catch (const error::Conversion& e)
179+
{
180+
std::ostringstream oss;
181+
oss << e.what() << ", for object member \"" << key << '"';
182+
throw error::Conversion(oss.str());
183+
}
184+
catch (const error::Access&)
185+
{
186+
std::ostringstream oss;
187+
oss << "wamp::error::Conversion: Attemping to access field type "
188+
<< typeNameOf(var_) << "as object using key \"" << key << '"';
189+
throw error::Conversion(oss.str());
190+
}
191+
catch (const std::out_of_range&)
192+
{
193+
std::ostringstream oss;
194+
oss << "wamp::error::Conversion: Key \"" << key
195+
<< "\" not found in object";
196+
throw error::Conversion(oss.str());
197+
}
198+
150199
return *this;
151200
}
152201

202+
/** @details
203+
The member is converted to the destination type via Variant::to.
204+
@pre The variant is an object.
205+
@pre The member, if it exists, is convertible to the destination type.
206+
@throws error::Conversion if the variant is not an object.
207+
@throws error::Conversion if the existing member is not convertible to the
208+
destination type. */
153209
template <typename T, typename U>
154210
FromVariantConverter& FromVariantConverter::operator()(const String& key,
155211
T& value, U&& fallback)
156212
{
157-
auto& obj = var_.as<Object>();
158-
auto kv = obj.find(key);
159-
if (kv != obj.end())
160-
kv->second.to(value);
161-
else
162-
value = std::forward<U>(fallback);
213+
try
214+
{
215+
auto& obj = var_.as<Object>();
216+
auto kv = obj.find(key);
217+
if (kv != obj.end())
218+
kv->second.to(value);
219+
else
220+
value = std::forward<U>(fallback);
221+
}
222+
catch (const error::Conversion& e)
223+
{
224+
std::ostringstream oss;
225+
oss << e.what() << ", for object member \"" << key << '"';
226+
throw error::Conversion(oss.str());
227+
}
228+
catch (const error::Access&)
229+
{
230+
std::ostringstream oss;
231+
oss << "wamp::error::Conversion: Attemping to access field type "
232+
<< typeNameOf(var_) << "as object using key \"" << key << '"';
233+
throw error::Conversion(oss.str());
234+
}
235+
163236
return *this;
164237
}
165238

@@ -181,7 +254,7 @@ template <typename TObject>
181254
void ConversionAccess::convertFrom(FromVariantConverter& c, TObject& obj)
182255
{
183256
static_assert(has_member_convertFrom<TObject>(),
184-
"The 'convertFrom' member function has not provided "
257+
"The 'convertFrom' member function has not been provided "
185258
"for this type.");
186259
obj.convertFrom(c);
187260
}
@@ -190,11 +263,13 @@ template <typename TObject>
190263
void ConversionAccess::convertTo(ToVariantConverter& c, const TObject& obj)
191264
{
192265
static_assert(has_member_convertTo<TObject>(),
193-
"The 'convertTo' member function has not provided "
194-
"for this type.");
266+
"The 'convertTo' member function has not been provided for this type.");
195267
obj.convertTo(c);
196268
}
197269

270+
template <typename TObject>
271+
TObject ConversionAccess::defaultConstruct() {return TObject();}
272+
198273

199274
//------------------------------------------------------------------------------
200275
template <typename TConverter, typename TValue>

cppwamp/include/cppwamp/internal/corounpacker.ipp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
------------------------------------------------------------------------------*/
77

88
#include <sstream>
9-
#include "varianttraits.hpp"
9+
#include "../peerdata.hpp"
1010

1111
namespace wamp
1212
{
1313

1414
namespace internal
1515
{
1616

17+
//------------------------------------------------------------------------------
18+
struct UnpackCoroError : public Error
19+
{
20+
UnpackCoroError() : Error("wamp.error.invalid_argument") {}
21+
};
22+
1723
//------------------------------------------------------------------------------
1824
template <typename... A>
1925
struct UnpackedCoroArgGetter
@@ -26,13 +32,13 @@ struct UnpackedCoroArgGetter
2632
{
2733
return args.at(N).to<TargetType>();
2834
}
29-
catch(const error::Conversion&)
35+
catch(const error::Conversion& e)
3036
{
3137
std::ostringstream oss;
32-
oss << "Expected type " << ArgTraits<TargetType>::typeName()
33-
<< " for arg index " << N
34-
<< ", but got type " << typeNameOf(args.at(N));
35-
throw UnpackError(oss.str());
38+
oss << "Type " << typeNameOf(args.at(N))
39+
<< " at arg index " << N
40+
<< " is not convertible to the RPC's target type";
41+
throw UnpackCoroError().withArgs(oss.str(), e.what());
3642
}
3743
}
3844
};
@@ -54,7 +60,7 @@ void CoroEventUnpacker<S,A...>::operator()(Event&& event)
5460
std::ostringstream oss;
5561
oss << "Expected " << sizeof...(A)
5662
<< " args, but only got " << event.args().size();
57-
throw internal::UnpackError(oss.str());
63+
throw internal::UnpackCoroError().withArgs(oss.str());
5864
}
5965

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

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

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

227233
// Use the integer parameter pack technique shown in

0 commit comments

Comments
 (0)