www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - another cool RTInfo trick - i want in runtime

reply "Adam D. Ruppe" <destructionator gmail.com> writes:
Check this out. Modify your druntime slightly. In object.di, find 
template RTInfo(T) and add this bit:

     import core.config;
     alias checkResult = TypeCheck!T;

Then, pop open import/core/config.d and put these contents in it:

module core.config;
template TypeCheck(T) {
     enum TypeCheck = true;
}


Compile a program, any program. Should be no difference in 
anything - no errors, no changes to the binary, no compile 
slowdown - from before the modifications.


Now, create a file named anything you want with these contents:

module core.config;
template TypeCheck(T) {
   static if(T.stringof == "Cool")
     static assert(0, "No type named 'Cool' is allowed!");
   enum TypeCheck = true;
}


Compile your program, should still work. Then add "struct Cool{}" 
to it anywhere... and you get a compile error.



Brothers and sisters, I've talked to you before about doing 
custom RTInfo using __traits(compiles) tricks to look for the 
config module, but my previous ways - while they worked - sucked 
because the compiler would look for the module over and over 
again, slowing things down a bit.

This new method achieves success on all counts. By offering a 
file with druntime to be the module up front, the compiler does 
no special searching, it finds it. It is a trivial file, so 
impact on compile speed is negligible.

But then, if you offer your own file with the same module name on 
the command line, dmd looks THERE first, finds your special code, 
and knows to skip searching the import path! Even true for 
object.d, since this is all in templates with local imports; the 
template still needs to be instantiated.

Since this object.d doesn't actually store anything* in the 
RTInfo itself, there's no worry of binary incompatibilities 
resulting from separate compiliation with different rtinfo 
modules. It simply provides a hook into the types for checking.

Using this, we can automatically check all kinds of per-project 
requirements. Don't want virtual functions in your class unless 
they are reviewed? Make your TypeCheck loop over them and look 
for a UDA, issuing a static assert if not there. Don't want 
references to un-owned mutable data? Ditto. Have a naming 
convention to check? Can do that too.


I see lots of upsides, and the downsides from my last experiment 
with this I believe have been eliminated. Am I missing something? 
I wanna do a druntime pull request!


* User data extensions could potentially be allowed if RTInfo 
stored a pointer to an interface, which may be null. Other 
modules may implement the interface differently, but as long as 
they implement it (or set it to null), it should still have 
consistent binary compatibility.

You might make object.d's RTInfo create a static struct with 
information the GC needs first, then a pointer to the user 
defined data. The RTInfo template ultimately resolves to the 
pointer to that struct. The data itself is consistent - still 
defined in only one place, druntime itself, and the GC info is 
available right there, no added indirection, and we have the hook 
for user additions too, all under a consistent interface.

You can also add runtime info about a type using static 
constructors:
===
module core.config;

string[] types;

template TypeCheck(T) {
     enum TypeCheck = true;

     shared static this() {
         types ~= T.mangleof;
     }
}
===
module test500.d
struct Cool {}

void main() {
	import core.config, std.stdio;
	writeln(types);
}
===
dmd test500.d rtinfo.d

$ ./test500
["S7test5004Cool", 
"S3std5array17__T8AppenderTAyaZ8Appender4Data", 
"S3std5array17__T8AppenderTAyaZ8Appender"]


Run time info available about all the types used in that program!


Soooo yeah, this is pretty much a pure win all around to my eyes. 
Am I blind to the suck?
Jan 16 2014
next sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 16 January 2014 at 15:57:05 UTC, Adam D. Ruppe wrote:
 Now, create a file named anything you want with these contents:
I guess this won't work with rdmd, then?
Jan 16 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 16 January 2014 at 16:09:52 UTC, Vladimir Panteleev 
wrote:
 I guess this won't work with rdmd, then?
That's due to a deficiency in rdmd: tt doesn't let you forward all the available module options to dmd, even though it would make sense to do it, for this kind of replaceable config module thing and also for replacing say one phobos file at a time for doing dev work on that . rdmd should be fixed to allow you to pass more than one explicit module (maybe just add an option like --additional-dmd-option=file.d).
Jan 16 2014
parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 16 January 2014 at 16:21:25 UTC, Adam D. Ruppe wrote:
 On Thursday, 16 January 2014 at 16:09:52 UTC, Vladimir 
 Panteleev wrote:
 I guess this won't work with rdmd, then?
That's due to a deficiency in rdmd: tt doesn't let you forward all the available module options to dmd, even though it would make sense to do it, for this kind of replaceable config module thing and also for replacing say one phobos file at a time for doing dev work on that . rdmd should be fixed to allow you to pass more than one explicit module (maybe just add an option like --additional-dmd-option=file.d).
That sounds overcomplicated. Why not move the standard search paths to after the users' ones? So that users can override Phobos and Druntime modules simply by creating them within their search path.
Jan 16 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 16 January 2014 at 16:26:49 UTC, Vladimir Panteleev 
wrote:
 Why not move the standard search paths to after the users' ones?
That might work too. Either way though, the compiler and language let you list additional modules and it all just works if you do, so rdmd should too... however it does. I don't use rdmd myself so the specific solution should probably be guided by people who do.
Jan 16 2014
parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 16 January 2014 at 16:37:16 UTC, Adam D. Ruppe wrote:
 I don't use rdmd myself so the specific solution should 
 probably be guided by people who do.
Well, the beauty of rdmd is that you don't need to mess with options in the general case. My setup is that generally I press "Enter" on a .d file and it is built into an .exe (if there is an entry point in any imported modules) - no other action required. If I had to modify the command line for every invocation, that would not work for my case, so I'd have to write a rdmd wrapper to look for additional command-line options in a configuration file in the current directory, or implement something like that in rdmd itself. If these requirements can be specified in the source code itself, that would be much better. In our case it would simply be the presence of a file in the project search path. An existing example of this is pragma(lib) - instead of telling the user to specify which libraries to add to the linker command line, or providing a makefile (with all its problems), a pragma(lib) can be used to pull in those libraries automatically.
Jan 16 2014
prev sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 16 January 2014 at 16:26:49 UTC, Vladimir Panteleev 
wrote:
 Why not move the standard search paths to after the users' 
 ones? So that users can override Phobos and Druntime modules 
 simply by creating them within their search path.
Never mind, this is already the case! It depends on how you write your own sc.ini (I had mine with Phobos/Druntime in front).
Jan 16 2014
parent "Dicebot" <public dicebot.lv> writes:
On Thursday, 16 January 2014 at 17:10:42 UTC, Vladimir Panteleev 
wrote:
 On Thursday, 16 January 2014 at 16:26:49 UTC, Vladimir 
 Panteleev wrote:
 Why not move the standard search paths to after the users' 
 ones? So that users can override Phobos and Druntime modules 
 simply by creating them within their search path.
Never mind, this is already the case! It depends on how you write your own sc.ini (I had mine with Phobos/Druntime in front).
Probably we should pick one way or another as standard for packaging so that projects can rely on it.
Jan 16 2014
prev sibling next sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 16 January 2014 at 15:57:05 UTC, Adam D. Ruppe wrote:
 Check this out. Modify your druntime slightly. In object.di, 
 find template RTInfo(T) and add this bit:
Suggestions: 1. I guess TypeCheck is a temporary name? I would suggest e.g. CustomRTInfo. 2. How about using a template mixin instead of a template? Then you can drop the dummy bool. I know that if you use a mixin, you can't refer to stuff that's in the scope where it's declared, but I think that's OK - because you probably don't want an object file for config.d anyway, since: 3. rdmd will not add config.d to the compiler's command line, since it lies in an excluded package "core" (so it's assumed it'll be in phobos.lib). But that's OK, since there will be no symbols in config.d to link against anyway! It all fits together quite nicely. You can access stuff in your main program by importing its modules within the template mixin.
Jan 16 2014
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 16 January 2014 at 18:15:39 UTC, Vladimir Panteleev 
wrote:
 1. I guess TypeCheck is a temporary name? I would suggest e.g. 
 CustomRTInfo.
My first thought when writing this was the TypeCheck would just contain a bunch of static asserts to ensure the type matches whatever your project's special requirements are; it would act kinda like a plugin to the compiler's semantic check. (Actually, it does the bool because my first try was to use it as a template constraint, but if that fails, ti just stops the RTInfo from instantiating; it doesn't actually fail the compile like static assert inside does.) But CustomRTInfo is a good name too, especially if we are adding data.
 2. How about using a template mixin instead of a template?
If we actually mix it in that could change the binary layout of the thing though. We don't need the dummy bool btw, I just tried and it works the same without it.
 It all fits together quite nicely. You can access stuff in your 
 main program by importing its modules within the template mixin.
yes
Jan 16 2014
prev sibling next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Thursday, 16 January 2014 at 15:57:05 UTC, Adam D. Ruppe wrote:
 Check this out. Modify your druntime slightly. In object.di, 
 find template RTInfo(T) and add this bit:

     import core.config;
     alias checkResult = TypeCheck!T;

 Then, pop open import/core/config.d and put these contents in 
 it:

 module core.config;
 template TypeCheck(T) {
     enum TypeCheck = true;
 }


 Compile a program, any program. Should be no difference in 
 anything - no errors, no changes to the binary, no compile 
 slowdown - from before the modifications.


 Now, create a file named anything you want with these contents:

 module core.config;
 template TypeCheck(T) {
   static if(T.stringof == "Cool")
     static assert(0, "No type named 'Cool' is allowed!");
   enum TypeCheck = true;
 }


 Compile your program, should still work. Then add "struct 
 Cool{}" to it anywhere... and you get a compile error.



 Brothers and sisters, I've talked to you before about doing 
 custom RTInfo using __traits(compiles) tricks to look for the 
 config module, but my previous ways - while they worked - 
 sucked because the compiler would look for the module over and 
 over again, slowing things down a bit.

 This new method achieves success on all counts. By offering a 
 file with druntime to be the module up front, the compiler does 
 no special searching, it finds it. It is a trivial file, so 
 impact on compile speed is negligible.

 But then, if you offer your own file with the same module name 
 on the command line, dmd looks THERE first, finds your special 
 code, and knows to skip searching the import path! Even true 
 for object.d, since this is all in templates with local 
 imports; the template still needs to be instantiated.

 Since this object.d doesn't actually store anything* in the 
 RTInfo itself, there's no worry of binary incompatibilities 
 resulting from separate compiliation with different rtinfo 
 modules. It simply provides a hook into the types for checking.

 Using this, we can automatically check all kinds of per-project 
 requirements. Don't want virtual functions in your class unless 
 they are reviewed? Make your TypeCheck loop over them and look 
 for a UDA, issuing a static assert if not there. Don't want 
 references to un-owned mutable data? Ditto. Have a naming 
 convention to check? Can do that too.


 I see lots of upsides, and the downsides from my last 
 experiment with this I believe have been eliminated. Am I 
 missing something? I wanna do a druntime pull request!


 * User data extensions could potentially be allowed if RTInfo 
 stored a pointer to an interface, which may be null. Other 
 modules may implement the interface differently, but as long as 
 they implement it (or set it to null), it should still have 
 consistent binary compatibility.

 You might make object.d's RTInfo create a static struct with 
 information the GC needs first, then a pointer to the user 
 defined data. The RTInfo template ultimately resolves to the 
 pointer to that struct. The data itself is consistent - still 
 defined in only one place, druntime itself, and the GC info is 
 available right there, no added indirection, and we have the 
 hook for user additions too, all under a consistent interface.

 You can also add runtime info about a type using static 
 constructors:
 ===
 module core.config;

 string[] types;

 template TypeCheck(T) {
     enum TypeCheck = true;

     shared static this() {
         types ~= T.mangleof;
     }
 }
 ===
 module test500.d
 struct Cool {}

 void main() {
 	import core.config, std.stdio;
 	writeln(types);
 }
 ===
 dmd test500.d rtinfo.d

 $ ./test500
 ["S7test5004Cool", 
 "S3std5array17__T8AppenderTAyaZ8Appender4Data", 
 "S3std5array17__T8AppenderTAyaZ8Appender"]


 Run time info available about all the types used in that 
 program!


 Soooo yeah, this is pretty much a pure win all around to my 
 eyes. Am I blind to the suck?
I guess that could work pretty well for a C++ friend emulation with UDA's! Were not you the one who wanted to show me an example for that with using RTInfo? Offer your way the possibility to check automatically for null references? With an UDA e.g. safe_ref or not_null
Jan 16 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 16 January 2014 at 18:44:54 UTC, Namespace wrote:
 I guess that could work pretty well for a C++ friend emulation 
 with UDA's! Were not you the one who wanted to show me an 
 example for that with using RTInfo?
I've played with rtinfo a lot before, but I don't remember doing C++ friend emulation...
 Offer your way the possibility to check automatically for null 
 references? With an UDA e.g.  safe_ref or  not_null
I think those would be better done with the type info, like NotNull!t. What's cool about the RTInfo thing though is it is checked on everything, so it can check for the *absence* of a UDA as well as for the presence. You might say something must not have any nullable references unless specially marked. The rtinfo checker can confirm that: loop over all members, if it is potentially nullable and isn't marked with either NotNull or i_know_this_is_nullable_and_that_is_ok, it can throw an error. It is possible to do that kind of thing with static assert on the module level too.... sort of. See, the module level one for one can be forgotten and it can't access nested things in functions: void foo() { struct InnerStruct {} } // can't see foo.InnerStuct here! static assert(ModulePassesCheck!(mixin("this_module"))); But RTInfo *does* see InnerStruct, and you don't have to remember to add the check to your module too.
Jan 16 2014
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Thursday, 16 January 2014 at 19:13:42 UTC, Adam D. Ruppe wrote:
 On Thursday, 16 January 2014 at 18:44:54 UTC, Namespace wrote:
 I guess that could work pretty well for a C++ friend emulation 
 with UDA's! Were not you the one who wanted to show me an 
 example for that with using RTInfo?
I've played with rtinfo a lot before, but I don't remember doing C++ friend emulation...
 Offer your way the possibility to check automatically for null 
 references? With an UDA e.g.  safe_ref or  not_null
I think those would be better done with the type info, like NotNull!t. What's cool about the RTInfo thing though is it is checked on everything, so it can check for the *absence* of a UDA as well as for the presence. You might say something must not have any nullable references unless specially marked. The rtinfo checker can confirm that: loop over all members, if it is potentially nullable and isn't marked with either NotNull or i_know_this_is_nullable_and_that_is_ok, it can throw an error. It is possible to do that kind of thing with static assert on the module level too.... sort of. See, the module level one for one can be forgotten and it can't access nested things in functions: void foo() { struct InnerStruct {} } // can't see foo.InnerStuct here! static assert(ModulePassesCheck!(mixin("this_module"))); But RTInfo *does* see InnerStruct, and you don't have to remember to add the check to your module too.
All right, it wasn't you, it was Andrej Mitrovic on my thread http://forum.dlang.org/thread/pdshwedjqgquoibxhrxa forum.dlang.org#post-mailman.1977.1381065059.1719.digitalmars-d-learn:40puremagic.com The problem on a library NotNull struct is: nobody will use it, because it requires NotNull on both sides, by the caller and by the callee: ---- class A { } void safe_foo(NotNull!A na) { } void main() { NotNull!A na = NotNull!A(new A()); safe_foo(na); } ---- Which is a lot more effor than: ---- class A { } void safe_foo( safe_ref A na) { } void main() { A a = new A(); safe_foo(a); } ---- because it moves the check to the callee.
Jan 16 2014
next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 All right, it wasn't you, it was Andrej Mitrovic on my thread 
 http://forum.dlang.org/thread/pdshwedjqgquoibxhrxa forum.dlang.org#post-mailman.1977.1381065059.1719.digitalmars-d-learn:40puremagic.com

 The problem on a library NotNull struct is: nobody will use it, 
 because it requires NotNull on both sides, by the caller and by 
 the callee:

 ----
 class A { }

 void safe_foo(NotNull!A na) { }

 void main() {
     NotNull!A na = NotNull!A(new A());
     safe_foo(na);
 }
 ----

 Which is a lot more effor than:

 ----
 class A { }

 void safe_foo( safe_ref A na) { }

 void main() {
     A a = new A();
     safe_foo(a);
 }
 ----

 because it moves the check to the callee.
Also it is far more readable and easier to write as a contract like: ---- void safe _foo(A a) in { assert(a !is null); } body { } ----
Jan 16 2014
prev sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
This is a bit of a side discussion because the RTInfo can't 
*change* the type, it can only look at it. It is more like a 
centralized place to static asserts or maybe static data than an 
AST macro.

But...

On Thursday, 16 January 2014 at 19:32:10 UTC, Namespace wrote:
 The problem on a library NotNull struct is: nobody will use it, 
 because it requires NotNull on both sides, by the caller and by 
 the callee:
That's a lot of the benefit: the type system forces you to handle it sooner rather than later, so you can see where the invalid state came from.
     NotNull!A na = NotNull!A(new A());
This could be easily automated though with a helper function: NotNull!T create(T, Args...)(Args args) { return assumeNotNull(new T(args)); } // use it! notice that it is notnull already auto na = create!A(); And then you can just use it.
 Also it is far more readable and easier to write as a contract
That's what I tend to do now but the advantage of NotNull!() is catching it earlier.
Jan 16 2014
parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 16 January 2014 at 23:56:14 UTC, Adam D. Ruppe wrote:
 // use it! notice that it is notnull already
 auto na = create!A();
That's neat. Didn't Andrei mention something about wanting new to be removed from the language and replaced with a library solution using allocators? If that were the case, it'd be pretty easy to enforce that class allocators return NotNull!T. It'd get rid of nullable references without needing a breaking language change.
Jan 16 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 17 January 2014 at 00:03:21 UTC, Meta wrote:
 Didn't Andrei mention something about wanting new to be removed 
 from the language and replaced with a library solution using 
 allocators?
I'm not sure if he's on board, but that's something I've been wanting for a while. Probably won't happen though, the new operator has a lot of fans (I kinda like it myself and fear the massive amount of code breakage removing it would cause). But we could still use specialized alternatives - new being there doesn't prohibit using a create function.
Jan 16 2014
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 17 Jan 2014 00:17:58 +0000
schrieb "Adam D. Ruppe" <destructionator gmail.com>:

 On Friday, 17 January 2014 at 00:03:21 UTC, Meta wrote:
 Didn't Andrei mention something about wanting new to be removed 
 from the language and replaced with a library solution using 
 allocators?
I'm not sure if he's on board, but that's something I've been wanting for a while. Probably won't happen though, the new operator has a lot of fans (I kinda like it myself and fear the massive amount of code breakage removing it would cause). But we could still use specialized alternatives - new being there doesn't prohibit using a create function.
The state of NotNull without it being a language feature would still be a bit silly. A good implementation allows promotion to NotNull like this: void foo(Object o) { if (o !is null) { bar(o); } } void bar(NotNull!Object) {} Maybe it could be implemented like "if (__ctfe)". -- Marco
Jan 16 2014
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 17 January 2014 at 07:35:00 UTC, Marco Leise wrote:
 void foo(Object o) {
     if (o !is null) {
         bar(o);
     }
 }
My current NotNull draft allows this: http://arsdnet.net/dcode/notnull.d int b; int* bp = &b; if(auto a = bp.checkNull) { static assert(is(typeof(a == NotNull!(int*)))); } else { assert(0); } So the type of bp doesn't change like it might when built into the language but since we can do if(auto a = ...) it is pretty close. Thanks to Andrej Mitrovic for the checkNull code.
Jan 17 2014
prev sibling parent Michel Fortin <michel.fortin michelf.ca> writes:
On 2014-01-17 00:03:19 +0000, "Meta" <jared771 gmail.com> said:

 On Thursday, 16 January 2014 at 23:56:14 UTC, Adam D. Ruppe wrote:
 // use it! notice that it is notnull already
 auto na = create!A();
That's neat. Didn't Andrei mention something about wanting new to be removed from the language and replaced with a library solution using allocators? If that were the case, it'd be pretty easy to enforce that class allocators return NotNull!T. It'd get rid of nullable references without needing a breaking language change.
In one C++ project of mine I have a make_new< T >(args...) template function that does exactly that. Pretty neat. The biggest downside to such an approach is that you're creating your own meta-language on top of the regular language (replacing the language syntax with your own). But it cannot really be avoided if you want NotNull to be usable. The next language facility you might have to duplicate is the cast (to keep the not-null "qualifier" when casting). Then maybe you'll want it to play nice with other the other "qualifier" type template Rebindable. -- Michel Fortin michel.fortin michelf.ca http://michelf.ca
Jan 16 2014
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-16 16:57, Adam D. Ruppe wrote:

 Brothers and sisters, I've talked to you before about doing custom
 RTInfo using __traits(compiles) tricks to look for the config module,
 but my previous ways - while they worked - sucked because the compiler
 would look for the module over and over again, slowing things down a bit.

 This new method achieves success on all counts. By offering a file with
 druntime to be the module up front, the compiler does no special
 searching, it finds it. It is a trivial file, so impact on compile speed
 is negligible.

 But then, if you offer your own file with the same module name on the
 command line, dmd looks THERE first, finds your special code, and knows
 to skip searching the import path! Even true for object.d, since this is
 all in templates with local imports; the template still needs to be
 instantiated.
Does this only work once? Say I have two libraries that use this technique, would that work? This seems somewhat complicated, depending on the orders of modules passed to the compiler.
 Since this object.d doesn't actually store anything* in the RTInfo
 itself, there's no worry of binary incompatibilities resulting from
 separate compiliation with different rtinfo modules. It simply provides
 a hook into the types for checking.
No, not yet. As far as I know, the idea was to use RTInfo to collect and store type info for a precise GC.
 Using this, we can automatically check all kinds of per-project
 requirements. Don't want virtual functions in your class unless they are
 reviewed? Make your TypeCheck loop over them and look for a UDA, issuing
 a static assert if not there. Don't want references to un-owned mutable
 data? Ditto. Have a naming convention to check? Can do that too.


 I see lots of upsides, and the downsides from my last experiment with
 this I believe have been eliminated. Am I missing something? I wanna do
 a druntime pull request!
I have had a different idea for a solution to this problem for a while. That is, add a new manifest constant to object.d, called rtInfo. This will act like a UDA. Any template with the object.rtInfo UDA attached to itself will behave just as RTInfo does today. module object; enum rtInfo; rtInfo template RTInfo (T) { enum RTInfo = null; } module foo.check_virtual_methods; rtInfo template CheckVirtualMethods (T) { static if (!containsVirtualMethods!(T)) static assert(false, "Error, contains virtual methods"); enum CheckVirtualMethods = null; } An idea for storing data in TypeInfo is to create an associative array. The keys would be the fully qualified name of the template, in this case "foo.check_virtual_methods.CheckVirtualMethods". The values would be whatever the template evaluates to. There's one problem with this, the associative array would need to be built at runtime due to separate compilation. -- /Jacob Carlborg
Jan 16 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 16 January 2014 at 20:13:29 UTC, Jacob Carlborg 
wrote:
 Does this only work once? Say I have two libraries that use 
 this technique, would that work?
No, then you'd get complaints about the module being defined in two files. This would be a once-per-project thing. Libraries however could provide helper functions with instructions for the user to call them in their project file. For example module my.serialization; mixin template SerializationAdditionToTypeInfo(T) { Whatever serialize() { ..... } } and then the user would open up their magic file module core.config; // BTW this name is just because it was on my mind from another discussion with it for enabled features template CustomRtInfo(T) { import my.serialization; mixin SerializationAdditionToTypeInfo!T; // you can also do other library's stuff here } hmmm Vladimir might be right that the custom info thing would be better done as a mixin template, then we could throw all this right in there and maybe build the whole class. I've gotta try it, I'm not sure yet.
 No, not yet. As far as I know, the idea was to use RTInfo to 
 collect and store type info for a precise GC.
Yeah, RTInfo is one of the most potentially cool things that has sat untapped for a long time.
 I have had a different idea for a solution to this problem for 
 a while. That is, add a new manifest constant to object.d, 
 called rtInfo. This will act like a UDA. Any template with the 
 object.rtInfo UDA attached to itself will behave just as RTInfo 
 does today.
I think that would need compiler support though and might not work as well when compiling two files separately since then part of the rtinfo wouldn't get checked. True, here you'd have to include the config file each time you compile a file, but at least that bit is centralized so easier to manage.
 An idea for storing data in TypeInfo is to create an 
 associative array. The keys would be the fully qualified name 
 of the template, in this case 
 "foo.check_virtual_methods.CheckVirtualMethods". The values 
 would be whatever the template evaluates to. There's one 
 problem with this, the associative array would need to be built 
 at runtime due to separate compilation.
Yeah, I've played with this idea before too, it is definitely doable today.
Jan 16 2014
parent Jacob Carlborg <doob me.com> writes:
On 2014-01-17 01:04, Adam D. Ruppe wrote:

 No, then you'd get complaints about the module being defined in two
 files. This would be a once-per-project thing.

 Libraries however could provide helper functions with instructions for
 the user to call them in their project file. For example

 module my.serialization;
 mixin template SerializationAdditionToTypeInfo(T) {
      Whatever serialize() { ..... }
 }

 and then the user would open up their magic file

 module core.config; // BTW this name is just because it was on my mind
 from another discussion with it for enabled features

 template CustomRtInfo(T) {
      import my.serialization;
      mixin SerializationAdditionToTypeInfo!T;

      // you can also do other library's stuff here
 }
Ok, I see.
 Yeah, RTInfo is one of the most potentially cool things that has sat
 untapped for a long time.
You don't want to remove this possibility. Storing the data in type info is a must, for this use case.
 I think that would need compiler support though
Yes, compiler support is necessary. But adding something like a standard core.config module would be similar.
 and might not work as  well when compiling two files separately since then
part of the rtinfo
 wouldn't get checked. True, here you'd have to include the config file
 each time you compile a file, but at least that bit is centralized so
 easier to manage.
I was thinking that for most cases, even when doing separate complication, the files would be included via the -I flag as "header" files. With my suggestion you don't need to modify your own CustomRtInfo template. Just make sure the file containing rtInfo is processed one way or an other by the compiler. It also doesn't depend on the order of the modules. We already have some linker problems with because of this. It don't think it's a good idea to add "a new feature" that depends on this. -- /Jacob Carlborg
Jan 17 2014
prev sibling next sibling parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Thu, 16 Jan 2014 15:57:04 +0000
schrieb "Adam D. Ruppe" <destructionator gmail.com>:

 Check this out. Modify your druntime slightly. In object.di, find 
 template RTInfo(T) and add this bit:
 
      import core.config;
      alias checkResult = TypeCheck!T;
 
 Then, pop open import/core/config.d and put these contents in it:
 
 module core.config;
 template TypeCheck(T) {
      enum TypeCheck = true;
 }
 
 
 Compile a program, any program. Should be no difference in 
 anything - no errors, no changes to the binary, no compile 
 slowdown - from before the modifications.
 
 
 Now, create a file named anything you want with these contents:
 
 module core.config;
 template TypeCheck(T) {
    static if(T.stringof == "Cool")
      static assert(0, "No type named 'Cool' is allowed!");
    enum TypeCheck = true;
 }
 
 
 Compile your program, should still work. Then add "struct Cool{}" 
 to it anywhere... and you get a compile error.
 
 
 
 Brothers and sisters, I've talked to you before about doing 
 custom RTInfo using __traits(compiles) tricks to look for the 
 config module, but my previous ways - while they worked - sucked 
 because the compiler would look for the module over and over 
 again, slowing things down a bit.
 
 This new method achieves success on all counts. By offering a 
 file with druntime to be the module up front, the compiler does 
 no special searching, it finds it. It is a trivial file, so 
 impact on compile speed is negligible.
 
 But then, if you offer your own file with the same module name on 
 the command line, dmd looks THERE first, finds your special code, 
 and knows to skip searching the import path! Even true for 
 object.d, since this is all in templates with local imports; the 
 template still needs to be instantiated.
 
 Since this object.d doesn't actually store anything* in the 
 RTInfo itself, there's no worry of binary incompatibilities 
 resulting from separate compiliation with different rtinfo 
 modules. It simply provides a hook into the types for checking.
 
 Using this, we can automatically check all kinds of per-project 
 requirements. Don't want virtual functions in your class unless 
 they are reviewed? Make your TypeCheck loop over them and look 
 for a UDA, issuing a static assert if not there. Don't want 
 references to un-owned mutable data? Ditto. Have a naming 
 convention to check? Can do that too.
 
 
 I see lots of upsides, and the downsides from my last experiment 
 with this I believe have been eliminated. Am I missing something? 
 I wanna do a druntime pull request!
 
 
 * User data extensions could potentially be allowed if RTInfo 
 stored a pointer to an interface, which may be null. Other 
 modules may implement the interface differently, but as long as 
 they implement it (or set it to null), it should still have 
 consistent binary compatibility.
 
 You might make object.d's RTInfo create a static struct with 
 information the GC needs first, then a pointer to the user 
 defined data. The RTInfo template ultimately resolves to the 
 pointer to that struct. The data itself is consistent - still 
 defined in only one place, druntime itself, and the GC info is 
 available right there, no added indirection, and we have the hook 
 for user additions too, all under a consistent interface.
 
 You can also add runtime info about a type using static 
 constructors:
 ===
 module core.config;
 
 string[] types;
 
 template TypeCheck(T) {
      enum TypeCheck = true;
 
      shared static this() {
          types ~= T.mangleof;
      }
 }
 ===
 module test500.d
 struct Cool {}
 
 void main() {
 	import core.config, std.stdio;
 	writeln(types);
 }
 ===
 dmd test500.d rtinfo.d
 
 $ ./test500
 ["S7test5004Cool", 
 "S3std5array17__T8AppenderTAyaZ8Appender4Data", 
 "S3std5array17__T8AppenderTAyaZ8Appender"]
 
 
 Run time info available about all the types used in that program!
 
 
 Soooo yeah, this is pretty much a pure win all around to my eyes. 
 Am I blind to the suck?
This solve quite a few issues at once! It looks like stuff that could only be done with compiler extensions or AST manipulations and yet it works with a simple template. Just for the neat "once in a lifetime" idea it should be accepted. :) -- Marco
Jan 16 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 16 January 2014 at 20:16:59 UTC, Marco Leise wrote:
 It looks like
 stuff that could only be done with compiler extensions or AST
 manipulations and yet it works with a simple template.
Yeah, though remember that it can't actually manipulate the type, it just looks and can build up other data or issue errors, but not change the code it is looking at.
Jan 16 2014
parent Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 17 Jan 2014 00:15:51 +0000
schrieb "Adam D. Ruppe" <destructionator gmail.com>:

 On Thursday, 16 January 2014 at 20:16:59 UTC, Marco Leise wrote:
 It looks like
 stuff that could only be done with compiler extensions or AST
 manipulations and yet it works with a simple template.
Yeah, though remember that it can't actually manipulate the type, it just looks and can build up other data or issue errors, but not change the code it is looking at.
Right. It did get me interested in how UDAs work though, since I never really looked at them when they were introduced. The compiler can cope with a lot of template magic I throw at it now. I'm sure I would have had a dozen errors already with 2.063. I'm investigating "data driven" library bindings. E.g. some template contains function pointers with UDAs that describe how wrappers are to be generated that turn getError() calls into D exceptions and turn D strings into C char* and similar tricks, like handling API changes from one version to the next. -- Marco
Jan 16 2014
prev sibling parent "Tove" <tove fransson.se> writes:
On Thursday, 16 January 2014 at 15:57:05 UTC, Adam D. Ruppe wrote:
 Soooo yeah, this is pretty much a pure win all around to my 
 eyes. Am I blind to the suck?
Wow, great trick! I'd love to see this merged.
Jan 16 2014