Browse Source

Separated out the various binder implementations to their own files.
De-templated some parts of the common sections, moved into json_common.cpp

Samuel Jaffe 9 years ago
parent
commit
22ca06f921

+ 27 - 0
json.xcodeproj/project.pbxproj

@@ -8,6 +8,8 @@
 
 /* Begin PBXBuildFile section */
 		CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD217D8F1CCAD587007C50C6 /* json_test.cpp */; };
+		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 */; };
 		CDB2F7431C5D48090067C2EC /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
 		CDB2F7441C5D48090067C2EC /* json.h in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.h */; };
 /* End PBXBuildFile section */
@@ -27,6 +29,13 @@
 /* Begin PBXFileReference section */
 		CD217D8F1CCAD587007C50C6 /* json_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_test.cpp; sourceTree = "<group>"; };
 		CD217D921CCAD885007C50C6 /* json_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_common.h; sourceTree = "<group>"; };
+		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>"; };
+		CD472C7B1CCC1DA20084C8D6 /* json_direct_map_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_map_binder.hpp; sourceTree = "<group>"; };
+		CD472C7C1CCC1DDF0084C8D6 /* json_direct_vector_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_vector_binder.hpp; sourceTree = "<group>"; };
+		CD472C7D1CCC1E120084C8D6 /* json_direct_scalar_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_scalar_binder.hpp; sourceTree = "<group>"; };
+		CD472C7E1CCC498C0084C8D6 /* json_direct_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_binder.hpp; sourceTree = "<group>"; };
 		CDB2F7331C5D47F70067C2EC /* libjson.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjson.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDB2F7411C5D48090067C2EC /* json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = json.cpp; sourceTree = "<group>"; tabWidth = 2; };
 		CDB2F7421C5D48090067C2EC /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = "<group>"; tabWidth = 2; };
@@ -53,6 +62,20 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		CD472C781CCC1CB00084C8D6 /* impl */ = {
+			isa = PBXGroup;
+			children = (
+				CD472C791CCC1CD80084C8D6 /* json_tuple_binder.hpp */,
+				CD472C7A1CCC1D440084C8D6 /* json_object_binder.hpp */,
+				CD472C7E1CCC498C0084C8D6 /* json_direct_binder.hpp */,
+				CD472C7B1CCC1DA20084C8D6 /* json_direct_map_binder.hpp */,
+				CD472C7D1CCC1E120084C8D6 /* json_direct_scalar_binder.hpp */,
+				CD472C7C1CCC1DDF0084C8D6 /* json_direct_vector_binder.hpp */,
+			);
+			name = impl;
+			path = json;
+			sourceTree = "<group>";
+		};
 		CDB2F72A1C5D47F70067C2EC = {
 			isa = PBXGroup;
 			children = (
@@ -60,8 +83,10 @@
 				CDB2F7411C5D48090067C2EC /* json.cpp */,
 				CDB2F7421C5D48090067C2EC /* json.h */,
 				CD217D921CCAD885007C50C6 /* json_common.h */,
+				CD472C751CCC1ABD0084C8D6 /* json_common.cpp */,
 				CDB2F7451C5E9BEB0067C2EC /* json_parser.hpp */,
 				CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */,
+				CD472C781CCC1CB00084C8D6 /* impl */,
 				CDB2F7341C5D47F70067C2EC /* Products */,
 			);
 			sourceTree = "<group>";
@@ -164,6 +189,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				CDB2F7431C5D48090067C2EC /* json.cpp in Sources */,
+				CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -172,6 +198,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */,
+				CD472C771CCC1ABD0084C8D6 /* json_common.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 33 - 0
json/json_direct_binder.hpp

@@ -0,0 +1,33 @@
+//
+//  json_direct_binder.hpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#pragma once
+
+namespace json { namespace binder {
+  template <typename T, typename E>
+  class direct_binder : public binder_impl<T> {
+  public:
+    direct_binder(E T::*p, binder<E> const& i) :
+    ptr(p), impl(i) {
+    }
+    
+    virtual binder_impl<T>* clone() const override {
+      return new direct_binder(*this);
+    }
+    
+    virtual void parse(T& val, char const*& data) const override {
+      impl.parse(val.*ptr, data);
+    }
+    
+    virtual void write(T const& val, std::string & data) const override {
+      impl.write(val.*ptr, data);
+    }
+  private:
+    E T::*ptr;
+    binder<E> impl;
+  };
+} }

+ 55 - 0
json/json_direct_map_binder.hpp

@@ -0,0 +1,55 @@
+//
+//  json_direct_map_binder.hpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#pragma once
+
+namespace json { namespace binder {
+  template <typename T, typename V>
+  class direct_binder<T, std::map<std::string, V> > : public binder_impl<T> {
+  public:
+    direct_binder(std::vector<V> T::*p, binder<V> const&i);
+    virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+    
+    virtual void parse(T& val, char const*& data) const override {
+      const char ch = json::helper::get_next_element(data);
+      if (ch != '{') {
+        throw json::malformed_json_exception("Expected an array type");
+      }
+      ++data;
+      
+      V to_make;
+      std::map<std::string, V>& vec = val.*ptr;
+      std::string key;
+      while (*data && *data != '}') {
+        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");
+        }
+        impl.parse(to_make, ++data);
+        vec.emplace(key, to_make);
+        json::helper::advance_to_boundary('}', data);
+      }
+      if (*data) ++data;
+      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 += "{";
+      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 + "\":";
+        impl.write(it->second, data);
+        data += ",";
+      }
+      data.back() = '}';
+    }
+  private:
+    std::map<std::string, V> T::*ptr;
+    binder<V> impl;
+  };
+} }

+ 97 - 0
json/json_direct_scalar_binder.hpp

@@ -0,0 +1,97 @@
+//
+//  json_direct_scalar_binder.hpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#pragma once
+
+namespace json { namespace binder {
+  template <typename T>
+  class direct_binder<T, bool> : public binder_impl<T> {
+  public:
+    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 {
+      if (!strncmp(data, "true", 4)) {
+        val.*ptr = true;
+      } else if (!strncmp(data, "false", 5)) {
+        val.*ptr = false;
+      } else {
+        throw json::malformed_json_exception("Expected a boolean type here");
+      }
+    }
+    
+    virtual void write(T const& val, std::string & data) const override {
+      data += (val.*ptr ? "true" : "false");
+    }
+    
+  private:
+    bool T::*ptr;
+  };
+  
+  template <typename T>
+  class direct_binder<T, int> : public binder_impl<T> {
+  public:
+    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 {
+      //        if (false) {
+      json::helper::parse_numeric(val.*ptr, data);
+      //        } else {
+      //          throw json::malformed_json_exception("Expected an integral type here");
+      //        }
+    }
+    
+    virtual void write(T const& val, std::string & data) const override {
+      char buffer[16] = { '\0' };
+      snprintf(buffer, sizeof(buffer), "%d", val.*ptr);
+      data += buffer;
+    }
+  private:
+    int T::*ptr;
+  };
+  
+  template <typename T>
+  class direct_binder<T, double> : public binder_impl<T> {
+  public:
+    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 {
+      //        if (false) {
+      json::helper::parse_numeric(val.*ptr, data);
+      //        } else {
+      //          throw json::malformed_json_exception("Expected a floating point type here");
+      //        }
+    }
+    
+    virtual void write(T const& val, std::string & data) const override {
+      char buffer[32] = { '\0' };
+      snprintf(buffer, sizeof(buffer), "%lf", val.*ptr);
+      data += buffer;
+    }
+  private:
+    double T::*ptr;
+  };
+  
+  template <typename T>
+  class direct_binder<T, std::string> : public binder_impl<T> {
+  public:
+    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 + "\"";
+    }
+  private:
+    std::string T::*ptr;
+  };
+} }

+ 49 - 0
json/json_direct_vector_binder.hpp

@@ -0,0 +1,49 @@
+//
+//  json_direct_vector_binder.hpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#pragma once
+
+namespace json { namespace binder {
+  template <typename T, typename V>
+  class direct_binder<T, std::vector<V> > : public binder_impl<T> {
+  public:
+    direct_binder(std::vector<V> T::*p, binder<V> const&i);
+    virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
+    
+    virtual void parse(T& val, char const*& data) const override {
+      const char ch = json::helper::get_next_element(data);
+      if (ch != '[') {
+        throw json::malformed_json_exception("Expected an array type");
+      }
+      ++data;
+      
+      V to_make;
+      std::vector<V>& vec = val.*ptr;
+      while (*data && *data != ']') {
+        impl.parse(to_make, data);
+        vec.emplace_back(to_make);
+        json::helper::advance_to_boundary(']', data);
+      }
+      if (*data) ++data;
+      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 += "[";
+      std::vector<V> const & vec = val.*ptr;
+      for (typename std::vector<V>::const_iterator it = vec.begin(),
+           end = vec.end(); it != end; ++it) {
+        impl.write(*it, data);
+        data += ",";
+      }
+      data.back() = ']';
+    }
+  private:
+    std::vector<V> T::*ptr;
+    binder<V> impl;
+  };
+} }

+ 73 - 0
json/json_object_binder.hpp

@@ -0,0 +1,73 @@
+//
+//  json_object_binder.hpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#pragma once
+
+namespace json { namespace binder {
+  template <typename T>
+  class object_binder : public binder_impl<T> {
+  public:
+    object_binder() {}
+    virtual binder_impl<T>* clone() const override { return new object_binder(*this); }
+    
+    template <typename V>
+    object_binder& operator()(std::string const&k, V T::*ptr, binder_impl<V> const&v) {
+      return (*this)(k, binder<T>(new direct_binder<T, V>(ptr, binder<V>(v) )));
+    }
+    
+    object_binder& operator()(std::string const&k, binder<T> const&v) {
+      mapping.emplace(k, v);
+      return *this;
+    }
+    
+    virtual void parse(T& object, char const*& data) const override {
+      const char ch = json::helper::get_next_element(data);
+      if (ch == '{') {
+        parse_object(object, ++data);
+      } else {
+        throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
+      }
+    }
+    
+    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 + "\":";
+        it->second.write(val, data);
+        data += ",";
+      }
+      data.back() = '}';
+    }
+    
+    void parse_object(T& object, char const*& data) const {
+      std::string key;
+      while (*data && *data != '}') {
+        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");
+        }
+        auto it = mapping.find(key);
+        if (it != mapping.end()) {
+          it->second.parse(object, ++data);
+        } else {
+          throw json::malformed_json_exception("Unexpected key " + key);
+        }
+        json::helper::advance_to_boundary('}', data);
+      }
+      if (*data) ++data;
+      else throw json::malformed_json_exception("Reached end of parse string without finding object end");
+    }
+    
+    template <typename E>
+    object_binder& operator()(std::string const& s, E T::*p) {
+      return operator()(s, binder<T>(new direct_binder<T, E>(p)));
+    }
+  private:
+    std::map<std::string, binder<T>> mapping;
+  };
+} }

+ 63 - 0
json/json_tuple_binder.hpp

@@ -0,0 +1,63 @@
+//
+//  json_tuple_binder.hpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#pragma once
+
+namespace json { namespace binder {
+  template <typename T>
+  class tuple_binder : public binder_impl<T> {
+  public:
+    virtual binder_impl<T>* clone() const override { return new tuple_binder(*this); }
+    
+    tuple_binder& operator()(binder<T> const&b) {
+      members.push_back(b);
+      return *this;
+    }
+    
+    virtual void parse(T& object, char const*& data) const override {
+      const char ch = json::helper::get_next_element(data);
+      if (ch == '[') {
+        parse_tuple(object, ++data);
+      } else {
+        throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
+      }
+    }
+    
+    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) {
+        it->write(val, data);
+        data += ",";
+      }
+      data.back() = ']';
+    }
+    
+    void parse_tuple(T& object, char const*& data) const {
+      auto it = members.begin();
+      while (*data && *data != ']' && it != members.end()) {
+        it->parse(object, data);
+        json::helper::advance_to_boundary(']', data);
+        ++it;
+      }
+      if (it != members.end()) {
+        throw json::malformed_json_exception("Failed to parse every member of tuple");
+      }
+      if (*data) ++data;
+      else throw json::malformed_json_exception("Reached end of parse string without finding array end");
+    }
+    
+    template <typename E>
+    tuple_binder& operator()(E T::*p) {
+      return operator()(binder<T>(new direct_binder<T, E>(p)));
+    }
+    
+  private:
+    std::vector<binder<T>> members;
+  };
+
+} }

+ 26 - 308
json_binder.hpp

@@ -30,328 +30,40 @@ namespace json {
     template <typename T>
     class binder {
     public:
-      binder() : impl(nullptr), owned(false) {}
-      binder(binder const& other) : impl(other.impl->clone()), owned(true) {}
-      binder(binder_impl<T> const* p) : impl(p), owned(true) {}
-      binder(binder_impl<T> const& r) : impl(&r), owned(false) {}
-      ~binder() { if (impl && owned) delete impl; }
-      
-      void parse(T& object, char const*& data) const {
-        if (!impl) return;
-        impl->parse(object, data);
-      }
-      
-      void write(T const& object, std::string & data) const {
-        if (!impl) return;
-        impl->write(object, data);
-      }
-    private:
-      binder_impl<T> const* impl;
-      bool owned;
-    };
-    
-    template <typename T, typename E>
-    class direct_binder : public binder_impl<T> {
-    public:
-      direct_binder(E T::*p, binder<E> const& i) : ptr(p), impl(i) {}
-      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
-      
-      virtual void parse(T& val, char const*& data) const override {
-        impl.parse(val.*ptr, data);
-      }
-      
-      virtual void write(T const& val, std::string & data) const override {
-        impl.write(val.*ptr, data);
-      }
-    private:
-      E T::*ptr;
-      binder<E> impl;
-    };
-    
-    template <typename T>
-    class direct_binder<T, bool> : public binder_impl<T> {
-    public:
-      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 {
-        if (!strncmp(data, "true", 4)) {
-          val.*ptr = true;
-        } else if (!strncmp(data, "false", 5)) {
-          val.*ptr = false;
-        } else {
-          throw json::malformed_json_exception("Expected a boolean type here");
-        }
-      }
-      
-      virtual void write(T const& val, std::string & data) const override {
-        data += (val.*ptr ? "true" : "false");
-      }
-      
-    private:
-      bool T::*ptr;
-    };
-    
-    template <typename T>
-    class direct_binder<T, int> : public binder_impl<T> {
-    public:
-      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 {
-//        if (false) {
-        json::helper::parse_numeric(val.*ptr, data);
-//        } else {
-//          throw json::malformed_json_exception("Expected an integral type here");
-//        }
+      binder() :
+      impl(nullptr) {
       }
       
-      virtual void write(T const& val, std::string & data) const override {
-        char buffer[16] = { '\0' };
-        snprintf(buffer, sizeof(buffer), "%d", val.*ptr);
-        data += buffer;
-      }
-    private:
-      int T::*ptr;
-    };
-    
-    template <typename T>
-    class direct_binder<T, double> : public binder_impl<T> {
-    public:
-      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 {
-//        if (false) {
-          json::helper::parse_numeric(val.*ptr, data);
-//        } else {
-//          throw json::malformed_json_exception("Expected a floating point type here");
-//        }
+      binder(binder const& other) :
+      impl(other.impl->clone()) {
       }
       
-      virtual void write(T const& val, std::string & data) const override {
-        char buffer[32] = { '\0' };
-        snprintf(buffer, sizeof(buffer), "%lf", val.*ptr);
-        data += buffer;
-      }
-    private:
-      double T::*ptr;
-    };
-    
-    template <typename T>
-    class direct_binder<T, std::string> : public binder_impl<T> {
-    public:
-      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);
+      binder(binder_impl<T> const* p) :
+      impl(p) {
       }
       
-      virtual void write(T const& val, std::string & data) const override {
-        data += "\"" + val.*ptr + "\"";
+      binder(binder_impl<T> const& r) :
+      impl(r.clone()) {
       }
-    private:
-      std::string T::*ptr;
-    };
-    
-    template <typename T, typename V>
-    class direct_binder<T, std::vector<V> > : public binder_impl<T> {
-    public:
-      direct_binder(std::vector<V> T::*p, binder<V> const&i);
-      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
       
-      virtual void parse(T& val, char const*& data) const override {
-        const char ch = json::helper::get_next_element(data);
-        if (ch != '[') {
-          throw json::malformed_json_exception("Expected an array type");
+      ~binder() {
+        if (impl) {
+          delete impl;
+          impl = nullptr;
         }
-        ++data;
-
-        V to_make;
-        std::vector<V>& vec = val.*ptr;
-        while (*data && *data != ']') {
-          impl.parse(to_make, data);
-          vec.emplace_back(to_make);
-          json::helper::advance_to_boundary<']'>(data);
-        }
-        if (*data) ++data;
-        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 += "[";
-        std::vector<V> const & vec = val.*ptr;
-        for (typename std::vector<V>::const_iterator it = vec.begin(),
-             end = vec.end(); it != end; ++it) {
-          impl.write(*it, data);
-          data += ",";
-        }
-        data.back() = ']';
-      }
-    private:
-      std::vector<V> T::*ptr;
-      binder<V> impl;
-    };
-    
-    template <typename T, typename V>
-    class direct_binder<T, std::map<std::string, V> > : public binder_impl<T> {
-    public:
-      direct_binder(std::vector<V> T::*p, binder<V> const&i);
-      virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
-      
-      virtual void parse(T& val, char const*& data) const override {
-        const char ch = json::helper::get_next_element(data);
-        if (ch != '{') {
-          throw json::malformed_json_exception("Expected an array type");
-        }
-        ++data;
-        
-        V to_make;
-        std::map<std::string, V>& vec = val.*ptr;
-        std::string key;
-        while (*data && *data != '}') {
-          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");
-          }
-          impl.parse(to_make, ++data);
-          vec.emplace(key, to_make);
-          json::helper::advance_to_boundary<'}'>(data);
-        }
-        if (*data) ++data;
-        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 += "{";
-        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 + "\":";
-          impl.write(it->second, data);
-          data += ",";
-        }
-        data.back() = '}';
-      }
-    private:
-      std::map<std::string, V> T::*ptr;
-      binder<V> impl;
-    };
-    
-    template <typename T>
-    class object_binder : public binder_impl<T> {
-    public:
-      object_binder() {}
-      virtual binder_impl<T>* clone() const override { return new object_binder(*this); }
-
-      template <typename V>
-      object_binder& operator()(std::string const&k, V T::*ptr, binder_impl<V> const&v) {
-        return (*this)(k, binder<T>(new direct_binder<T, V>(ptr, binder<V>(v) )));
-      }
-      
-      object_binder& operator()(std::string const&k, binder<T> const&v) {
-        mapping.emplace(k, v);
-        return *this;
-      }
-      
-      virtual void parse(T& object, char const*& data) const override {
-        const char ch = json::helper::get_next_element(data);
-        if (ch == '{') {
-          parse_object(object, ++data);
-        } else {
-          throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
-        }
-      }
-      
-      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 + "\":";
-          it->second.write(val, data);
-          data += ",";
-        }
-        data.back() = '}';
-      }
-      
-      void parse_object(T& object, char const*& data) const {
-        std::string key;
-        while (*data && *data != '}') {
-          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");
-          }
-          auto it = mapping.find(key);
-          if (it != mapping.end()) {
-            it->second.parse(object, ++data);
-          } else {
-            throw json::malformed_json_exception("Unexpected key " + key);
-          }
-          json::helper::advance_to_boundary<'}'>(data);
-        }
-        if (*data) ++data;
-        else throw json::malformed_json_exception("Reached end of parse string without finding object end");
-      }
-
-      template <typename E>
-      object_binder& operator()(std::string const& s, E T::*p) {
-        return operator()(s, binder<T>(new direct_binder<T, E>(p)));
-      }
-    private:
-      std::map<std::string, binder<T>> mapping;
-    };
-    
-    template <typename T>
-    class tuple_binder : public binder_impl<T> {
-    public:
-      virtual binder_impl<T>* clone() const override { return new tuple_binder(*this); }
-
-      tuple_binder& operator()(binder<T> const&b) {
-        members.push_back(b);
-        return *this;
-      }
-      
-      virtual void parse(T& object, char const*& data) const override {
-        const char ch = json::helper::get_next_element(data);
-        if (ch == '[') {
-          parse_tuple(object, ++data);
-        } else {
-          throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
-        }
-      }
-      
-      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) {
-          it->write(val, data);
-          data += ",";
-        }
-        data.back() = ']';
+      void parse(T& object, char const*& data) const {
+        if (!impl) return;
+        impl->parse(object, data);
       }
       
-      void parse_tuple(T& object, char const*& data) const {
-        auto it = members.begin();
-        while (*data && *data != ']' && it != members.end()) {
-          it->parse(object, data);
-          json::helper::advance_to_boundary<']'>(data);
-          ++it;
-        }
-        if (it != members.end()) {
-          throw json::malformed_json_exception("Failed to parse every member of tuple");
-        }
-        if (*data) ++data;
-        else throw json::malformed_json_exception("Reached end of parse string without finding array end");
-      }
-
-      template <typename E>
-      tuple_binder& operator()(E T::*p) {
-        return operator()(binder<T>(new direct_binder<T, E>(p)));
+      void write(T const& object, std::string & data) const {
+        if (!impl) return;
+        impl->write(object, data);
       }
-    
     private:
-      std::vector<binder<T>> members;
+      binder_impl<T> const* impl;
     };
     
     template <typename T, typename S = T>
@@ -389,7 +101,13 @@ namespace json {
       visitor.write(data);
     }
   }
-  
 }
 
+#include "json/json_direct_binder.hpp"
+#include "json/json_tuple_binder.hpp"
+#include "json/json_object_binder.hpp"
+#include "json/json_direct_map_binder.hpp"
+#include "json/json_direct_scalar_binder.hpp"
+#include "json/json_direct_vector_binder.hpp"
+
 #endif /* json_binder_h */

+ 46 - 0
json_common.cpp

@@ -0,0 +1,46 @@
+//
+//  json_binder.cpp
+//  json
+//
+//  Created by Sam Jaffe on 4/23/16.
+//
+
+#include "json_common.h"
+
+namespace json {
+  namespace helper {
+    namespace {
+      std::string get_no_end_error(char expected, char found) {
+        char str[64] = { '\0' };
+        snprintf(str, sizeof(str),
+                 "Expected delimeter: ',' or '%c', got '%c' instead",
+                 expected, found);
+        return str;
+      }
+    }
+
+    const char get_next_element(char const*& data) {
+      while (isspace(*data)) ++data;
+      return *data;
+    }
+    
+    void advance_to_boundary(char const endtok, char const*& data) {
+      char const next = get_next_element(data);
+      if (next == ',') {
+        ++data;
+      } else if (next != endtok) {
+        throw json::malformed_json_exception(get_no_end_error(endtok, *data));
+      }
+    }
+    
+    std::string parse_string(char const * & data) {
+      char const* start = data;
+      while (*++data) {
+        if (*data == '"' && *(data-1) != '\\') {
+          return std::string(start+1, data++);
+        }
+      }
+      throw json::malformed_json_exception("Could not locate end of string");
+    }
+  }
+}

+ 11 - 26
json_common.h

@@ -19,36 +19,21 @@ namespace json {
 
 namespace json {
   namespace helper {
-    const char get_next_element(char const*& data) {
-      while (isspace(*data)) ++data;
-      return *data;
-    }
+    const char get_next_element(char const*& data);
     
-    template <char const E>
-    void advance_to_boundary(char const*& data) {
-      switch (get_next_element(data)) {
-        case ',': // This is there 'more data remains' token for the compound value
-          ++data;
-          break;
-        case E: // This is the end-token for the compound value
-          break;
-        default: // Error, malformed JSON
-          throw json::malformed_json_exception(std::string("Expected to recieve container delimiter ',' or '") + E + "', got '" + *data + "' instead");
-          break;
-      }
-    }
+    /**
+     * @throws json::malformed_json_exception
+     */
+    void advance_to_boundary(char const endtok, char const*& data);
+    
+    /**
+     * @throws json::malformed_json_exception
+     */
+    std::string parse_string(char const * & data);
     
     template <typename T>
     void parse_string(T& json, char const*& data) {
-      char const* start = data;
-      while (*++data) {
-        if (*data == '"' && *(data-1) != '\\') {
-          json = std::string(start+1, data);
-          ++data;
-          return;
-        }
-      }
-      throw json::malformed_json_exception("Could not locate end of string");
+      json = parse_string(data);
     }
     
     template <typename T>