www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Dlang equivalent of #define/#ifdef : not... version

reply ichneumwn <idonotenjoyemail idonotenjoyemail.org> writes:
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
Apr 20 2021
next sibling parent mw <mingwu gmail.com> writes:
On Tuesday, 20 April 2021 at 18:57:46 UTC, ichneumwn wrote:

 So my questions:
 - is there a module-crossing equivalent of "version"?
https://dub.pm/package-format-json.html Configurations / versions { ... "name": "somepackage", "configurations": [ { "name": "metro-app", "targetType": "executable", "platforms": ["windows"], "versions": ["MetroApp"], "libs": ["d3d11"] }, { "name": "desktop-app", "targetType": "executable", "platforms": ["windows"], "versions": ["DesktopApp"], "libs": ["d3d9"] }, { "name": "glut-app", "targetType": "executable", "versions": ["GlutApp"] } ] }
Apr 20 2021
prev sibling next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 20 April 2021 at 18:57:46 UTC, ichneumwn wrote:
 So my questions:
 - is there a module-crossing equivalent of "version"?
mw covered this. There's more documentation here: https://dlang.org/spec/version.html#version https://dlang.org/dmd-windows.html#switch-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!() ?
__traits(hasMember, features, "i_am_not_a_feature") This is actually what std.traits.hasMember does, but for some reason it can't take a module as its first argument. -- Simen
Apr 20 2021
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 4/20/21 3:03 PM, Simen Kj=C3=A6r=C3=A5s wrote:
 On Tuesday, 20 April 2021 at 18:57:46 UTC, ichneumwn wrote:
 So my questions:
 - is there a module-crossing equivalent of "version"?
=20 mw covered this. There's more documentation here:=20 https://dlang.org/spec/version.html#version https://dlang.org/dmd-windows.html#switch-version =20 =20
 - if not, is there some way I could test for the existence of the enum=
=20
 can_i_test_for_this? A SymbolExists!() or ModuleHasSymbol!() or=20
 ModuleHasMember!() ?
=20 __traits(hasMember, features, "i_am_not_a_feature") =20 This is actually what std.traits.hasMember does, but for some reason it=
=20
 can't take a module as its first argument.
=20
 --=20
  =C2=A0 Simen
Could it be because __MODULE__ is string, requiring mixin? The following = prints 'true': enum i_am_not_a_feature =3D 42; pragma(msg, __traits(hasMember, mixin(__MODULE__), "i_am_not_a_feature"))= ; Ali
Apr 20 2021
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/20/21 2:57 PM, 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
enum i_am_a_feature = true;
 
 The C-code uses this define as a guard:
 
     .c : #ifdef i_am_a_feature
static if(i_am_a_feature) { ... }
 So my questions:
 - is there a module-crossing equivalent of "version"?
enum isn't exactly "module crossing", but it's defined within the module, which means you can look at it from elsewhere.
 - 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!() ?
This is surprisingly tricky. A template will not accept a symbol by alias that doesn't exist, and if you pass in a string and test, it will test if that template can see the symbol, not your code. You can probably do it inline: static if(__traits(compiles, {alias x = doesThisExist;})); -Steve
Apr 20 2021
parent reply Dukc <ajieskola gmail.com> writes:
On Tuesday, 20 April 2021 at 23:58:46 UTC, Steven Schveighoffer 
wrote:
 static if(i_am_a_feature) {
    ...
 }
This would be correct if `i_am_a_feature` would be always defined, just set to `false` if not existent. But I think the idea was to not define the symbol at all if feature does not exists. In that case, the condition should be `static if(is(typeof(i_am_a_feature)))`.
Apr 20 2021
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/21/21 2:38 AM, Dukc wrote:
 On Tuesday, 20 April 2021 at 23:58:46 UTC, Steven Schveighoffer wrote:
 static if(i_am_a_feature) {
    ...
 }
This would be correct if `i_am_a_feature` would be always defined, just set to `false` if not existent. But I think the idea was to not define the symbol at all if feature does not exists. In that case, the condition should be `static if(is(typeof(i_am_a_feature)))`.
Yeah, you're right. It's different than ifdef. I think version is the closest thing. Note that you can convert versions into enums via: version(define_the_thing) { enum the_thing_is_defined = 1; } else { enum the_thing_is_defined = 0; } -Steve
Apr 21 2021
prev sibling parent ichneumwn <idonotenjoyemail idonotenjoyemail.org> writes:
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!
Apr 21 2021