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> #include <cassert>
#endif #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 namespace json
{ {
///////////////////////// /////////////////////////
// UnknownElement members // 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 class UnknownElement::Imp
{ {
public: public:
@ -57,15 +65,15 @@ template <typename ElementTypeT>
class UnknownElement::Imp_T : public UnknownElement::Imp class UnknownElement::Imp_T : public UnknownElement::Imp
{ {
public: public:
Imp_T(const ElementTypeT& element) : m_Element(element) {} Imp_T(const ElementTypeT& element) : m_Element(element) { }
virtual Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); } Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
virtual void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); } void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
virtual void Accept(Visitor& visitor) { 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); imp.Accept(castVisitor);
return castVisitor.element && m_Element == *castVisitor.element; 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::~UnknownElement() { delete m_pImp; }
UnknownElement::operator const Object& () const { return CastTo<Object>(); } UnknownElement::operator Object const&() const { return CastTo<Object>(); }
UnknownElement::operator const Array& () const { return CastTo<Array>(); } UnknownElement::operator Array const&() const { return CastTo<Array>(); }
UnknownElement::operator const Number& () const { return CastTo<Number>(); } UnknownElement::operator Number const&() const { return CastTo<Number>(); }
UnknownElement::operator const Boolean& () const { return CastTo<Boolean>(); } UnknownElement::operator Boolean const&() const { return CastTo<Boolean>(); }
UnknownElement::operator const String& () const { return CastTo<String>(); } UnknownElement::operator String const&() const { return CastTo<String>(); }
UnknownElement::operator const Null& () const { return CastTo<Null>(); } UnknownElement::operator Null const&() const { return CastTo<Null>(); }
UnknownElement::operator Object& () { return ConvertTo<Object>(); } UnknownElement::operator Object&() { return CastTo<Object>(); }
UnknownElement::operator Array& () { return ConvertTo<Array>(); } UnknownElement::operator Array&() { return CastTo<Array>(); }
UnknownElement::operator Number& () { return ConvertTo<Number>(); } UnknownElement::operator Number&() { return CastTo<Number>(); }
UnknownElement::operator Boolean& () { return ConvertTo<Boolean>(); } UnknownElement::operator Boolean&() { return CastTo<Boolean>(); }
UnknownElement::operator String& () { return ConvertTo<String>(); } UnknownElement::operator String&() { return CastTo<String>(); }
UnknownElement::operator Null& () { return ConvertTo<Null>(); } UnknownElement::operator Null&() { return CastTo<Null>(); }
UnknownElement& UnknownElement::operator = (const UnknownElement& unknown) UnknownElement& UnknownElement::operator =(const UnknownElement& unknown)
{ {
delete m_pImp; delete m_pImp;
m_pImp = unknown.m_pImp->Clone(); m_pImp = unknown.m_pImp->Clone();
return *this; 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 CastTo<Object>()[key];
return ConvertTo<Object>()[key];
} }
const UnknownElement& UnknownElement::operator[] (const std::string& key) const 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; return it->second;
} }
UnknownElement& UnknownElement::operator[] (size_t index) UnknownElement& UnknownElement::operator[](size_t index) { return CastTo<Array>()[index]; }
{ UnknownElement const& UnknownElement::operator[](size_t index) const { return CastTo<Array>()[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];
}
template <typename ElementTypeT> 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); m_pImp->Accept(castVisitor);
if (!castVisitor.element) if (!castVisitor.element)
throw Exception("Bad cast"); throw Exception("Bad cast");
@ -149,17 +147,21 @@ const ElementTypeT& UnknownElement::CastTo() const
} }
template <typename ElementTypeT> template <typename ElementTypeT>
ElementTypeT& UnknownElement::ConvertTo() ElementTypeT& UnknownElement::CastTo()
{ {
CastVisitor_T<ElementTypeT> castVisitor; CastVisitor<ElementTypeT> castVisitor;
Accept(castVisitor); Accept(castVisitor);
if (!castVisitor.element)
{ // If this element is uninitialized, implicitly convert it to the desired type
// we're not the right type. fix it & try again if (castVisitor.is_null) {
*this = ElementTypeT(); *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); } 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); reader.Scan(tokens, inputStream);
TokenStream tokenStream(tokens); TokenStream tokenStream(tokens);
reader.Parse(element, tokenStream); element = reader.Parse(tokenStream);
if (!tokenStream.EOS()) 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 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) { switch (token.nType) {
case Token::TOKEN_OBJECT_BEGIN: case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
{ case Token::TOKEN_ARRAY_BEGIN: return ParseArray(tokenStream);
// implicit non-const cast will perform conversion for us (if necessary) case Token::TOKEN_STRING: return ParseString(tokenStream);
Object& object = element; case Token::TOKEN_NUMBER: return ParseNumber(tokenStream);
Parse(object, tokenStream); case Token::TOKEN_BOOLEAN: return ParseBoolean(tokenStream);
break; case Token::TOKEN_NULL: return ParseNull(tokenStream);
}
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;
}
default: default:
{
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd); throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
} }
}
} }
Object Reader::ParseObject(Reader::TokenStream& tokenStream)
void Reader::Parse(Object& object, Reader::TokenStream& tokenStream)
{ {
MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream); MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
bool bContinue = (tokenStream.EOS() == false && Object object;
tokenStream.Peek().nType != Token::TOKEN_OBJECT_END);
while (bContinue) 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 // first the member name. save the token in case we have to throw an exception
const Token& tokenName = tokenStream.Peek(); const Token& tokenName = tokenStream.Peek();
std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream); std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
if (object.count(name)) if (object.count(name))
{
throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd); throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
}
// ...then the key/value separator... // ...then the key/value separator...
MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream); MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
// ...then the value itself (can be anything). // ...then the value itself (can be anything).
UnknownElement value; object[name] = Parse(tokenStream);
Parse(value, tokenStream);
object[name] = value; if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
bContinue = (tokenStream.EOS() == false &&
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
if (bContinue)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream); MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
} }
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream); MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
return object;
} }
Array Reader::ParseArray(Reader::TokenStream& tokenStream)
void Reader::Parse(Array& array, Reader::TokenStream& tokenStream)
{ {
MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream); MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
bool bContinue = (tokenStream.EOS() == false && Array array;
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);
bContinue = (tokenStream.EOS() == false && while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT); {
if (bContinue) array.push_back(Parse(tokenStream));
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream); MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
} }
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream); MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
return array;
} }
String Reader::ParseString(Reader::TokenStream& tokenStream)
void Reader::Parse(String& string, Reader::TokenStream& tokenStream)
{ {
string = MatchExpectedToken(Token::TOKEN_STRING, tokenStream); return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
} }
Number Reader::ParseNumber(Reader::TokenStream& tokenStream)
void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
{ {
const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream); const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
@ -438,25 +386,21 @@ void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
iStr >> dValue; iStr >> dValue;
// did we consume all characters in the token? // 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); throw ParseException("Unexpected character in NUMBER token: " + iStr.peek(), currentToken.locBegin, currentToken.locEnd);
}
number = dValue; return dValue;
} }
Boolean Reader::ParseBoolean(Reader::TokenStream& tokenStream)
void Reader::Parse(Boolean& boolean, Reader::TokenStream& tokenStream)
{ {
const std::string& sValue = MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream); return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
boolean = (sValue == "true");
} }
Null Reader::ParseNull(Reader::TokenStream& tokenStream)
void Reader::Parse(Null&, Reader::TokenStream& tokenStream)
{ {
MatchExpectedToken(Token::TOKEN_NULL, 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."; LOG_D("hotkey/init") << "Generating hotkeys.";
json::UnknownElement hotkey_root = agi::json_util::file(config_file, default_config); 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) for (json::Object::const_iterator index(object.begin()); index != object.end(); ++index)
BuildHotkey(index->first, index->second); BuildHotkey(index->first, index->second);

View file

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

View file

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

View file

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