TypeFunction example creatiing a conversion matrix

H. S. Teoh hsteoh at quickfur.ath.cx
Fri Oct 2 15:04:22 UTC 2020


On Fri, Oct 02, 2020 at 03:36:32AM +0000, Adam D. Ruppe via Digitalmars-d wrote:
[...]
> The basic idea though is it doesn't take magic to do this. All the
> types the Variant needs *are* available to it thanks to normal
> templates, every type it ever sees are sent in (and ones it never sees
> it never needs), just the difficulty is they come in two separate
> calls. Thus it extracts what it needs from them into a runtime class
> to bridge that gap.
[...]

This is awesome!  I thought about it last night, and realized that this
is the same recipe that I discovered for automatic extraction of
i18n-able strings.  The key ingredient is static ctors, usually wrapped
in a wrapper struct, that register the needed data with a central
collector at program startup (or even when dynamically-loaded libraries
are loaded):

	auto myTemplateFunc(T)(T t) {
		struct RuntimeStartupHook {
			static this() {
				registerType!T( /* some info here */);
			}
		}

		return ... /* function implementation here */;
	}

	static RuntimeInfo db;
	void registerType(T)(...) {
		db.add( ... /* information about T */);
	}

Each time somebody calls myTemplateFunc, information about the type T is
injected into a static ctor that will get called upon program startup,
which registers information about T into the runtime database `db`. This
can be anything from the type name, to the typeid, or whatever traits
introspected from T in RuntimeStartupHook.  By the time main() runs, we
have collected information about *all* types that have been used to
instantiate myTemplateFunc.  This works with separate compilation.

Better yet, this will even work for dynamically-loaded libraries
(provided their static ctors are automatically run upon startup),
because the static ctors will register any new T's that were used in the
library code with the central database. So after loading the library,
`db` will contain this new information, and the implementation of
myTemplateFunc can then make use of it.

In this way, we can collect *all* types used to instantiate a particular
template function, and be able to make decisions over that at runtime.
Such as Variant being able to loop over all types it was instantiated
with.  For example, if we wanted to support, say, invoking some method
.abc on T if it exists, we can do it by having RuntimeStartupHook
introspect T at compile-time to bind any method named .abc and wrap that
in a function pointer or delegate.  Then at runtime, we simply consult
the database to determine whether T has such a method, and if so, invoke
it.  A lot of compile-time information can be transferred to runtime
this way. This is powerful stuff!

About the only thing we can't do is things like implicit conversions
between two instantiations of T, because is(T : U) requires knowledge of
*both* T and U simultaneously at compile-time, but in the scheme above,
T and U are individually registered at runtime, and we cannot know both
simultaneously until after the runtime hooks have run. (E.g., if T was
used in main but U was used in a dynamically-loaded library, then there
is no way to leverage is(T : U) at compile-time.)  So unfortunately, we
still have to re-implement is(T : U) manually in library code.


T

-- 
People walk. Computers run.


More information about the Digitalmars-d mailing list