-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is it possible to make the library work with templates? #8
Comments
After some experimentation I've figured out that a part of the problem is in the fact that in this case the template instantiates not in the module scope, so |
I am thinking this through. What you ask for, is often asked for virtual functions too. The answer is: it's impossible because the virtual table could not be known until link time. But open methods face the same problem and get around it (that's updateMethods). So there is a glimmer of hope. |
No, I don't get a message with the core dump (and the stack trace as shown by
|
I took a first look at this and it is not very promising. It seems that class template instantiations are not registered in ModuleInfo: import std.stdio;
class Foo {}
class Bar(T) : Foo
{
int i;
}
alias BarInt = Bar!int;
const barInt = new BarInt;
void main()
{
foreach (mod; ModuleInfo) {
foreach (c; mod.localClasses) {
writeln(c);
}
}
} Output:
I'll ask in the forum if someone has an idea... |
@jll63 : I had actually written out a long example, but noticed that you have a I'm quite smitten with the openmethods.d approach and would like to implement a matrix library based on it, but only if it conveniently supports templating in terms of matrix data types. How far along are you? How can I help (if at all)? |
The Extrapolating from the non-templatized example: module matrix;
Matrix plus(virtual!Matrix, virtual!Matrix);
@method DenseMatrix _plus(DenseMatrix a, DenseMatrix b) { ... } Ideally we want this to work: module matrix;
mixin(registerMethods);
Matrix!T plus(T)(virtual!(Matrix!T), virtual!(Matrix!T));
@method DenseMatrix!T _plus(T)(DenseMatrix!T a, DenseMatrix!T b) { ... } A compiler based approach would make this possible (not that it would be easy). For a library, it is much more difficult. There are three challenges. Generating the dispatcherMaybe you already know this, if you looked into how Matrix plus(Matrix a, Matrix b) { return openmethods.Method!(matrix.plus).resolve(a, b)(a, b); }
openmethods.Method!(matrix.plus) plus(openmethods.MethodTag, Matrix, Matrix); (Take this as pseudocode. In reality, the functions are aliases, The first function is used at runtime, it is the entry point in the method: it finds the correct specialization and calls it. The second function (which is used at compilation time; it does not have a body) is used to match method specializations with the right method declaration. The machinery inside And here lies the first challenge for templatized methods: D does not make it possible to fully inspect a function template. Thus it it not possible - at least the last time I looked - for the library to generate this: Matrix!T plus(Matrix!T a, Matrix!T b) { return openmethods.Method!(matrix.plus(T)).resolve(a, b)(a, b); }
openmethods.Method!(matrix.plus!T) plus(openmethods.MethodTag, Matrix!T, Matrix!T); The alternative is to require the user to write the two functions by hand, and the challenge becomes, what can the library do to make it as terse and as robust as possible. Instantiating the specializationWhen the compiler sees code like this: @method DenseMatrix!T _plus(T)(DenseMatrix!T a, DenseMatrix!T b) { ... } It has no reason to instantiate If open methods were part of the language, it would probably involve a link-time step that would do something like this:
It is probably be possible to create a compile-link wrapper tool that would do this for A much simpler approach is to require the user to explicitly instantiate the specializations, probably using a mixin helper: mixin template RegisterMatrixOps(T)
{
DenseMatrix!T plus(T)(DenseMatrix a, DenseMatrix b) { ... }
DenseMatrix!T times(T)(DenseMatrix a, DenseMatrix b) { ... }
}
mixin RegisterMatrixOps!int;
mixin RegisterMatrixOps!double; Registering the classes
template declareMatrixClasses(T)
{
mixin registerClasses!(Matrix!T);
mixin registerClasses!(DenseMatrix!T);
mixin registerClasses!(DiagonalMatrix!T);
}
mixin declareMatrixClasses!int;
mixin declareMatrixClasses!double; So you see, it's challenging...On the other hand, the burden of manual registration would be on the author of the
For me, at the time being, D is a hobby I return to once in a while. Every time I do, D has evolved. Maybe at one point challenge #1 will become tractable. Probably it will involve better compile-time inspection for templates. A while ago I made a bit of noise about this in the forum. As for the other two challenges, I think that most people could live with the manual instantiation approach. Have you looked at the internals? Perhaps you can come up with ideas on challenge #1. I spent many days racking my brains, |
Being fairly unfamiliar with the magic behind the scenes, the outcome I would naïvely like to see is to have an auto-generated per-class (= per file if we assume an idiom where each class is confined to its own file) set of machinery which can be called before specialisation use to declare and register the class specialisation(s) and the method specialisation(s) in one go. So if the user needs both ushort and float matrices, they would only need to add (something like) the following at the call-site: import Matrix;
import DenseMatrix;
import DiagonalMatrix;
import FooMatrix;
import BarMatrix;
import openmethods;
mixin(registerMethods);
auto classes = ["Matrix", "DenseMatrix", "DiagonalMatrix", "FooMatrix", "BarMatrix"];
foreach(cls; classes)
{
declareSpecialisation!ushort(cls);
declareSpecialisation!float(cls);
} The goal here would be to be able to use both compile-time and run-time introspection to construct (or marshal / compose) e.g. So to re-use your Matrix class example, for each type of Matrix class I would need to write/specify two templates which each have a pre-defined naming structure mandated by openmethods.d. Using the DenseMatrix class definition file as an example, I would simply declare the specialisations of the class and the methods as follows (think of it in pseudocode terms, not as a concrete implementation example): template declareClassSpecialisation(T) {
mixin registerClasses!(DenseMatrix!T);
}
template declareMethodSpecialisation(T)
{
DenseMatrix!T plus(T)(DenseMatrix!T a, DenseMatrix!T b) { ... }
DenseMatrix!T times(T)(DenseMatrix!T a, DenseMatrix!T b) { ... }
} The openmethods.d machinery would then look in each imported file for those two templates names and just do the right thing if they are present. This would allow the declarations to stay local to each file that makes use of them, thus obeying both the principle of least surprise and the principle of locality? Is this line of thinking at all fruitful? |
Ugh. I typed a lengthy reply to your comment, then, as I was cleaning it up, Vivaldi crashed while trying to load suggestions for a typo. I don't have the spirit to re-type everything from scratch right now...I think that the major point was my reply to:
But how would the machinery find out which In conclusion, I added that there were reasonable solutions to problem 2 (instantiating and registering the method specialisations), but problem 1 (nice syntax for templatized method declarations) was a tougher problem, given the state of D (last time I looked). |
Am I misunderstanding that this information ought to be supplied by the user? Are you suggesting that the library writer has to have the foresight to specify which instantiations the user might want to make (re. problem 1: nice syntax for templatized method declarations)? As in: To support a specific (and limited) set of instantiations only? Wouldn't that defeat the point of generics/template metaprogramming in the first place? |
So, actionable suggestion: Would it make sense to you if I suggested that you try to add templates to your matrix examples where the first goal is to simply get something working? Then the next step can be to work on how to better organise it and make it prettier/more elegant to use? Having a base to work from (even if it's ugly) is probably more fruitful than having theoretical discussions about it... I have dabbled in adding templates locally, but it feels like I'm hitting a dead end; mostly it's a knowledge/skill gap I guess -- hence why I'm trying to gently poke you. ^^' |
Unless method templates are supported by the language or the toolchain, yes.
I think that in general, that is not desirable. In the specific case of matrices, one could argue that Things get even worse when you consider that, in all likelihood, a flexible matrix library would support mixed binary operators: Matrix!(typeof(T1.init + T2.init)) plus(T1, T2)(const T1 a, const T2 b) Now we have to deal with all the combinations of two types :-| So to sum up, when it comes to template instantiations, there is a spectrum of possible implementations, from least desirable to most:
It is possible to work on this incrementally. First implement (1) with the best and simplest API for manual instantiation. Then proceed to (2) - I have a couple of ideas on how to do this, again within a spectrum between easy to implement to easy to use. Then, (3), hack |
That's this branch.
Yes, that's the current bit to do. Alas, I am very pessimistic about the declaration syntax. Although I have thought of a couple of approaches. I am working on this subject in the C++ version too. There, crazy C++ template syntax led me to toy with making some of the internals public. Clean up the
Sure. I would love to see this work in D and in C++. I have not done any D programming for months, unfortunately D is more of a hobby for me. Each time I want to do serious work I have to re-learn parts of the language - and re-discover some of the gaps and shortcomings. Have you noticed the |
For example, |
Consider the following snippet, which attempts to recreate (a better, extensible)
to
:This doesn't compile because, it seems, the open methods mechanism doesn't kick in for
to
and the compiler doesn't seeWrapper!string
as a possible stand-in forvirtual!BaseWrapper
. The error message isIs it possible to enable the library to work with templates? This would add a considerable layer of cool and useful to it.
The text was updated successfully, but these errors were encountered: