Only automatically convert json objects to other types when they are uninitialized

Originally committed to SVN as r6005.
This commit is contained in:
Thomas Goyne 2011-12-22 21:10:22 +00:00
parent a78417177a
commit 3097dc634e
6 changed files with 130 additions and 181 deletions

View file

@ -14,32 +14,40 @@ Author: Terry Caton
#include <cassert>
#endif
namespace {
using namespace json;
class CastVisitorBase : public Visitor, public ConstVisitor {
void Visit(Array&) { }
void Visit(Object&) { }
void Visit(Number&) { }
void Visit(String&) { }
void Visit(Boolean&) { }
void Visit(Null&) { is_null = true; }
void Visit(Array const&) { }
void Visit(Object const&) { }
void Visit(Number const&) { }
void Visit(String const&) { }
void Visit(Boolean const&) { }
void Visit(Null const&) { is_null = true; }
public:
bool is_null;
CastVisitorBase() : is_null(false) { }
};
template<class T>
struct CastVisitor : public CastVisitorBase {
T *element;
CastVisitor() : element(0) { }
void Visit(T& ele) { element = &ele; }
};
}
namespace json
{
/////////////////////////
// UnknownElement members
class CastVisitor : public ConstVisitor
{
virtual void Visit(const Array&) { }
virtual void Visit(const Object&) { }
virtual void Visit(const Number&) { }
virtual void Visit(const String&) { }
virtual void Visit(const Boolean&) { }
virtual void Visit(const Null&) { }
};
template <typename ElementTypeT>
class CastVisitor_T : public CastVisitor
{
public:
const ElementTypeT *element;
CastVisitor_T() : element(0) { }
// we don't know what this is, but it overrides one of the base's no-op functions
void Visit(const ElementTypeT& element) { this->element = &element; }
};
class UnknownElement::Imp
{
public:
@ -57,15 +65,15 @@ template <typename ElementTypeT>
class UnknownElement::Imp_T : public UnknownElement::Imp
{
public:
Imp_T(const ElementTypeT& element) : m_Element(element) {}
virtual Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
Imp_T(const ElementTypeT& element) : m_Element(element) { }
Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
virtual void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
virtual void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
virtual bool Compare(const Imp& imp) const
bool Compare(const Imp& imp) const
{
CastVisitor_T<ElementTypeT> castVisitor;
CastVisitor<const ElementTypeT> castVisitor;
imp.Accept(castVisitor);
return castVisitor.element && m_Element == *castVisitor.element;
}
@ -88,31 +96,30 @@ UnknownElement::UnknownElement(const Null& null) : m_pImp( new Imp
UnknownElement::~UnknownElement() { delete m_pImp; }
UnknownElement::operator const Object& () const { return CastTo<Object>(); }
UnknownElement::operator const Array& () const { return CastTo<Array>(); }
UnknownElement::operator const Number& () const { return CastTo<Number>(); }
UnknownElement::operator const Boolean& () const { return CastTo<Boolean>(); }
UnknownElement::operator const String& () const { return CastTo<String>(); }
UnknownElement::operator const Null& () const { return CastTo<Null>(); }
UnknownElement::operator Object const&() const { return CastTo<Object>(); }
UnknownElement::operator Array const&() const { return CastTo<Array>(); }
UnknownElement::operator Number const&() const { return CastTo<Number>(); }
UnknownElement::operator Boolean const&() const { return CastTo<Boolean>(); }
UnknownElement::operator String const&() const { return CastTo<String>(); }
UnknownElement::operator Null const&() const { return CastTo<Null>(); }
UnknownElement::operator Object& () { return ConvertTo<Object>(); }
UnknownElement::operator Array& () { return ConvertTo<Array>(); }
UnknownElement::operator Number& () { return ConvertTo<Number>(); }
UnknownElement::operator Boolean& () { return ConvertTo<Boolean>(); }
UnknownElement::operator String& () { return ConvertTo<String>(); }
UnknownElement::operator Null& () { return ConvertTo<Null>(); }
UnknownElement::operator Object&() { return CastTo<Object>(); }
UnknownElement::operator Array&() { return CastTo<Array>(); }
UnknownElement::operator Number&() { return CastTo<Number>(); }
UnknownElement::operator Boolean&() { return CastTo<Boolean>(); }
UnknownElement::operator String&() { return CastTo<String>(); }
UnknownElement::operator Null&() { return CastTo<Null>(); }
UnknownElement& UnknownElement::operator = (const UnknownElement& unknown)
UnknownElement& UnknownElement::operator =(const UnknownElement& unknown)
{
delete m_pImp;
m_pImp = unknown.m_pImp->Clone();
return *this;
}
UnknownElement& UnknownElement::operator[] (const std::string& key)
UnknownElement& UnknownElement::operator[](const std::string& key)
{
// the people want an object. make us one if we aren't already
return ConvertTo<Object>()[key];
return CastTo<Object>()[key];
}
const UnknownElement& UnknownElement::operator[] (const std::string& key) const
@ -125,23 +132,14 @@ const UnknownElement& UnknownElement::operator[] (const std::string& key) const
return it->second;
}
UnknownElement& UnknownElement::operator[] (size_t index)
{
// the people want an array. make us one if we aren't already
return ConvertTo<Array>()[index];
}
const UnknownElement& UnknownElement::operator[] (size_t index) const
{
// throws if we aren't an array
return CastTo<Array>()[index];
}
UnknownElement& UnknownElement::operator[](size_t index) { return CastTo<Array>()[index]; }
UnknownElement const& UnknownElement::operator[](size_t index) const { return CastTo<Array>()[index]; }
template <typename ElementTypeT>
const ElementTypeT& UnknownElement::CastTo() const
ElementTypeT const& UnknownElement::CastTo() const
{
CastVisitor_T<ElementTypeT> castVisitor;
CastVisitor<const ElementTypeT> castVisitor;
m_pImp->Accept(castVisitor);
if (!castVisitor.element)
throw Exception("Bad cast");
@ -149,17 +147,21 @@ const ElementTypeT& UnknownElement::CastTo() const
}
template <typename ElementTypeT>
ElementTypeT& UnknownElement::ConvertTo()
ElementTypeT& UnknownElement::CastTo()
{
CastVisitor_T<ElementTypeT> castVisitor;
CastVisitor<ElementTypeT> castVisitor;
Accept(castVisitor);
if (!castVisitor.element)
{
// we're not the right type. fix it & try again
// If this element is uninitialized, implicitly convert it to the desired type
if (castVisitor.is_null) {
*this = ElementTypeT();
return *this;
}
return *this;
// Otherwise throw an exception
if (!castVisitor.element)
throw Exception("Bad cast");
return *castVisitor.element;
}
void UnknownElement::Accept(ConstVisitor& visitor) const { m_pImp->Accept(visitor); }

View file

@ -123,7 +123,7 @@ void Reader::Read_i(ElementTypeT& element, std::istream& istr)
reader.Scan(tokens, inputStream);
TokenStream tokenStream(tokens);
reader.Parse(element, tokenStream);
element = reader.Parse(tokenStream);
if (!tokenStream.EOS())
{
@ -304,131 +304,79 @@ void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream)
}
void Reader::Parse(UnknownElement& element, Reader::TokenStream& tokenStream)
UnknownElement Reader::Parse(Reader::TokenStream& tokenStream)
{
if (tokenStream.EOS()) {
if (tokenStream.EOS())
throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
}
const Token& token = tokenStream.Peek();
Token const& token = tokenStream.Peek();
switch (token.nType) {
case Token::TOKEN_OBJECT_BEGIN:
{
// implicit non-const cast will perform conversion for us (if necessary)
Object& object = element;
Parse(object, tokenStream);
break;
}
case Token::TOKEN_ARRAY_BEGIN:
{
Array& array = element;
Parse(array, tokenStream);
break;
}
case Token::TOKEN_STRING:
{
String& string = element;
Parse(string, tokenStream);
break;
}
case Token::TOKEN_NUMBER:
{
Number& number = element;
Parse(number, tokenStream);
break;
}
case Token::TOKEN_BOOLEAN:
{
Boolean& boolean = element;
Parse(boolean, tokenStream);
break;
}
case Token::TOKEN_NULL:
{
Null& null = element;
Parse(null, tokenStream);
break;
}
case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
case Token::TOKEN_ARRAY_BEGIN: return ParseArray(tokenStream);
case Token::TOKEN_STRING: return ParseString(tokenStream);
case Token::TOKEN_NUMBER: return ParseNumber(tokenStream);
case Token::TOKEN_BOOLEAN: return ParseBoolean(tokenStream);
case Token::TOKEN_NULL: return ParseNull(tokenStream);
default:
{
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
}
}
}
void Reader::Parse(Object& object, Reader::TokenStream& tokenStream)
Object Reader::ParseObject(Reader::TokenStream& tokenStream)
{
MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
bool bContinue = (tokenStream.EOS() == false &&
tokenStream.Peek().nType != Token::TOKEN_OBJECT_END);
while (bContinue)
Object object;
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
{
// first the member name. save the token in case we have to throw an exception
const Token& tokenName = tokenStream.Peek();
std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
if (object.count(name))
{
throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
}
// ...then the key/value separator...
MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
// ...then the value itself (can be anything).
UnknownElement value;
Parse(value, tokenStream);
object[name] = Parse(tokenStream);
object[name] = value;
bContinue = (tokenStream.EOS() == false &&
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
if (bContinue)
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
}
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
return object;
}
void Reader::Parse(Array& array, Reader::TokenStream& tokenStream)
Array Reader::ParseArray(Reader::TokenStream& tokenStream)
{
MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
bool bContinue = (tokenStream.EOS() == false &&
tokenStream.Peek().nType != Token::TOKEN_ARRAY_END);
while (bContinue)
{
// ...what's next? could be anything
array.push_back(UnknownElement());
UnknownElement& element = array.back();
Parse(element, tokenStream);
Array array;
bContinue = (tokenStream.EOS() == false &&
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
if (bContinue)
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
{
array.push_back(Parse(tokenStream));
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
}
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
return array;
}
void Reader::Parse(String& string, Reader::TokenStream& tokenStream)
String Reader::ParseString(Reader::TokenStream& tokenStream)
{
string = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
}
void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
Number Reader::ParseNumber(Reader::TokenStream& tokenStream)
{
const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
@ -438,25 +386,21 @@ void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
iStr >> dValue;
// did we consume all characters in the token?
if (iStr.eof() == false)
{
if (!iStr.eof())
throw ParseException("Unexpected character in NUMBER token: " + iStr.peek(), currentToken.locBegin, currentToken.locEnd);
}
number = dValue;
return dValue;
}
void Reader::Parse(Boolean& boolean, Reader::TokenStream& tokenStream)
Boolean Reader::ParseBoolean(Reader::TokenStream& tokenStream)
{
const std::string& sValue = MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream);
boolean = (sValue == "true");
return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
}
void Reader::Parse(Null&, Reader::TokenStream& tokenStream)
Null Reader::ParseNull(Reader::TokenStream& tokenStream)
{
MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
return Null();
}

View file

@ -69,7 +69,7 @@ Hotkey::Hotkey(const std::string &file, const std::string &default_config)
LOG_D("hotkey/init") << "Generating hotkeys.";
json::UnknownElement hotkey_root = agi::json_util::file(config_file, default_config);
json::Object object = hotkey_root;
json::Object const& object = hotkey_root;
for (json::Object::const_iterator index(object.begin()); index != object.end(); ++index)
BuildHotkey(index->first, index->second);

View file

@ -93,6 +93,8 @@ LogSink::~LogSink() {
array.push_back(entry);
}
root["timeval"] = json::Object();
json::Array timeval_open;
timeval_open.push_back(time_start.tv_sec);
timeval_open.push_back(time_start.tv_usec);

View file

@ -79,24 +79,24 @@ public:
UnknownElement& operator = (const UnknownElement& unknown);
// implicit cast to actual element type. throws on failure
operator const Object& () const;
operator const Array& () const;
operator const Number& () const;
operator const Boolean& () const;
operator const String& () const;
operator const Null& () const;
// implicit cast to actual element type. *converts* on failure, and always returns success
operator Object& ();
operator Array& ();
operator Number& ();
operator Boolean& ();
operator String& ();
operator Null& ();
operator Object const&() const;
operator Array const&() const;
operator Number const&() const;
operator Boolean const&() const;
operator String const&() const;
operator Null const&() const;
operator Object&();
operator Array&();
operator Number&();
operator Boolean&();
operator String&();
operator Null&();
// provides quick access to children when real element type is object
UnknownElement& operator[] (const char *key) { return operator[](std::string(key)); }
const UnknownElement& operator[] (const char *key) const { return operator[](std::string(key)); }
template<int N>
UnknownElement& operator[] (const char (&key)[N]) { return operator[](std::string(key)); }
template<int N>
const UnknownElement& operator[] (const char (&key)[N]) const { return operator[](std::string(key)); }
UnknownElement& operator[] (const std::string& key);
const UnknownElement& operator[] (const std::string& key) const;
@ -109,7 +109,8 @@ public:
void Accept(Visitor& visitor);
// tests equality. first checks type, then value if possible
bool operator == (const UnknownElement& element) const;
bool operator ==(const UnknownElement& element) const;
bool operator !=(const UnknownElement& element) const { return !(*this == element); }
private:
class Imp;
@ -118,10 +119,10 @@ private:
class Imp_T;
template <typename ElementTypeT>
const ElementTypeT& CastTo() const;
ElementTypeT const& CastTo() const;
template <typename ElementTypeT>
ElementTypeT& ConvertTo();
ElementTypeT& CastTo();
Imp* m_pImp;
};

View file

@ -110,13 +110,13 @@ private:
void MatchExpectedString(const std::string& sExpected, InputStream& inputStream);
// parsing token sequence into element structure
void Parse(UnknownElement& element, TokenStream& tokenStream);
void Parse(Object& object, TokenStream& tokenStream);
void Parse(Array& array, TokenStream& tokenStream);
void Parse(String& string, TokenStream& tokenStream);
void Parse(Number& number, TokenStream& tokenStream);
void Parse(Boolean& boolean, TokenStream& tokenStream);
void Parse(Null& null, TokenStream& tokenStream);
UnknownElement Parse(TokenStream& tokenStream);
Object ParseObject(TokenStream& tokenStream);
Array ParseArray(TokenStream& tokenStream);
String ParseString(TokenStream& tokenStream);
Number ParseNumber(TokenStream& tokenStream);
Boolean ParseBoolean(TokenStream& tokenStream);
Null ParseNull(TokenStream& tokenStream);
const std::string& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
};