Thread overview
'version'-based code selection
Jun 01, 2019
Yatheendra
Jun 01, 2019
rikki cattermole
Jun 01, 2019
Jonathan M Davis
Jun 02, 2019
Yatheendra
Jun 02, 2019
Anonymouse
Jun 02, 2019
Jonathan M Davis
June 01, 2019
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)?
June 01, 2019
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.
June 01, 2019
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



June 02, 2019
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).
June 02, 2019
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.

June 02, 2019
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