www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - thisExePath purity

reply crimaniak <crimaniak gmail.com> writes:
Hi!

Is there situations when output of thisExePath() can be different 
during runtime? If yes, what the reason?
If no, is this possible to mark it as pure in phobos?

https://dlang.org/library/std/file/this_exe_path.html
Sep 19 2016
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 20 September 2016 at 00:01:58 UTC, crimaniak wrote:
 Hi!

 Is there situations when output of thisExePath() can be 
 different during runtime? If yes, what the reason?
 If no, is this possible to mark it as pure in phobos?

 https://dlang.org/library/std/file/this_exe_path.html
No way to do that. It does I/O. However you cheat. look for assumePure in https://dlang.org/phobos/std_traits.html and then you can create a wrapper for it that is fake pure.
Sep 19 2016
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Tuesday, September 20, 2016 00:37:10 Stefan Koch via Digitalmars-d-learn 
wrote:
 On Tuesday, 20 September 2016 at 00:01:58 UTC, crimaniak wrote:
 Hi!

 Is there situations when output of thisExePath() can be
 different during runtime? If yes, what the reason?
 If no, is this possible to mark it as pure in phobos?

 https://dlang.org/library/std/file/this_exe_path.html
No way to do that. It does I/O. However you cheat. look for assumePure in https://dlang.org/phobos/std_traits.html and then you can create a wrapper for it that is fake pure.
Yes, you can cast a function pointer to force purity, but that's almost always a bad idea. It makes far more sense to just grab the value once and reuse it. The only time that I'd suggest using the casting trick for something like this would be when you can't afford to use static constructors, and you can't afford to pass the value around. Casting to pure should be a tool of last resort. It _is_ an option though if you really have no reasonable alternative. Another thing to consider in this case is that casting like that would actually be needlessly expensive if he actually needs to make the call more than once. It would be far cheaper to just save the result and reuse it. And if it's stored in an immutable module-level or static variable, then it can be used in a pure function. - Jonathan M Davis
Sep 19 2016
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/19/16 8:01 PM, crimaniak wrote:
 Hi!

 Is there situations when output of thisExePath() can be different during
 runtime? If yes, what the reason?
 If no, is this possible to mark it as pure in phobos?

 https://dlang.org/library/std/file/this_exe_path.html
Not in a way that D can ensure purity. Yes, it will not change. But that guarantee is not communicated via the OS functions required to call to get the information. One thing you can do: immutable string myPath; shared static this() { import std.file: thisExePath; myPath = thisExePath; } // now use myPath as required from pure functions. -Steve
Sep 19 2016
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Tuesday, September 20, 2016 00:01:58 crimaniak via Digitalmars-d-learn 
wrote:
 Hi!

 Is there situations when output of thisExePath() can be different
 during runtime? If yes, what the reason?
 If no, is this possible to mark it as pure in phobos?

 https://dlang.org/library/std/file/this_exe_path.html
In principle, it should be impossible for it to change while the program is running. However, what Phobos has to do to get the information can't be pure. In the best case, it involves calling C functions that we can reasonably assume will be pure but can't technically guarantee will be, but in some cases, it actually involves querying the file system (e.g. on Linux, it reads "/proc/self/exe"), which definitely can't be pure, because it involves I/O. If you really want a pure way to deal with this, then I'd suggest grabbing the value at startup and setting it to an immutable variable. e.g. something like immutable string executablePath; shared static this() { import std.file : thisExePath(); executablePath = thisExePath(); } and then you can use that variable in pure code, because it's guaranteed not to change. - Jonathan M Davis
Sep 19 2016
parent reply crimaniak <crimaniak gmail.com> writes:
Hi and thanks all!

On Tuesday, 20 September 2016 at 00:43:10 UTC, Jonathan M Davis 
wrote:

 immutable string executablePath;

 shared static this()
 {
     import std.file : thisExePath();
     executablePath = thisExePath();
 }
This code is good for my needs but I start to think about how to call thisExePath only if it is really used and come to this solution: import std.traits: ReturnType, Parameters; string staticMemoize(alias T, Parms = Parameters!T)() pure { struct Holder(alias T) { static shared immutable ReturnType!T value; shared static this(){ value = T(Parms); } } return Holder!T.value; } unittest { import std.file : thisExePath; assert(staticMemoize!thisExePath == thisExePath); } Something like this. Need to refine about input parameters, but I hope, idea is clear. Unlike the function memoize from phobos staticMemoize really pure. And unlike proposed solution with ordinary variable staticMemoize is lazy, because no call - no instantiation. https://dlang.org/library/std/functional/memoize.html
Sep 19 2016
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Tuesday, September 20, 2016 04:17:21 crimaniak via Digitalmars-d-learn 
wrote:
          static shared immutable ReturnType!T value;
I would point out that immutable is implicitly shared, so there's no reason to put shared on an immutable variable. However, you _do_ want to put shared on a static constructor that initializes an immutable variable so that it's only run once for the program instead of once per thread (the compiler really should enforce that, but there's a longstanding bug that allows you to reinitialize an immutable variable by not putting shared on the static constructor and starting multiple threads). - Jonathan M Davis
Sep 19 2016
parent crimaniak <crimaniak gmail.com> writes:
On Tuesday, 20 September 2016 at 04:26:05 UTC, Jonathan M Davis 
wrote:
 On Tuesday, September 20, 2016 04:17:21 crimaniak via 
 Digitalmars-d-learn wrote:
          static shared immutable ReturnType!T value;
I would point out that immutable is implicitly shared, so there's no reason to put shared on an immutable variable. However, you _do_ want to put shared on a static constructor that initializes an immutable variable so that it's only run once for the program instead of once per thread (the compiler really should enforce that, but there's a longstanding bug that allows you to reinitialize an immutable variable by not putting shared on the static constructor and starting multiple threads).
Ok, I got it. Thanks.
Sep 20 2016
prev sibling next sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Tuesday, 20 September 2016 at 04:17:21 UTC, crimaniak wrote:
 Hi and thanks all!

 On Tuesday, 20 September 2016 at 00:43:10 UTC, Jonathan M Davis 
 wrote:

 immutable string executablePath;

 shared static this()
 {
     import std.file : thisExePath();
     executablePath = thisExePath();
 }
This code is good for my needs but I start to think about how to call thisExePath only if it is really used and come to this solution: import std.traits: ReturnType, Parameters; string staticMemoize(alias T, Parms = Parameters!T)() pure { struct Holder(alias T) { static shared immutable ReturnType!T value; shared static this(){ value = T(Parms); } } return Holder!T.value; } unittest { import std.file : thisExePath; assert(staticMemoize!thisExePath == thisExePath); } Something like this. Need to refine about input parameters, but I hope, idea is clear. Unlike the function memoize from phobos staticMemoize really pure. And unlike proposed solution with ordinary variable staticMemoize is lazy, because no call - no instantiation.
Have a look at `std.concurrency.initOnce`: https://dlang.org/phobos/std_concurrency.html#.initOnce But you will still need to use assumePure() for calling `thisExePath`, and it might do other things that are impure...
Sep 20 2016
parent crimaniak <crimaniak gmail.com> writes:
On Tuesday, 20 September 2016 at 09:14:39 UTC, Marc Schütz wrote:
 Have a look at `std.concurrency.initOnce`:
 https://dlang.org/phobos/std_concurrency.html#.initOnce

 But you will still need to use assumePure() for calling 
 `thisExePath`, and it might do other things that are impure...
Yes, it's near but in this case I try to fix purity, so any variants of lazy initialization is not applicable here.
Sep 20 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/20/16 12:17 AM, crimaniak wrote:
 Hi and thanks all!

 On Tuesday, 20 September 2016 at 00:43:10 UTC, Jonathan M Davis wrote:

 immutable string executablePath;

 shared static this()
 {
     import std.file : thisExePath();
     executablePath = thisExePath();
 }
This code is good for my needs but I start to think about how to call thisExePath only if it is really used and come to this solution: import std.traits: ReturnType, Parameters; string staticMemoize(alias T, Parms = Parameters!T)() pure { struct Holder(alias T) { static shared immutable ReturnType!T value; shared static this(){ value = T(Parms); } } return Holder!T.value; } unittest { import std.file : thisExePath; assert(staticMemoize!thisExePath == thisExePath); } Something like this. Need to refine about input parameters, but I hope, idea is clear. Unlike the function memoize from phobos staticMemoize really pure. And unlike proposed solution with ordinary variable staticMemoize is lazy, because no call - no instantiation.
Yes, but if your code does instantiate it, it is called, even if you don't ever call the function that calls it. Note that if you don't import the module that contains the static ctor, it should be trimmed by the linker. I would absolutely caution you from putting static this() inside any template. Unfortunately, due to the way D generates these static constructors, any module that uses staticMemoize, or *imports a module that uses it*, will be marked as having a static constructor, and will potentially create cycles. -Steve
Sep 20 2016
parent reply crimaniak <crimaniak gmail.com> writes:
On Tuesday, 20 September 2016 at 13:35:27 UTC, Steven 
Schveighoffer wrote:
 Yes, but if your code does instantiate it, it is called, even 
 if you don't ever call the function that calls it.
Yes, it's not ideal but better then just global variable and static block - it's called in any case, even if variable is not used at all. Ideal solution will be something like attribute for static block leading to make it optional, so module will not be included if no usage of other symbols found. But I don't know way how to make it so template is used.
 Note that if you don't import the module that contains the 
 static ctor, it should be trimmed by the linker.
Let's imagine linker can trim even imported module with static ctor, if we have something like: immutable string executablePath; local shared static this() { import std.file : thisExePath; executablePath = thisExePath(); } and there is no references to executablePath. Here it would be useful, I think. Attribute local (or module? the name does not matter) mean this block used only to init other symbols in this module so it can be skipped if no references.
 I would absolutely caution you from putting static this() 
 inside any template. Unfortunately, due to the way D generates 
 these static constructors, any module that uses staticMemoize, 
 or *imports a module that uses it*, will be marked as having a 
 static constructor, and will potentially create cycles.
Please be more detail about cycles. Do you mean something like this? https://isocpp.org/wiki/faq/ctors#static-init-order
Sep 20 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/20/16 3:42 PM, crimaniak wrote:
 On Tuesday, 20 September 2016 at 13:35:27 UTC, Steven Schveighoffer wrote:
 Note that if you don't import the module that contains the static
 ctor, it should be trimmed by the linker.
Let's imagine linker can trim even imported module with static ctor, if we have something like: immutable string executablePath; local shared static this() { import std.file : thisExePath; executablePath = thisExePath(); } and there is no references to executablePath. Here it would be useful, I think. Attribute local (or module? the name does not matter) mean this block used only to init other symbols in this module so it can be skipped if no references.
But if this is all that is in the module, why import the module if you aren't going to use any of it?
 I would absolutely caution you from putting static this() inside any
 template. Unfortunately, due to the way D generates these static
 constructors, any module that uses staticMemoize, or *imports a module
 that uses it*, will be marked as having a static constructor, and will
 potentially create cycles.
Please be more detail about cycles. Do you mean something like this? https://isocpp.org/wiki/faq/ctors#static-init-order
Sort of, I mean this: https://dlang.org/spec/module.html#order_of_static_ctor In other words, because the template has a static ctor in it, just instantiating it in your unrelated module puts static ctor in your module. And then cycles can appear that you wouldn't expect to happen. -Steve
Sep 21 2016