Browse Source

Cleaned up includes
Removed references to json::value in json_common.hpp/cpp
Using unique_ptr<char[]> instead of "scope(exit) { delete[] data; }"
Transition to using iostream to write/read data instead of using string directly
Implement cppcheck code guidelines
Moved/combined some common functions

Made libjson-direct for direct json binding instead of normal json structure.

Samuel Jaffe 9 years ago
parent
commit
357524597e

+ 4 - 0
example.json

@@ -0,0 +1,4 @@
+{
+  "t":{"a":1,"b":2},
+  "d":1.5
+}

+ 8 - 13
json.hpp

@@ -6,12 +6,13 @@
 //  Copyright © 2016 Sam Jaffe. All rights reserved.
 //
 
-#ifndef json_hpp
-#define json_hpp
+#pragma once
 
 #include "../variant/variant.hpp"
 #include "json_common.hpp"
 
+#include <map>
+#include <vector>
 #include <utility>
 
 #define JSON_TYPE_LIST \
@@ -24,16 +25,8 @@
   X(bool)
 
 namespace json {
-  namespace {
-    const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (0x7FFFFFFF % 10);
-    const constexpr json::int_jt INT_JT_MAX = json::uint_jt(-1) - 1;
-    const constexpr json::int_jt INT_JT_MIN = json::int_jt(~(json::uint_jt(-1) / 2));
-    const constexpr json::uint_jt INT_JT_OVER = json::uint_jt(INT_JT_MAX) + 1;
-    const constexpr json::uint_jt UINT_JT_MAX = json::uint_jt(0) - 1;
-    //  const constexpr json::uint_jt UINT_JT_MIN = 0;
-  }
-  
   class value;
+  
   namespace parser {
     void parse(value&, char const*);
   }
@@ -98,6 +91,8 @@ namespace json {
     
   };
 #undef JSON_TYPE_LIST
+  namespace parser {
+    void parse(json::value &, char const *);
+    void parse(json::value& json, std::istream & in);
+  }
 }
-
-#endif /* json_hpp */

+ 114 - 2
json.xcodeproj/project.pbxproj

@@ -8,8 +8,21 @@
 
 /* Begin PBXBuildFile section */
 		CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD217D8F1CCAD587007C50C6 /* json_test.cpp */; };
+		CD3C80C61D6A711000ACC795 /* json_binder_discard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD17473E1D4C1DFD000C344B /* json_binder_discard.cpp */; };
+		CD3C80C71D6A711000ACC795 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
+		CD3C80CA1D6A711000ACC795 /* json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.hpp */; };
+		CD3C80CB1D6A711000ACC795 /* json_binder_discard.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD17473F1D4C1DFD000C344B /* json_binder_discard.hpp */; };
+		CD3C80D31D6A728000ACC795 /* json_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
+		CD3C80DB1D6A728A00ACC795 /* json_binder_parser.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7451C5E9BEB0067C2EC /* json_binder_parser.hpp */; };
+		CD3C80DC1D6A728A00ACC795 /* json_tuple_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD472C791CCC1CD80084C8D6 /* json_tuple_binder.hpp */; };
+		CD3C80DD1D6A728A00ACC795 /* json_object_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD472C7A1CCC1D440084C8D6 /* json_object_binder.hpp */; };
+		CD3C80DE1D6A728A00ACC795 /* json_direct_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD472C7E1CCC498C0084C8D6 /* json_direct_binder.hpp */; };
+		CD3C80DF1D6A728A00ACC795 /* json_direct_map_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD472C7B1CCC1DA20084C8D6 /* json_direct_map_binder.hpp */; };
+		CD3C80E01D6A728A00ACC795 /* json_direct_scalar_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD472C7D1CCC1E120084C8D6 /* json_direct_scalar_binder.hpp */; };
+		CD3C80E11D6A728A00ACC795 /* json_direct_vector_binder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD472C7C1CCC1DDF0084C8D6 /* json_direct_vector_binder.hpp */; };
+		CD3C80E41D6A731D00ACC795 /* libjson-direct.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */; };
+		CD3C80E51D6A731D00ACC795 /* libjson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB2F7331C5D47F70067C2EC /* libjson.dylib */; };
 		CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
-		CD472C771CCC1ABD0084C8D6 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
 		CD472C801CCDA4B00084C8D6 /* json_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */; };
 		CDB2F7431C5D48090067C2EC /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
 		CDB2F7441C5D48090067C2EC /* json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.hpp */; };
@@ -28,8 +41,12 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		CD17473E1D4C1DFD000C344B /* json_binder_discard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_binder_discard.cpp; sourceTree = "<group>"; };
+		CD17473F1D4C1DFD000C344B /* json_binder_discard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = json_binder_discard.hpp; sourceTree = "<group>"; };
+		CD1747431D4C216B000C344B /* example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = example.json; sourceTree = "<group>"; };
 		CD217D8F1CCAD587007C50C6 /* json_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_test.cpp; sourceTree = "<group>"; };
 		CD217D921CCAD885007C50C6 /* json_common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_common.hpp; sourceTree = "<group>"; };
+		CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjson-direct.dylib"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD472C751CCC1ABD0084C8D6 /* json_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_common.cpp; sourceTree = "<group>"; };
 		CD472C791CCC1CD80084C8D6 /* json_tuple_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_tuple_binder.hpp; sourceTree = "<group>"; };
 		CD472C7A1CCC1D440084C8D6 /* json_object_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_object_binder.hpp; sourceTree = "<group>"; };
@@ -47,6 +64,13 @@
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		CD3C80C81D6A711000ACC795 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDB2F7301C5D47F70067C2EC /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -58,6 +82,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD3C80E41D6A731D00ACC795 /* libjson-direct.dylib in Frameworks */,
+				CD3C80E51D6A731D00ACC795 /* libjson.dylib in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -67,6 +93,8 @@
 		CD472C781CCC1CB00084C8D6 /* impl */ = {
 			isa = PBXGroup;
 			children = (
+				CD17473E1D4C1DFD000C344B /* json_binder_discard.cpp */,
+				CD17473F1D4C1DFD000C344B /* json_binder_discard.hpp */,
 				CDB2F7451C5E9BEB0067C2EC /* json_binder_parser.hpp */,
 				CD472C791CCC1CD80084C8D6 /* json_tuple_binder.hpp */,
 				CD472C7A1CCC1D440084C8D6 /* json_object_binder.hpp */,
@@ -101,6 +129,7 @@
 		CDB2F72A1C5D47F70067C2EC = {
 			isa = PBXGroup;
 			children = (
+				CD1747431D4C216B000C344B /* example.json */,
 				CD217D8F1CCAD587007C50C6 /* json_test.cpp */,
 				CD217D921CCAD885007C50C6 /* json_common.hpp */,
 				CD472C751CCC1ABD0084C8D6 /* json_common.cpp */,
@@ -115,6 +144,7 @@
 			children = (
 				CDB2F7331C5D47F70067C2EC /* libjson.dylib */,
 				CDF643321C6E9A8B0016A475 /* json-test */,
+				CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -122,6 +152,23 @@
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
+		CD3C80C91D6A711000ACC795 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD3C80DD1D6A728A00ACC795 /* json_object_binder.hpp in Headers */,
+				CD3C80E01D6A728A00ACC795 /* json_direct_scalar_binder.hpp in Headers */,
+				CD3C80CA1D6A711000ACC795 /* json.hpp in Headers */,
+				CD3C80DE1D6A728A00ACC795 /* json_direct_binder.hpp in Headers */,
+				CD3C80DB1D6A728A00ACC795 /* json_binder_parser.hpp in Headers */,
+				CD3C80DC1D6A728A00ACC795 /* json_tuple_binder.hpp in Headers */,
+				CD3C80E11D6A728A00ACC795 /* json_direct_vector_binder.hpp in Headers */,
+				CD3C80DF1D6A728A00ACC795 /* json_direct_map_binder.hpp in Headers */,
+				CD3C80D31D6A728000ACC795 /* json_binder.hpp in Headers */,
+				CD3C80CB1D6A711000ACC795 /* json_binder_discard.hpp in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDB2F7311C5D47F70067C2EC /* Headers */ = {
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
@@ -133,6 +180,23 @@
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
+		CD3C80C21D6A711000ACC795 /* json-direct */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CD3C80CC1D6A711000ACC795 /* Build configuration list for PBXNativeTarget "json-direct" */;
+			buildPhases = (
+				CD3C80C31D6A711000ACC795 /* Sources */,
+				CD3C80C81D6A711000ACC795 /* Frameworks */,
+				CD3C80C91D6A711000ACC795 /* Headers */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "json-direct";
+			productName = json;
+			productReference = CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */;
+			productType = "com.apple.product-type.library.dynamic";
+		};
 		CDB2F7321C5D47F70067C2EC /* json */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */;
@@ -197,12 +261,22 @@
 			projectRoot = "";
 			targets = (
 				CDB2F7321C5D47F70067C2EC /* json */,
+				CD3C80C21D6A711000ACC795 /* json-direct */,
 				CDF643311C6E9A8B0016A475 /* json-test */,
 			);
 		};
 /* End PBXProject section */
 
 /* Begin PBXSourcesBuildPhase section */
+		CD3C80C31D6A711000ACC795 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD3C80C61D6A711000ACC795 /* json_binder_discard.cpp in Sources */,
+				CD3C80C71D6A711000ACC795 /* json_common.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDB2F72F1C5D47F70067C2EC /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -218,13 +292,38 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */,
-				CD472C771CCC1ABD0084C8D6 /* json_common.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
+		CD3C80CD1D6A711000ACC795 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				EXECUTABLE_PREFIX = lib;
+				GCC_ENABLE_CPP_EXCEPTIONS = YES;
+				GCC_ENABLE_CPP_RTTI = YES;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CD3C80CE1D6A711000ACC795 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				EXECUTABLE_PREFIX = lib;
+				GCC_ENABLE_CPP_EXCEPTIONS = YES;
+				GCC_ENABLE_CPP_RTTI = YES;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 		CDB2F73C1C5D47F70067C2EC /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -333,6 +432,8 @@
 		CDF643361C6E9A8B0016A475 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				GCC_WARN_PEDANTIC = YES;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
 			name = Debug;
@@ -340,6 +441,8 @@
 		CDF643371C6E9A8B0016A475 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				GCC_WARN_PEDANTIC = YES;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
 			name = Release;
@@ -347,6 +450,15 @@
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
+		CD3C80CC1D6A711000ACC795 /* Build configuration list for PBXNativeTarget "json-direct" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD3C80CD1D6A711000ACC795 /* Debug */,
+				CD3C80CE1D6A711000ACC795 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		CDB2F72E1C5D47F70067C2EC /* Build configuration list for PBXProject "json" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

+ 12 - 2
json.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -4,19 +4,29 @@
 <dict>
 	<key>SchemeUserState</key>
 	<dict>
+		<key>json-direct.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>21</integer>
+		</dict>
 		<key>json-test.xcscheme</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>15</integer>
+			<integer>13</integer>
 		</dict>
 		<key>json.xcscheme</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>14</integer>
+			<integer>12</integer>
 		</dict>
 	</dict>
 	<key>SuppressBuildableAutocreation</key>
 	<dict>
+		<key>CD3C80C21D6A711000ACC795</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
 		<key>CDB2F7321C5D47F70067C2EC</key>
 		<dict>
 			<key>primary</key>

+ 69 - 0
json/json_binder_discard.cpp

@@ -0,0 +1,69 @@
+//
+//  json_binder_discard.cpp
+//  json
+//
+//  Created by Sam Jaffe on 7/29/16.
+//
+
+#include "json_common.hpp"
+
+#include "json_binder_discard.hpp"
+
+namespace json { namespace {
+  struct discard_t {
+    template <typename T> discard_t & operator= (T const &) { return *this; }
+    template <typename T> discard_t & operator[](T const &) { return *this; }
+  };
+  
+  void parse_object(discard_t& json, char const*& data);
+  void parse_array(discard_t& json, char const*& data);
+  void parse_one_token(discard_t& json, char const*& data);
+  
+  void parse_one_token(discard_t& json, char const*& data) {
+    const char ch = helper::get_next_element(data);
+    if (ch == '{') {
+      parse_object(json, ++data);
+    } else if (ch == '[') {
+      parse_array(json, ++data);
+    } else if (ch == '"') {
+      helper::parse_string(json, ++data);
+    } else if (!strncmp(data, "true", 4)) {
+      json = true;
+    } else if (!strncmp(data, "false", 5)) {
+      json = false;
+    } else {
+      helper::parse_numeric(json, data);
+    }
+  }
+  
+  void parse_object(discard_t& json, char const*& data) {
+    std::string key;
+    while (*data && *data != '}') {
+      helper::parse_string(key, data);
+      if (helper::get_next_element(data) != ':') {
+        throw malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
+      }
+      parse_one_token(json[key], ++data);
+      helper::advance_to_boundary('}', data);
+    }
+    if (*data) ++data;
+    else throw malformed_json_exception("Reached end of parse string without finding object end");
+  }
+  
+  void parse_array(discard_t& json, char const*& data) {
+    size_t current_idx = 0;
+    while (*data && *data != ']') {
+      parse_one_token(json[current_idx++], data);
+      helper::advance_to_boundary(']', data);
+    }
+    if (*data) ++data;
+    else throw malformed_json_exception("Reached end of parse string without finding array end");
+  }
+} }
+
+namespace json {
+  void parse_discard_token( char const * & data ) {
+    json::discard_t tmp;
+    parse_one_token( tmp, data );
+  }
+}

+ 12 - 0
json/json_binder_discard.hpp

@@ -0,0 +1,12 @@
+//
+//  json_binder_discard.hpp
+//  json
+//
+//  Created by Sam Jaffe on 7/29/16.
+//
+
+#pragma once
+
+namespace json {
+  void parse_discard_token( char const * & data );
+}

+ 16 - 3
json/json_binder_parser.hpp

@@ -10,6 +10,8 @@
 #define json_parser_h
 #pragma once
 
+#include <iostream>
+
 namespace json {
   class value;
   
@@ -19,9 +21,20 @@ namespace json {
     template <typename T, typename S>
     void parse(binder::visitor<T, S>&, char const*);
     
-    template <typename T, typename S, typename V>
-    void parse(binder::visitor<T, S>&& v, V& s) {
-      parse(v, s);
+    template <typename T, typename S>
+    void parse(binder::visitor<T, S>&& v, std::string const& s) {
+      parse(static_cast<binder::visitor<T, S>&>(v), s.c_str());
+    }
+    
+    template <typename T, typename S>
+    void parse(binder::visitor<T, S>&& v, std::istream & in) {
+      if (!in) return;
+      in.seekg(0, std::ios_base::end);
+      size_t end = in.tellg();
+      std::unique_ptr<char[]> data{new char[end]};
+      in.seekg(0);
+      in.read(data.get(), end);
+      parse(static_cast<binder::visitor<T, S>&>(v), data.get());
     }
   }
 }

+ 1 - 1
json/json_direct_binder.hpp

@@ -23,7 +23,7 @@ namespace json { namespace binder {
       impl.parse(val.*ptr, data);
     }
     
-    virtual void write(T const& val, std::string & data) const override {
+    virtual void write(T const& val, std::ostream & data) const override {
       impl.write(val.*ptr, data);
     }
   private:

+ 11 - 7
json/json_direct_map_binder.hpp

@@ -37,16 +37,20 @@ namespace json { namespace binder {
       else throw json::malformed_json_exception("Reached end of parse string without finding object end");
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      data += "{";
+    virtual void write(T const& val, std::ostream & data) const override {
+      data << '{';
       std::map<std::string, V> const & map = val.*ptr;
-      for (typename std::map<std::string, V>::const_iterator it = map.begin(),
-           end = map.end(); it != end; ++it) {
-        data += "\"" + it->first + "\":";
+      typename std::map<std::string, V>::const_iterator it = map.begin(), end = map.end();
+      if (it != end) {
+        data << '"' << it->first << '"' << ':';
         impl.write(it->second, data);
-        data += ",";
+        for (++it; it != end; ++it) {
+          data << ',';
+          data << '"' << it->first << '"' << ':';
+          impl.write(it->second, data);
+        }
       }
-      data.back() = '}';
+      data << '}';
     }
   private:
     std::map<std::string, V> T::*ptr;

+ 18 - 18
json/json_direct_scalar_binder.hpp

@@ -11,7 +11,7 @@ namespace json { namespace binder {
   template <typename T>
   class direct_binder<T, bool> : public binder_impl<T> {
   public:
-    direct_binder(bool T::*p) : ptr(p) {}
+    explicit direct_binder(bool T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data) const override {
@@ -24,8 +24,8 @@ namespace json { namespace binder {
       }
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      data += (val.*ptr ? "true" : "false");
+    virtual void write(T const& val, std::ostream & data) const override {
+      data << (val.*ptr ? "true" : "false");
     }
     
   private:
@@ -35,17 +35,17 @@ namespace json { namespace binder {
   template <typename T>
   class direct_binder<T, int> : public binder_impl<T> {
   public:
-    direct_binder(int T::*p) : ptr(p) {}
+    explicit direct_binder(int T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data) const override {
-      json::helper::parse_integer(val.*ptr, data);
+      json::helper::parse_numeric(val.*ptr, data);
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      char buffer[16] = { '\0' };
-      snprintf(buffer, sizeof(buffer), "%d", val.*ptr);
-      data += buffer;
+    virtual void write(T const& val, std::ostream & data) const override {
+//      char buffer[16] = { '\0' };
+//      snprintf(buffer, sizeof(buffer), "%d", val.*ptr);
+      data << val.*ptr;
     }
   private:
     int T::*ptr;
@@ -54,17 +54,17 @@ namespace json { namespace binder {
   template <typename T>
   class direct_binder<T, double> : public binder_impl<T> {
   public:
-    direct_binder(double T::*p) : ptr(p) {}
+    explicit direct_binder(double T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data) const override {
-      json::helper::parse_double(val.*ptr, data);
+      json::helper::parse_numeric(val.*ptr, data);
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      char buffer[32] = { '\0' };
-      snprintf(buffer, sizeof(buffer), "%lf", val.*ptr);
-      data += buffer;
+    virtual void write(T const& val, std::ostream & data) const override {
+//      char buffer[32] = { '\0' };
+//      snprintf(buffer, sizeof(buffer), "%lf", );
+      data << val.*ptr;
     }
   private:
     double T::*ptr;
@@ -73,15 +73,15 @@ namespace json { namespace binder {
   template <typename T>
   class direct_binder<T, std::string> : public binder_impl<T> {
   public:
-    direct_binder(std::string T::*p) : ptr(p) {}
+    explicit direct_binder(std::string T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data) const override {
       json::helper::parse_string(val.*ptr, data);
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      data += "\"" + val.*ptr + "\"";
+    virtual void write(T const& val, std::ostream & data) const override {
+      data << "\"" << val.*ptr << "\"";
     }
   private:
     std::string T::*ptr;

+ 9 - 6
json/json_direct_vector_binder.hpp

@@ -32,15 +32,18 @@ namespace json { namespace binder {
       else throw json::malformed_json_exception("Reached end of parse string without finding array end");
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      data += "[";
+    virtual void write(T const& val, std::ostream & data) const override {
+      data << '[';
       std::vector<V> const & vec = val.*ptr;
-      for (typename std::vector<V>::const_iterator it = vec.begin(),
-           end = vec.end(); it != end; ++it) {
+      typename std::vector<V>::const_iterator it = vec.begin(), end = vec.end();
+      if (it != end) {
         impl.write(*it, data);
-        data += ",";
+        for (++it; it != end; ++it) {
+          data << ",";
+          impl.write(*it, data);
+        }
       }
-      data.back() = ']';
+      data << ']';
     }
   private:
     std::vector<V> T::*ptr;

+ 19 - 8
json/json_object_binder.hpp

@@ -7,6 +7,8 @@
 
 #pragma once
 
+#include "json_binder_discard.hpp"
+
 namespace json { namespace binder {
   template <typename T>
   class object_binder : public binder_impl<T> {
@@ -33,20 +35,28 @@ namespace json { namespace binder {
       }
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      data += "{";
-      for (typename std::map<std::string, binder<T>>::const_iterator it = mapping.begin(),
-           end = mapping.end(); it != end; ++it) {
-        data += "\"" + it->first + "\":";
+    virtual void write(T const& val, std::ostream & data) const override {
+      data << '{';
+      typename std::map<std::string, binder<T>>::const_iterator it = mapping.begin(),
+      end = mapping.end();
+      if (it != end) {
+        data << '"' << it->first << '"' << ':';
         it->second.write(val, data);
-        data += ",";
+        for (++it; it != end; ++it) {
+          data << ',';
+          data << '"' << it->first << '"' << ':';
+          it->second.write(val, data);
+        }
       }
-      data.back() = '}';
+      data << '}';
     }
     
     void parse_object(T& object, char const*& data) const {
       std::string key;
       while (*data && *data != '}') {
+        if (json::helper::get_next_element(data) != '"') {
+          throw json::malformed_json_exception(std::string("Expected object key starting with '\"', got '") + *data + "' instead");
+        }
         json::helper::parse_string(key, data);
         if (json::helper::get_next_element(data) != ':') {
           throw json::malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
@@ -55,7 +65,8 @@ namespace json { namespace binder {
         if (it != mapping.end()) {
           it->second.parse(object, ++data);
         } else {
-          throw json::malformed_json_exception("Unexpected key " + key);
+          parse_discard_token(++data);
+//          throw json::malformed_json_exception("Unexpected key " + key);
         }
         json::helper::advance_to_boundary('}', data);
       }

+ 10 - 6
json/json_tuple_binder.hpp

@@ -27,14 +27,18 @@ namespace json { namespace binder {
       }
     }
     
-    virtual void write(T const& val, std::string & data) const override {
-      data += "[";
-      for (typename std::vector<binder<T>>::const_iterator it = members.begin(),
-           end = members.end(); it != end; ++it) {
+    virtual void write(T const& val, std::ostream & data) const override {
+      data << '[';
+      typename std::vector<binder<T>>::const_iterator it = members.begin(),
+      end = members.end();
+      if (it != end) {
         it->write(val, data);
-        data += ",";
+        for (++it; it != end; ++it) {
+          data << ',';
+          it->write(val, data);
+        }
       }
-      data.back() = ']';
+      data << ']';
     }
     
     void parse_tuple(T& object, char const*& data) const {

+ 14 - 6
json_binder.hpp

@@ -14,6 +14,7 @@
 
 #include <map>
 #include <string>
+#include <sstream>
 #include <vector>
 
 namespace json {
@@ -24,7 +25,7 @@ namespace json {
       virtual binder_impl<T>* clone() const = 0;
       virtual ~binder_impl() {}
       virtual void parse(T&, char const*&) const = 0;
-      virtual void write(T const&, std::string &) const = 0;
+      virtual void write(T const&, std::ostream &) const = 0;
     };
     
     template <typename T>
@@ -38,11 +39,11 @@ namespace json {
       impl(other.impl->clone()) {
       }
       
-      binder(binder_impl<T> const* p) :
+      explicit binder(binder_impl<T> const* p) :
       impl(p) {
       }
       
-      binder(binder_impl<T> const& r) :
+      explicit binder(binder_impl<T> const& r) :
       impl(r.clone()) {
       }
       
@@ -58,7 +59,7 @@ namespace json {
         impl->parse(object, data);
       }
       
-      void write(T const& object, std::string & data) const {
+      void write(T const& object, std::ostream & data) const {
         if (!impl) return;
         impl->write(object, data);
       }
@@ -75,7 +76,7 @@ namespace json {
         b.parse(obj, data);
       }
       
-      void write(std::string & data) const {
+      void write(std::ostream & data) const {
         b.write(obj, data);
       }
       
@@ -95,10 +96,17 @@ namespace json {
     void parse(binder::visitor<T>& visitor, char const* data) {
       visitor.parse(data);
     }
+        
+    template <typename T, typename S>
+    void write(binder::visitor<T, S> const & visitor, std::ostream & out) {
+      visitor.write(out);
+    }
     
     template <typename T, typename S>
     void write(binder::visitor<T, S> const & visitor, std::string & data) {
-      visitor.write(data);
+      std::stringstream ss;
+      visitor.write(ss);
+      data = ss.str();
     }
   }
 }

+ 8 - 0
json_common.cpp

@@ -18,6 +18,14 @@ namespace json {
         return str;
       }
     }
+    
+    char const * get_numeric_token_end(char const * start) {
+      while (strchr(",]}", *start) == NULL &&
+             !isspace(*start)) {
+        ++start;
+      }
+      return start;
+    }
 
     const char get_next_element(char const*& data) {
       while (isspace(*data)) ++data;

+ 60 - 31
json_common.hpp

@@ -7,36 +7,39 @@
 
 #pragma once
 
-#include "../variant/variant.hpp"
-
+#include <cstdint>
 #include <cstdlib>
-#include <iostream>
-#include <map>
 #include <stdexcept>
 #include <string>
-#include <vector>
 
 namespace json {
-  
   using string_jt = std::string;
   using double_jt = double;
   using int_jt = int32_t;
   using uint_jt = uint32_t;
   using bool_jt = bool;
   
-  using numeric_jt = variant<int_jt, uint_jt, double_jt>;
-  
   class value;
-
+  
   class malformed_json_exception :
   public std::domain_error {
     using std::domain_error::domain_error;
   };
+  
+  namespace {
+    const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (0x7FFFFFFF % 10);
+    const constexpr json::int_jt INT_JT_MAX = json::uint_jt(-1) - 1;
+    const constexpr json::int_jt INT_JT_MIN = json::int_jt(~(json::uint_jt(-1) / 2));
+    const constexpr json::uint_jt INT_JT_OVER = json::uint_jt(INT_JT_MAX) + 1;
+    const constexpr json::uint_jt UINT_JT_MAX = json::uint_jt(0) - 1;
+    //  const constexpr json::uint_jt UINT_JT_MIN = 0;
+  }
 }
 
 namespace json {
   namespace helper {
     const char get_next_element(char const*& data);
+    char const * get_numeric_token_end(char const * start);
     
     /**
      * @throws json::malformed_json_exception
@@ -56,29 +59,55 @@ namespace json {
     template <typename T>
     void parse_double(T& json, char const * & data) {
       json = atof(data);
-    }
+      while (isdigit(*data)) { ++data; }
+      if (*data == '.' || *data == 'e') { ++data; }
+      while (isdigit(*data)) { ++data; }
+   }
     
-    template <typename T>
-    void parse_integer(T& json, char const * & data) {
-      json = atoi(data);
-    }
-    
-  }
-  
-  namespace parser {
-    template <typename T>
-    void parse(T& json, std::string const& str) {
-      parse(json, str.c_str());
-    }
+//    template <typename T>
+//    void parse_integer(T& json, char const * & data) {
+//      json = atoi(data);
+//      while (isdigit(*data)) { ++data; }
+//    }
     
-    template <typename T>
-    void parse(T& json, std::istream & in) {
-      in.seekg(0, std::ios_base::end);
-      size_t end = in.tellg();
-      char data[end];
-      in.seekg(0);
-      in.read(data, end);
-      parse(json, data);
+    template <typename J>
+    void parse_numeric(J & json, char const * & data) {
+      char const * start = data;
+      char const * const end = get_numeric_token_end(start);
+      
+      if (end == start) {
+        throw malformed_json_exception("Expected any token, got nothing");
+      }
+      
+      bool const negative = *start == '-';
+      if (negative) ++start;
+      
+      uint_jt const threshold = (UINT_JT_MAX / 10);
+      uint_jt val = 0;
+      for (; start != end; ++start) {
+        if (!isdigit(*start)) {
+          helper::parse_double(json, data);
+          return;
+        }
+        int_jt digit = static_cast<int_jt>(*start - '0');
+        if (val >= threshold) {
+          if (val > threshold || (start + 1) < end || digit > INT_JT_MAX_LAST_DIGIT) {
+            helper::parse_double(json, data);
+            return;
+          }
+        }
+        val = (10 * val) + digit;
+      }
+      if (negative && val == INT_JT_OVER) {
+        json = INT_JT_MIN;
+      } else if (negative) {
+        json = -int_jt(val);
+      } else if (val <= uint_jt(INT_JT_MAX)) {
+        json = int_jt(val);
+      } else {
+        json = val;
+      }
+      data = start;
     }
-  }
+  }  
 }

+ 19 - 54
json_parser.cpp

@@ -5,11 +5,13 @@
 //  Created by Sam Jaffe on 4/24/16.
 //
 
+#include <iostream>
+
 #include "json.hpp"
 
+#include "json_common.hpp"
+
 namespace json { namespace {
-  char const * get_numeric_token_end(char const * start);
-  void parse_numeric(value& json, char const*& data);
   void parse_object(value& json, char const*& data);
   void parse_array(value& json, char const*& data);
   void parse_one_token(value& json, char const*& data);
@@ -27,56 +29,10 @@ namespace json { namespace {
     } else if (!strncmp(data, "false", 5)) {
       json = false;
     } else {
-      parse_numeric(json, data);
-    }
-  }
-  
-  char const * get_numeric_token_end(char const * start) {
-    while (strchr(",]}", *start) == NULL &&
-           !isspace(*start)) {
-      ++start;
+      helper::parse_numeric(json, data);
     }
-    return start;
   }
-  
-  void parse_numeric(value & json, char const * & data) {
-    char const * start = data;
-    char const * const end = get_numeric_token_end(start);
-    
-    if (end == start) {
-      throw malformed_json_exception("Expected any token, got nothing");
-    }
-    
-    bool const negative = *start == '-';
-    if (negative) ++start;
-    
-    uint_jt const threshold = (UINT_JT_MAX / 10);
-    uint_jt val = 0;
-    for (; start != end; ++start) {
-      if (!isdigit(*start)) {
-        helper::parse_double(json, start);
-        return;
-      }
-      int_jt digit = static_cast<int_jt>(*start - '\0');
-      if (val >= threshold) {
-        if (val > threshold || (start + 1) < end || digit > INT_JT_MAX_LAST_DIGIT) {
-          helper::parse_double(json, start);
-          return;
-        }
-      }
-      val = (10 * val) + digit;
-    }
-    if (negative && val == INT_JT_OVER) {
-      json = INT_JT_MIN;
-    } else if (negative) {
-      json = -int_jt(val);
-    } else if (val <= uint_jt(INT_JT_MAX)) {
-      json = int_jt(val);
-    } else {
-      json = val;
-    }
-  }
-  
+      
   void parse_object(value& json, char const*& data) {
     std::string key;
     while (*data && *data != '}') {
@@ -104,21 +60,30 @@ namespace json { namespace {
 
 namespace json {
   namespace parser {
-    void parse(value& json, char const* data) {
+    void parse(json::value& json, char const* data) {
       parse_one_token(json, data);
       if (*data) throw malformed_json_exception("Expected a single json token in top-level parse");
     }
+    
+    void parse(json::value& json, std::istream & in) {
+      in.seekg(0, std::ios_base::end);
+      size_t end = in.tellg();
+      std::unique_ptr<char[]> data{new char[end]};
+      in.seekg(0);
+      in.read(data.get(), end);
+      parse(json, data.get());
+    }
   }
 }
 
 void json::value::parse(char const* data) {
-  parser::parse(*this, data);
+  json::parser::parse(*this, data);
 }
 
 void json::value::parse(std::string const& str) {
-  parser::parse(*this, str);
+  json::parser::parse(*this, str.c_str());
 }
 
 void json::value::parse(std::istream & in) {
-  parser::parse(*this, in);
+  json::parser::parse(*this, in);
 }

+ 7 - 4
json_test.cpp

@@ -7,6 +7,8 @@
 
 #include "json_binder.hpp"
 
+#include <fstream>
+
 struct test_t {
   int a, b;
 };
@@ -29,18 +31,19 @@ int main(int argc, char const** argv) {
   ("t", &test_2_t::t, bind1)("d", &test_2_t::d);
   
   {
-    test_t out;
-    std::string data = "{\"a\":1,\"b\":2}";
+    test_t out{};
+    std::string data = "{\"a\":1,\"b\":2,\"c\":3}";
     
     json::parser::parse(json::binder::bind(out, bind1), data);
     std::cout << out.a << ',' << out.b << std::endl;
   }
   {
-    test_2_t out;
+    test_2_t out{};
     std::string data = "{\"t\":{\"a\":1,\"b\":2},\"d\":1.5}";
     std::cout << data << std::endl;
 
-    json::parser::parse(json::binder::bind(out, bind2), data);
+    std::ifstream file("/Users/samjaffe/Documents/Programming/XTools/misc/json/example.json");
+    json::parser::parse(json::binder::bind(out, bind2), file);
     std::cout << '{' << out.t.a << ',' << out.t.b << "}," << out.d << std::endl;
     
     std::string sout;