Dlang equivalent of #define/#ifdef : not... version

ichneumwn idonotenjoyemail at idonotenjoyemail.org
Wed Apr 21 20:16:30 UTC 2021


On Tuesday, 20 April 2021 at 18:57:46 UTC, ichneumwn wrote:
> Hi,
>
> Trying to convert a C header to D. The underlying package 
> exists in different versions and may or may not expose certain 
> functionality (modules/extensions). One should check at compile 
> time that the header provides the relevant definitions, through 
> #ifdef, and do a further run-time check to confirm the 
> functionality is really present. It is the compile time check 
> that I am finding tricky to do/emulate.
>
>     .h : #define i_am_a_feature 1
>
> The C-code uses this define as a guard:
>
>     .c : #ifdef i_am_a_feature
>
> What would the the D equivalent? This is my attempt so far:
>
> features.d:
>
>     import std.traits;
>
>     private enum capabilities {
>         i_am_a_feature
>     }
>
>     template supported(string member)
>     {
>         enum bool supported = hasMember!(capabilities, member);
>     }
>
>     version = vs_i_am_a_feature;
>
>     enum can_i_test_for_this;
>
> and use.d:
>
>     import features;
>     import std.stdio;
>
>     void main() {
>         static if (supported!("i_am_a_feature")) {
>             writeln("Feature 1!");
>         }
>         static if (supported!("i_am_not_a_feature")) {
>             writeln("Feature 2!");
>         }
>         version(vs_i_am_a_feature) {
>             writeln("If only I worked");
>         }
>     }
>
> This produces "Feature 1!", so the supported() path works, but 
> is a bit of a round-about way and all "capability flags" need 
> to put in that single enum capabilities instead of being 
> allowed to be scattered across the features.d module.
>
> As is documented, "version" does not cross the module boundary.
>
> So my questions:
> - is there a module-crossing equivalent of "version"?
> - if not, is there some way I could test for the existence of 
> the enum can_i_test_for_this? A SymbolExists!() or 
> ModuleHasSymbol!() or ModuleHasMember!() ?
>
> Cheers

Between them, Simen and Ali have cracked the case. Ali's 
demonstration that "true" was returned made me look at it further 
and tried the module's name directly and that worked too. It 
seems that Simen's expansion of hasMember() into the __traits 
version does the trick -- the __traits version *does* accept the 
module as a parameter. It seems the template expansion does not 
like module arguments, not __traits itself. Here is the demo:

features.d:

     import std.traits;

     enum can_sing = 1;
     enum can_dance = 1;

     template supported(string member) {
         enum bool supported = __traits(hasMember, features, 
member);
         // features can be replaced by: mixin(__MODULE__)
         // to make this module robust against renaming
     }
     /*
      * Note that using std.traits.hasMember *does not* work. 
Yields error:
      * template instance hasMember!(features, "can_sing") does 
not match template
      * declaration hasMember(T, string name)
      */

use.d:

     import features;
     import std.stdio;
     import std.traits;

     void main() {
         static if (supported!"can_sing") {
             writeln("can_sing");
         }
         static if (supported!("can_dance")) {
             writeln("can_dance");
         }
         static if (supported!("can_jump")) {
             writeln("can_jump");
         }
     }

Result:

     can_sing
     can_dance

I think the version with the mixin is neater, but I have put it 
in the direct "features" in the example to show it is not 
actually necessary.

Oh, I found a bonus option: passing the module as an argument. I 
do not really get templates beyond the very basic ones, but I 
remember seeing "alias" somewhere and that gets the module 
through my own template:

     // accept module as argument
     template supported(alias modname, string member) {
         enum bool supported = __traits(hasMember, modname, 
member);
     }
     // accept module name (string) as argument:
     template supported(string modname, string member) {
         enum bool supported = __traits(hasMember, mixin(modname), 
member);
     }

As I write this, I realise that the alias can also be used to get 
a version of hasMember that accepts modules:

     enum hasMember(alias T, string name) = __traits(hasMember, T, 
name);

Although that might have unintended side effects? And, indeed, 
shorten my own use case to:

     enum supported(string member) = __traits(hasMember, 
mixin(__MODULE__), member);


Many thanks to everyone with suggestions!



More information about the Digitalmars-d-learn mailing list