Determining function template from runtime type: better ideas?

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Mar 22 16:05:37 UTC 2022


On Tue, Mar 22, 2022 at 07:09:07AM +0000, cc via Digitalmars-d-learn wrote:
> Sorry for the fairly lengthy post.  I'm wondering if there are any
> suggested good practices in place for calling templated functions
> using the runtime type of an object, e.g. what `typeid(object)`
> returns.
[[...]

Templates are instantiated at compile-time, and polymorphic types are
resolved at runtime.  It's not possible to do what you describe (not
directly, anyway).

However, you *can* get away with something similar by using CRTP:

	class Serializable(Derived, Base = Object) : Base {
		static if (is(Base : Serializable!(Base, C), C)) {
			override void serialize(...) {
				serializeImpl();
			}
		} else {
			void serialize(...) {
				serializeImpl();
			}
		}
		private void serializeImpl(...) {
			auto target = cast(Derived) this;
			... // Now you have the derived type, serialize accordingly
		}
	}

	class Person : Serializable!Person { ... }

	class Boss : Serializable!(Boss, Person) { ... }

	... // etc.

The idea is to inject an intermediate base class above each leaf class
in your class hierarchy that automates away the boilerplate of
serialization code.  In accordance with CRTP, you pass the class you're
declaring as a template parameter into the Serializable wrapper, so that
it has compile-time information about your derived class type, which it
uses in serializeImpl() to serialize the derived class data directly --
no runtime typeid() is needed. (Generally, use of typeid() is
discouraged; it still exists to support legacy D code but it's usually
better to use compile-time introspection instead.)

The above example is only half the equation, of course. For
deserialization, you need a way of dynamically selecting the right
deserialization function overload to recreate the derived type. This can
be done by registering the deserializers into a global AA keyed by
derived class name using static this(), which gets run at startup time:

	static Object function(ubyte[] data)[string] deserializers;

	class Serializable(Derived, Base = Object) : Base {
		...
		static this() {
			deserializers[Derived.stringof] = (ubyte[] data)
			{
				auto obj = new Derived;
				... // code to decode data here
				return obj;
			};
		}
		...
	}

The main deserializer then just looks up the derived class name in the
input data, and calls the appropriate function in `deserializers` to
recreate the derived object. (Note: this method does not depend on
Object.factory, which is fraught with problems.)


T

-- 
Answer: Because it breaks the logical sequence of discussion. / Question: Why is top posting bad?


More information about the Digitalmars-d-learn mailing list