www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - 'version'-based code selection

reply Yatheendra <3df4 gmail.ru> writes:
Hi people.

The 'version' keyword sounds like a fantastic capability, but how 
far does DMD take it (and does GDC take it equally far)? This is 
not a "D Improvement Proposal", I am just asking how it is now.

Can code of multiple versions be compiled into the same 
executable or library, and a particular one selected from 
"later"? I guess not without some name mangling and wrangling.

Can such selection be done at link-time for standard as well as 
user-defined library code? Maybe some library file naming 
conventions are followed to allow the compiler to select the 
corresponding file?

Wishful thinking, but can the selection be at runtime? That would 
be language support for making some things easy, e.g. picking 
from assembly-coded routines based on runtime CPU id (the video 
player MPlayer picks routines that way, and I read that the D 
library, Mir, has CPU id support).

Thanks.

P.S: I am a brand-new C++'y asylum seeker who may have missed 
seeing some documentation, so RTFM is a valid response.

P.P.S: This question was triggered by the D GSoC project for 
independent-of-C implementations of malloc/free/memcpy/memset. 
Could a common malloc be exposed to the D 
runtime/Phobos/programs, with the C or D implementations 
selectable at link-time (using a mechanism available to user code 
too)?
May 31 2019
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 01/06/2019 3:59 PM, Yatheendra wrote:
 
 Hi people.
 
 The 'version' keyword sounds like a fantastic capability, but how far 
 does DMD take it (and does GDC take it equally far)? This is not a "D 
 Improvement Proposal", I am just asking how it is now.
GDC, LDC and DMD all share the same frontend from DMD.
 Can code of multiple versions be compiled into the same executable or 
 library, and a particular one selected from "later"? I guess not without 
 some name mangling and wrangling.
It is not supported. Think #ifdef and you should get a basic understanding of its scope.
May 31 2019
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, May 31, 2019 9:59:13 PM MDT Yatheendra via Digitalmars-d-learn 
wrote:
 Hi people.

 The 'version' keyword sounds like a fantastic capability, but how
 far does DMD take it (and does GDC take it equally far)? This is
 not a "D Improvement Proposal", I am just asking how it is now.

 Can code of multiple versions be compiled into the same
 executable or library, and a particular one selected from
 "later"? I guess not without some name mangling and wrangling.

 Can such selection be done at link-time for standard as well as
 user-defined library code? Maybe some library file naming
 conventions are followed to allow the compiler to select the
 corresponding file?

 Wishful thinking, but can the selection be at runtime? That would
 be language support for making some things easy, e.g. picking
 from assembly-coded routines based on runtime CPU id (the video
 player MPlayer picks routines that way, and I read that the D
 library, Mir, has CPU id support).

 Thanks.

 P.S: I am a brand-new C++'y asylum seeker who may have missed
 seeing some documentation, so RTFM is a valid response.

 P.P.S: This question was triggered by the D GSoC project for
 independent-of-C implementations of malloc/free/memcpy/memset.
 Could a common malloc be exposed to the D
 runtime/Phobos/programs, with the C or D implementations
 selectable at link-time (using a mechanism available to user code
 too)?
Like static ifs, version statements are completely a compile-time construct and having nothing to do with runtime beyond how they affect the code that's generated. They also have nothing to do with linking beyond how they affect what code is generated. version statements are basically just static if statements that are compiled in if the corresponding version identifier has been defined. They're esentially D's answer to C's #ifdefs. A version statement can only check a single version identifier (so, no boolean logic like with #ifdefs), but else can be used like with static ifs. e.g. version(linux) { // compile in this code on Linux } else version(Windows) { // compile in this code on Windows } else static assert(false, "This platform is not supported."); Multiple version identifiers exist when compiling. For instance, if compiling on 64-bit x86 Linux, both the linux and X86_64 version identifiers would be defined. So, it's not like there's only one version identifier when compiling. Additional version identifiers can be supplied on the command-line when compiling, and you can even define a version for just within the module (version identifers cannot be imported). You don't technically need to compile each module in a program with the same set of version identifiers, but it's usually asking for trouble if you don't, because that can cause problems when a module is imported using one set of version identifiers but actually compiled with another (e.g. totaly different symbol definitions could be used depending on what was versioned in the module, leading to linker errors). For the most part though, you don't declare your own version identifiers. It sometimes makes sense, but usually, version identifiers are used for versioning code based on the platform or architecture that it's compiled on. They're really only intended to be a saner version of #ifdefs, and if you're doing anything fancy with them, you're really not using them as intended and are probably going to have problems. The list of predefined version identifiers can be found here: https://dlang.org/spec/version.html#predefined-versions - Jonathan M Davis
Jun 01 2019
next sibling parent Yatheendra <3df4 gmail.ru> writes:
On Saturday, 1 June 2019 at 07:46:40 UTC, Jonathan M Davis wrote:
 Like static ifs, version statements are completely a 
 compile-time construct and having nothing to do with runtime 
 beyond how they affect the code that's generated.
 ...
 - Jonathan M Davis
Thanks for taking the time. That's it then for selecting between malloc/free implementations at runtime (program start-up time, usually) in such a way. There might still be hope in the form of a separate dynamic library with re-implemented malloc, and equivalent wrapper API's around libc in another library (whole program, including Phobos, relying on the same malloc library).
Jun 02 2019
prev sibling parent reply Anonymouse <zorael gmail.com> writes:
On Saturday, 1 June 2019 at 07:46:40 UTC, Jonathan M Davis wrote:
 For the most part though, you don't declare your own version 
 identifiers. It sometimes makes sense, but usually, version 
 identifiers are used for versioning code based on the platform 
 or architecture that it's compiled on. They're really only 
 intended to be a saner version of #ifdefs, and if you're doing 
 anything fancy with them, you're really not using them as 
 intended and are probably going to have problems.
I use versioning pervasively to make features opt-in/opt-out at compile-time. Like so, from dub.json: "versions": [ "AsAnApplication", "WithAdminPlugin", "WithAutomodePlugin", "WithBashQuotesPlugin", "WithChanQueriesService", "WithChatbotPlugin", "WithConnectService", "WithCTCPService", "WithHelpPlugin", "WithNotesPlugin", "WithPersistenceService", "WithPipelinePlugin", "WithPrinterPlugin", "WithQuotesPlugin", "WithSedReplacePlugin", "WithSeenPlugin", "WithWebtitlesPlugin" ], --- module foo; version(WithFoo): // ... Is this recommended against? It's a very convenient way of enabling and disabling modules outright, since (by default) dub eagerly compiles everything it sees. I haven't had any problems with it as of yet, at the very least.
Jun 02 2019
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, June 2, 2019 11:43:09 AM MDT Anonymouse via Digitalmars-d-learn 
wrote:
 On Saturday, 1 June 2019 at 07:46:40 UTC, Jonathan M Davis wrote:
 For the most part though, you don't declare your own version
 identifiers. It sometimes makes sense, but usually, version
 identifiers are used for versioning code based on the platform
 or architecture that it's compiled on. They're really only
 intended to be a saner version of #ifdefs, and if you're doing
 anything fancy with them, you're really not using them as
 intended and are probably going to have problems.
I use versioning pervasively to make features opt-in/opt-out at compile-time. Like so, from dub.json: "versions": [ "AsAnApplication", "WithAdminPlugin", "WithAutomodePlugin", "WithBashQuotesPlugin", "WithChanQueriesService", "WithChatbotPlugin", "WithConnectService", "WithCTCPService", "WithHelpPlugin", "WithNotesPlugin", "WithPersistenceService", "WithPipelinePlugin", "WithPrinterPlugin", "WithQuotesPlugin", "WithSedReplacePlugin", "WithSeenPlugin", "WithWebtitlesPlugin" ], --- module foo; version(WithFoo): // ... Is this recommended against? It's a very convenient way of enabling and disabling modules outright, since (by default) dub eagerly compiles everything it sees. I haven't had any problems with it as of yet, at the very least.
Personally, I wouldn't be in favor of doing much in the way of enabling or disabling features in a library or application in such a manner, but if you're going to do it, then version identifiers would be appropriate. However, you do need to watch out, because such an approach runs the risk of problems if you end up with a project depending on libraries that depend on your library, because they may not be compiled with the same set of version identifiers. I'm not sure how dub handles that (probably by considering it a conflict), but without any form of conflict resolution (or you have a form of conflict resolution that doesn't catch this issue), you run the risk of modules being imported with one set of version identifiers but actually compiled with another. In some situations, that will result in a linker error, but in others, it's just going to result in the code not behaving as expected. It's less of an issue if the version identifiers are for an application rather than a library. Also, if you're versioning out entire modules rather than pieces of modules, you're likely to be less at risk of subtle problems, since then you'll just get large stuff missing, and linking will fail, whereas if you're using such version identifiers within code (e.g. changing the body of a function), then you run a high risk of subtle problems. Personally, if I were writing a library with optional stuff, I'd make the optional stuff into sub-modules and try to avoid using version identifiers. I might do it for an application though, since in that case, you wouldn't be dealing with sub-modules, and you're not dealing with anything depending on your code, just controlling what ends up in the executable. - Jonathan M Davis
Jun 02 2019