Skip to content

Why not also look inside the type for (static) to_json and from_json funtions? #511

Description

@JoveToo

My knowledge of template magic is limited, so I'm probably missing something :).

Static member functions can access private and protected members.

class Type {
  ...
public:
    static void to_json(json &j, const Type &p) {...};
    static void from_json(json &j, Type &p) {...};
}

The main reason I see to declare these functions outside the scope of Type is to allow to separate serialization completely, which isn't possible because you always need the friends declaration, have getters/setters for all your internal variables or worse, have everything public.

I would even look inside Type for a serializer for std::shared_ptr<Type>.

In case a practical example helps, I am trying to serialize a template<Type> that contains map of std::shared_ptr<Type>.

// T::ptr_type is a typedef for std::shared_ptr<T>
template<typename T>
class Resolvable
{
public:
	typedef 	T	ref_type;
	Resolvable() {};
	Resolvable(ref_type r) : mReference(r) {};

	ref_type &Reference() {return mReference; };

protected:
	ref_type mReference;
};

template<typename T>
class Resolver {
public:
	Resolver()
	{
		// force T to be of Resolvable
		(void)static_cast<Resolvable<typename T::ref_type>*>((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<Resolvable<typename T::ref_type>*>(t.get())->Reference();
		if (mEntries.find(r) != mEntries.end())
			throw std::runtime_error("Duplicate entry " + std::to_string(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<T> &resolver)
	{
		for(const std::pair<typename T::ref_type, typename T::ptr_type> &entry: resolver.mEntries)
		{
			j.push_back(*(entry.second));
		}
	};

	static void from_json(json &j, Resolver<T> &resolver)
	{
		for (json &e : j)
		{
			typename T::ptr_type ptr = std::make_shared<T>();
			T::from_json(e, *ptr);
			resolver.add(ptr);
		}
	};

private:
	std::map<typename T::ref_type, typename T::ptr_type>	mEntries;
};

The problem lies in the Resolver::from_json function: I can't find a way to express T::from_json(e, *ptr); in another way. If I do (*ptr) = e;, *ptr = e.get<T>() or ::from_json(e, *ptr);, it can't find the serialization function (and I run out of alternatives).

I have it all working by using static member functions and declaring external helpers that forward to the static ones. It works, it probably all gets optimized away, but it requires a lot of useless code.

If the serializer would check for those functions in the type, none of it would be necessary.

I realize there are probably other good solutions for my problem here (feel free to suggest them) but the static member function seems so simple a solution (for my problem at least) that I had to ask why this is not supported.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind: enhancement/improvementstate: stalethe issue has not been updated in a while and will be closed automatically soon unless it is updated

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions