/* * test_template.cpp * * Created on: Mar 22, 2017 * Author: Johan Verrept * */ #include "json.hpp" using json = nlohmann::json; // // taken from http://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature // template struct has_to_json { template struct SFINAE {}; template static char Test(SFINAE*); template static int Test(...); static const bool value = sizeof(Test(0)) == sizeof(char); }; template struct has_from_json { template struct SFINAE {}; template static char Test(SFINAE*); template static int Test(...); static const bool value = sizeof(Test(0)) == sizeof(char); }; // // These template functions are automatically instantiated to // serialize any object T with static to_json and from_json // member functions. // template ::value >> inline void to_json(json& j, const T& opt) { T::to_json(j, opt); }; template ::value >> inline void from_json(const json& j, T& opt) { T::from_json(j, opt); }; // // These template functions are automatically instantiated to // serialize any pointer T::ptr_type as the class itself // template inline void to_json(json& j, const std::shared_ptr & opt) { T::to_json(j, *opt); }; template inline void from_json(const json& j, std::shared_ptr & opt) { T::from_json(j, *opt); }; // // Resolver functions // template class Resolvable { public: typedef T ref_type; Resolvable() {}; Resolvable(ref_type r) : mReference(r) {}; ref_type &Reference() {return mReference; }; void SetReference(ref_type r) { mReference = r; }; static void to_json(json &j, const Resolvable &p) { j["reference"] = p.mReference; }; static void from_json(const json &j, Resolvable &p) { p.mReference = j["reference"]; } protected: ref_type mReference; }; template class Resolver { typedef std::map map_type; public: Resolver() : mLastRef(0) { // force T to be of Resolvable (void)static_cast*>((T*)0); } typename T::ptr_type &resolve(const typename T::ref_type &r) { return mEntries.at(r); } typename T::ref_type add(const typename T::ptr_type &t) { typename T::ref_type r = static_cast*>(t.get())->Reference(); if (r > mLastRef) mLastRef = r; if (r != 0) { typename map_type::iterator e = mEntries.find(r); if (e != mEntries.end()) { // we already got this one. discard add. if (*t == *(e->second)) return r; throw std::runtime_error("Duplicate entry " + std::to_string(r) + " but objects are not the same."); } } else { // now you dunnit. we need to see if this string is already in the map. for(const typename map_type::value_type &entry : mEntries) if (*(entry.second) == *t) return entry.first; // we haven't got an entry for this one and it's ref is 0, so assign one. r = ++mLastRef; } static_cast*>(t.get())->SetReference(r); mEntries[r] = t; return r; } typename T::ref_type add(typename T::ptr_type &&t) { typename T::ref_type r = static_cast*>(t.get())->Reference(); if (r > mLastRef) mLastRef = r; if (r != 0) { typename map_type::iterator e = mEntries.find(r); if (e != mEntries.end()) { // we already got this one. discard add. if (*t == *(e->second)) return r; throw std::runtime_error("Duplicate entry " + std::to_string(r) + " but objects are not the same."); } } else { // now you dunnit. we need to see if this string is already in the map. for(const typename map_type::value_type &entry : mEntries) if (*(entry.second) == *t) return entry.first; // we haven't got an entry for this one and it's ref is 0, so assign one. r = ++mLastRef; } static_cast*>(t.get())->SetReference(r); mEntries[r] = t; return r; } void remove(const typename T::ref_type r) { mEntries.erase(r); } static void to_json(json &j, const Resolver &resolver) { for(const std::pair &entry: resolver.mEntries) { json e = json::object({}); e = (*entry.second); j.push_back(e); } }; static void from_json(const json &j, Resolver &resolver) { for (const json &e : j) { typename T::ptr_type ptr = std::make_shared(); *ptr = e; resolver.add(ptr); } }; private: std::map mEntries; typename T::ref_type mLastRef; }; class A { public: typedef std::shared_ptr ptr_type; A(std::string s) : mName(s) {}; static void to_json(json &j, const A &p) { j["name"] = p.mName; j["type"] = "A"; } static void from_json(const json &j, A& p) { if (j["type"] != "A") throw std::runtime_error(std::string("Saw ") + j["Name"].get() + " but expected " + p.mName); p.mName = j["name"]; } private: std::string mName; }; class R : public Resolvable { public: typedef std::shared_ptr ptr_type; typedef Resolver resolver_type; R(std::string s) : mName(s) {}; static void to_json(json &j, const R &p) { j["name"] = p.mName; j["type"] = "R"; Resolvable::to_json(j, static_cast(p)); } static void from_json(const json &j, R& p) { Resolvable::from_json(j, static_cast &>(p)); if (j["type"] != "R") throw std::runtime_error("Saw " + j["type"].get() + " but expected R"); p.mName = j["name"]; } static ptr_type &resolve(const ref_type &ref) { return sResolver.resolve(ref); }; static resolver_type &resolver() { return sResolver; }; friend bool operator==(const R &l, const R &r) { return false; } private: std::string mName; static resolver_type sResolver; }; R::resolver_type R::sResolver; // // These are created explicitly because I cannot find // a template that will match a class that inherits // a Resolvable but does not match others // AND that overrides the default template // for T::ptr_type defined above. // // Since these resolvable classes are rare, I chose // to make the other templates the default. // inline void to_json(json& j, const R::ptr_type & opt) { j = opt->Reference(); } inline void from_json(const json& j, R::ptr_type & opt) { opt = R::resolve(j); } int main(int argc, char *argv[]) { A::ptr_type a; R::ptr_type r1, r2; a = std::make_shared("Class A"); r1 = std::make_shared("Class R1"); r2 = std::make_shared("Class R2"); R::resolver().add(r1); R::resolver().add(r2); json resolver, normal, reftype; resolver = R::resolver(); normal = a; reftype = r1; std::stringstream r, n, ref; // convert to strings. r << resolver; n << normal; ref << reftype; // check string contents: warning a bit too static, this. if (r.str() != "[{\"name\":\"Class R1\",\"reference\":1,\"type\":\"R\"},{\"name\":\"Class R2\",\"reference\":2,\"type\":\"R\"}]") return -1; if (n.str() != "{\"name\":\"Class A\",\"type\":\"A\"}") return -1; if (ref.str() != "1") return -1; R::ptr_type rr1; // verify that the returned class is the correct one. rr1 = reftype; if (rr1 != r1) return -1; if (rr1->Reference() != 1) return -1; return 0; }