www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - new discovery: import only if available

reply "Adam D. Ruppe" <destructionator gmail.com> writes:
I watched the shared libraries in D video earlier today and one 
of the things mentioned was using a library only if it is 
available, and it rekindled something I wanted to do a while ago 
and couldn't: expand a module if and only if some other module is 
available.

Well, I could do it, but it meant using -versions. I'd prefer it 
if it worked with just listing the module.

But I think I have a solution now:

template moduleIsAvailable(string name) {
	enum moduleIsAvailable =
		mixin("__traits(compiles, { import " ~ name ~ "; } )")
		? true : false;
}

         pragma(msg, moduleIsAvailable!"test");
         pragma(msg, moduleIsAvailable!"not.existing");

It returned true and false, so cool. If a module is available 
though, this test actually imports it and if you don't add its 
object file, you'll get a linker error:

$ dmd test11
true
false
test11.o:(.data+0x50): undefined reference to 
`_D4test12__ModuleInfoZ'


But that might be ok, since if it is available, you'll probably 
be using it anyway - if not, why would you test? Though it would 
be cool if there was some kind of technique we could use such 
that it only pulls if the module is actually used elsewhere, like 
a low priority linker symbol or something. But I don't know about 
that.


Anyway, with this, you can wrap the usage of a module inside a 
static if(moduleIsAvailable) {} and offer some magical expansion 
of interoperability (though, granted, this kind of thing might be 
best put into a third module anyway), or a graceful degradation 
if you want a file to combine with a library and be standalone at 
the same time.


The -version method might be better anyway though, since it is 
more explicit.... so I'm on the fence as to if I should actually 
use this. What do you all think? Bad idea or worth doing to have 
one file be both standalone and interconnected at the same time?
Jun 18 2013
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
whoa, I was just about go to bed, but turned the computer on 
because I just thought of one case where I think I want to do 
this: config files.

For my work apps, if I need config I always do them as a module:

config_a.d
===
module application.config;

enum featureA = true;
string appName = "Acme Software";
===


The selective module magic could be used to offer a default 
configuration, with the import overriding it if available:

static if(importIsAvailable!"app.config") {
    import app.config;
} else {
    enum featureA = false;
    string appName= "My App";
}

. That's potentially way cool.
Jun 18 2013
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 19 Jun 2013 04:37:10 +0200, Adam D. Ruppe  
<destructionator gmail.com> wrote:

 If a module is available though, this test actually imports it and if  
 you don't add its object file, you'll get a linker error:

 $ dmd test11
 true
 false
 test11.o:(.data+0x50): undefined reference to `_D4test12__ModuleInfoZ'

As you say this might be acceptable, but I would still call it a bug - it's complaining about symbols that have no business being in the obj file. -- Simen
Jun 18 2013
prev sibling next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
On Jun 18, 2013, at 7:37 PM, Adam D. Ruppe <destructionator gmail.com> =
wrote:
=20
 It returned true and false, so cool. If a module is available though, =

you'll get a linker error:
=20
 $ dmd test11
 true
 false
 test11.o:(.data+0x50): undefined reference to `_D4test12__ModuleInfoZ'
=20
=20
 But that might be ok, since if it is available, you'll probably be =

there was some kind of technique we could use such that it only pulls if = the module is actually used elsewhere, like a low priority linker symbol = or something. But I don't know about that. rdmd?=
Jun 19 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/19/13 3:42 PM, Adam D. Ruppe wrote:
 On Wednesday, 19 June 2013 at 19:20:15 UTC, Sean Kelly wrote:
 rdmd?

That would pull it in here. What I mean is if: module a: "weakly" imports module c module b: imports module c Module c is available, but: dmd a.d c.d explicitly uses module c, so it is pulled. dmd a.d only uses module a, because module c isn't used elsewhere and isn't explicitly pulled in. dmd a.d b.d does use module c, because it is required anyway by module b.

Yah, you'd need something like rdmd **/*.d. Andrei
Jun 19 2013
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 19 June 2013 at 19:20:15 UTC, Sean Kelly wrote:
 rdmd?

That would pull it in here. What I mean is if: module a: "weakly" imports module c module b: imports module c Module c is available, but: dmd a.d c.d explicitly uses module c, so it is pulled. dmd a.d only uses module a, because module c isn't used elsewhere and isn't explicitly pulled in. dmd a.d b.d does use module c, because it is required anyway by module b.
Jun 19 2013
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Wed, 19 Jun 2013 04:37:10 +0200
"Adam D. Ruppe" <destructionator gmail.com> wrote:
 
 template moduleIsAvailable(string name) {
 	enum moduleIsAvailable =
 		mixin("__traits(compiles, { import " ~ name ~ "; } )")
 		? true : false;
 }
 
          pragma(msg, moduleIsAvailable!"test");
          pragma(msg, moduleIsAvailable!"not.existing");
 

I do that "__traits(compiles, {import blah;})" trick too, although I've never generalized it. Nice util. I find the trick useful for generating a clean error message if something is missing, like a configuration file written in D, or a -version that introduces a dependency on a certain external module but doesn't find it.
Jun 19 2013
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
OMG guys this finishes the druntime extension problem!


Try it yourself, open up dmd2/src/druntime/import/object.di and 
find template RTInfo.

Change it to this:

template RTInfo(T)
{
     static if(__traits(compiles, { import druntime.extensions; 
auto a =  druntime.extensions.    RTInfo!T; })) {
         import druntime.extensions;
          enum RTInfo = druntime.extensions.RTInfo!T;
     } else
         enum RTInfo = cast(void*)0x12345678;
}



Make a test program with just "struct Test {} "

And a helper file with contents:

===
module druntime.extensions;

template RTInfo(T) {
         static if(T.stringof == "Test") {
                 pragma(msg, "Here!");
         }

         enum RTInfo = null;
}
==



Compile without extensions:

$ dmd test23 -main
$


No messages, no errors, just as it should be.


Now add our helper file to the command line....


$ dmd test23 -main dext.d
Here!
$


It triggered! Boom, we have the potential for *project-wide* 
modifications to RTInfo without modifying druntime. Combine this 
with the other techniques I've talked about for rtinfo and we 
have it all.

You wouldn't necessarily have to add it specifically to the 
command line btw, you could put it in a file called 
druntime/extensions.d in your import directory as well, 
especially since it is a template.
Jun 21 2013
parent Rainer Schuetze <r.sagitario gmx.de> writes:
On 21.06.2013 14:41, Adam D. Ruppe wrote:
 OMG guys this finishes the druntime extension problem!


 Try it yourself, open up dmd2/src/druntime/import/object.di and find
 template RTInfo.

 Change it to this:

 template RTInfo(T)
 {
      static if(__traits(compiles, { import druntime.extensions; auto a
 =  druntime.extensions.    RTInfo!T; })) {
          import druntime.extensions;
           enum RTInfo = druntime.extensions.RTInfo!T;
      } else
          enum RTInfo = cast(void*)0x12345678;
 }

I guess you should move the "static if" outside of the template to avoid having it evaluated for every type. This can be pretty expensive as it includes a file search every time if it fails.
 It triggered! Boom, we have the potential for *project-wide*
 modifications to RTInfo without modifying druntime. Combine this with
 the other techniques I've talked about for rtinfo and we have it all.

 You wouldn't necessarily have to add it specifically to the command line
 btw, you could put it in a file called druntime/extensions.d in your
 import directory as well, especially since it is a template.

I was considering something similar, maybe this could even be extended to allow combining multiple extensions to work side-by-side. I'm unsure though if this is too brittle with respect to linking something together that might be compiled with or without these extensions.
Jun 21 2013
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Fri, 21 Jun 2013 14:41:29 +0200, Adam D. Ruppe  
<destructionator gmail.com> wrote:

 OMG guys this finishes the druntime extension problem!

That is awesome. Make a pull request. -- Simen
Jun 21 2013
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 21 June 2013 at 18:47:24 UTC, Rainer Schuetze wrote:
 I guess you should move the "static if" outside of the template 
 to avoid having it evaluated for every type. This can be pretty 
 expensive as it includes a file search every time if it fails.

Probably.... also the __traits(compiles) in there is a mistake, because that breaks any static assert(0)'s in the extension code, so want to take that out too. The other thing I'm thinking is maybe it should be a mixin template or something, because if it is supposed to be an extension, we don't want it completely replacing what druntime has. It should just add to it. This might need a cast and/or some indirection to keep straight through typeinfo.rtInfo() though.
 I'm unsure though if this is too brittle with respect to 
 linking something together that might be compiled with or 
 without these extensions.

The linker would probably complain about the symbol changing sizes if you did it wrong, There'd be some brittleness but maybe that's ok because not everyone would be tweaking this anyway.
Jun 21 2013