Jump to page: 1 2
Thread overview
Request assistance converting C's #ifndef to D
May 12, 2016
Andrew Edwards
May 12, 2016
H. S. Teoh
May 12, 2016
Andrew Edwards
May 13, 2016
Andrew Edwards
May 13, 2016
tsbockman
May 14, 2016
Andrew Edwards
May 13, 2016
Andrew Edwards
May 13, 2016
tsbockman
May 13, 2016
Andrew Edwards
May 13, 2016
Kagamin
May 13, 2016
sanjayss
May 13, 2016
The following preprocessor directives are frequently encountered in C code, providing a default constant value where the user of the code has not specified one:

	#ifndef MIN
	#define MIN     99
	#endif

	#ifndef MAX
	#define MAX     999
	#endif

I'm at a loss at how to properly convert it to D. I've tried the following:

	enum MIN = 0;
	static if(MIN <= 0)
	{
		MIN = 99;
	}

it works as long as the static if is enclosed in a static this(), otherwise the compiler complains:

	mo.d(493): Error: no identifier for declarator MIN
	mo.d(493): Error: declaration expected, not '='

This however, does not feel like the right way to do thinks but I cannot find any documentation that provides an alternative. Is there a better way to do this?

Thanks,
Andrew
May 12, 2016
On Fri, May 13, 2016 at 07:51:17AM +0900, Andrew Edwards via Digitalmars-d-learn wrote:
> The following preprocessor directives are frequently encountered in C code, providing a default constant value where the user of the code has not specified one:
> 
> 	#ifndef MIN
> 	#define MIN     99
> 	#endif
> 
> 	#ifndef MAX
> 	#define MAX     999
> 	#endif
> 
> I'm at a loss at how to properly convert it to D. I've tried the following:
> 
> 	enum MIN = 0;
> 	static if(MIN <= 0)
> 	{
> 		MIN = 99;
> 	}
[...]

That seems wrong. You can't assign to an enum. Besides, doesn't your declaration of MIN shadow whatever other definitions may be currently in effect?

Perhaps what you meant is something like this?

	static if (!is(typeof(MIN) : int))
		enum MIN = 99;

though I'm not sure if such a thing will actually work, since order-dependent declarations in D are a kind of dangerous territory to tread on.


T

-- 
What's an anagram of "BANACH-TARSKI"?
BANACH-TARSKI BANACH-TARSKI.
May 13, 2016
On 5/13/16 8:00 AM, H. S. Teoh via Digitalmars-d-learn wrote:
> On Fri, May 13, 2016 at 07:51:17AM +0900, Andrew Edwards via Digitalmars-d-learn wrote:
>> The following preprocessor directives are frequently encountered in C
>> code, providing a default constant value where the user of the code
>> has not specified one:
>>
>> 	#ifndef MIN
>> 	#define MIN     99
>> 	#endif
>>
>> 	#ifndef MAX
>> 	#define MAX     999
>> 	#endif
>>
>> I'm at a loss at how to properly convert it to D. I've tried the
>> following:
>>
>> 	enum MIN = 0;
>> 	static if(MIN <= 0)
>> 	{
>> 		MIN = 99;
>> 	}
> [...]
>
> That seems wrong. You can't assign to an enum. Besides, doesn't your
> declaration of MIN shadow whatever other definitions may be currently in
> effect?

Okay, got it. It seams I just hadn't hit that bug yet because of other unresolved issues.

> Perhaps what you meant is something like this?
>
> 	static if (!is(typeof(MIN) : int))
> 		enum MIN = 99;

This seems to do the trick.

> though I'm not sure if such a thing will actually work, since
> order-dependent declarations in D are a kind of dangerous territory to
> tread on.

So what is the current best practice when encountering such statements during porting?

>
> T
>

May 13, 2016
On 5/13/16 8:40 AM, Andrew Edwards wrote:
>> That seems wrong. You can't assign to an enum. Besides, doesn't your
>> declaration of MIN shadow whatever other definitions may be currently in
>> effect?
>
> Okay, got it. It seams I just hadn't hit that bug yet because of other
> unresolved issues.
>
>> Perhaps what you meant is something like this?
>>
>>     static if (!is(typeof(MIN) : int))
>>         enum MIN = 99;
>
> This seems to do the trick.

But not exactly the way it's expected to. In the snippets below, C outputs 10 while D outputs 100;

min.c
=========================
	#define MIN 10 // [1]

	#include "mild.h"

	int main()
	{
    		print();
    		return 0;
	}

min.h
=========================
	#include <stdio.h>

	#ifndef MIN
	#define MIN 100
	#endif

	void print()
	{
	    printf("%d\n", MIN);
	}

minA.d
=========================
	enum MIN = 10; // [1]

	import minB;

	void main()
	{
	    print();
	}

minB.d
=========================
	static if (!is(typeof(MIN) : int))
		enum MIN = 100;

	void print()
	{
    		import std.stdio: writeln;
    		writeln(MIN);
	}

Is there a way to reproduce the same behavior? Are there reason's for not allowing this functionality or am I just misunderstanding and going about things the wrong way?

[1] same result whether placed before or after the #include/import statement.

>
>> though I'm not sure if such a thing will actually work, since
>> order-dependent declarations in D are a kind of dangerous territory to
>> tread on.
>
> So what is the current best practice when encountering such statements
> during porting?
>
>>
>> T
>>
>

May 13, 2016
On Friday, 13 May 2016 at 04:59:23 UTC, Andrew Edwards wrote:
> Is there a way to reproduce the same behavior? Are there reason's for not allowing this functionality or am I just misunderstanding and going about things the wrong way?
>
> [1] same result whether placed before or after the #include/import statement.
>
>>> though I'm not sure if such a thing will actually work, since order-dependent declarations in D are a kind of dangerous territory to tread on.

C's #include directive and D's import statement have fundamentally different semantics:

C's is basically equivalent to copy-pasting the contents of the included file at the location of the #include directive. Combined with C's other pre-processor features, this means that a header file can (potentially) do something *completely* different in each file that includes it, because it is being recompiled from scratch, in a new context.

D's import statement, on the other hand, merely makes one module's symbols visible from another. Each module is only compiled once, regardless of how many times it is imported. As such, a module's semantics cannot be changed by each importer, unlike in C.

>> So what is the current best practice when encountering such statements during porting?

The affected code should be re-factored to use some other means of configuration, such as:

1) Adding configuration parameters individually to each public symbol as needed:

minA.d
=========================
    import minB;

    void main()
    {
        print!10();
    }

minB.d
=========================
    module minB;

    void print(long MIN)()
    {
        import std.stdio: writeln;
        writeln(MIN);
    }

2) Enclosing the module in a `struct` or `class` which can be instantiated and configured individually by each user, either at run time or compile time:

minA.d
=========================
    import minB;
    immutable minB = MinB(10);

    void main()
    {
        minB.print();
    }

minB.d
=========================
    module minB;

    struct MinB {
        long MIN;

        void print()
        {
            import std.stdio: writeln;
            writeln(MIN);
        }
    }

3) Enclosing the module in a `template` that can be instantiated and configured by each user at compile time:

minA.d
=========================
    import minB;
    alias minB = MinB!10;
    // Or: alias print = MinB!(10).print;

    void main()
    {
        minB.print();
        // Or: print();
    }

minB.d
=========================
    module minB;

    template MinB(long MIN) {
        void print()
        {
            import std.stdio: writeln;
            writeln(MIN);
        }
    }

4) Passing `version` identifiers at compile time (using the -version command line switch):

minA.d
=========================
    import minB;

    void main()
    {
        print!10();
    }

minB.d
=========================
    module minB;

    version(Min10) {
        enum MIN = 10;
    } else {
        enum MIN = 100;
    }

    void print()
    {
        import std.stdio: writeln;
        writeln(MIN);
    }

5) As a last resort, enclosing the module in a `mixin template` that can be mixed in to each importing module with the required configuration options at compile time:

minA.d
=========================
    import minB;
    mixin minB!10;

    void main()
    {
        print();
    }

minB.d
=========================
    module minB;

    mixin template minB(long MIN) {
        void print()
        {
            import std.stdio: writeln;
            writeln(MIN);
        }
    }

(5) is the most similar to how the C example works, but most of the time it is a very inefficient solution, causing tons of code bloat and redundant instantiations of the included functions and data structures. Don't do this unless you can't find any other reasonable way to do it.

Which of (1) to (4) is best just depends on exactly what the pre-processor logic was being used for.
May 13, 2016
On 5/13/16 7:51 AM, Andrew Edwards wrote:
> The following preprocessor directives are frequently encountered in C
> code, providing a default constant value where the user of the code has
> not specified one:
>
>      #ifndef MIN
>      #define MIN     99
>      #endif
>
>      #ifndef MAX
>      #define MAX     999
>      #endif
>
> I'm at a loss at how to properly convert it to D. I've tried the following:
>
>      enum MIN = 0;
>      static if(MIN <= 0)
>      {
>          MIN = 99;
>      }
>
> it works as long as the static if is enclosed in a static this(),
> otherwise the compiler complains:
>
>      mo.d(493): Error: no identifier for declarator MIN
>      mo.d(493): Error: declaration expected, not '='
>
> This however, does not feel like the right way to do thinks but I cannot
> find any documentation that provides an alternative. Is there a better
> way to do this?
>
> Thanks,
> Andrew

Additionally, what's the best way to handle nested #ifdef's? Those that appear inside structs, functions and the like... I know that global #ifdef's are turned to version blocks but versions blocks cannot be used inside classes, stucts, functions, etc.
May 13, 2016
On Friday, 13 May 2016 at 06:05:14 UTC, Andrew Edwards wrote:
> Additionally, what's the best way to handle nested #ifdef's? Those that appear inside structs, functions and the like... I know that global #ifdef's are turned to version blocks but versions blocks cannot be used inside classes, stucts, functions, etc.

`static if` and `version()` can be nested, and both work just fine inside classes, structs, functions, etc.:

module app;

version = withPrint;

struct A {
  version(withPrint) {
    class B {
      static if(size_t.sizeof == 4) {
        static void print() {
          import std.stdio : writeln;
          version(unittest) {
            writeln("Hello, 32-bit world of unit tests!");
          } else {
            writeln("Hello, 32-bit world!");
          }
        }
      } else {
        static void print() {
          import std.stdio : writeln;
          version(unittest) {
            writeln("Hello, presumably 64-bit world of unit tests!");
          } else {
            writeln("Hello, presumably 64-bit world!");
          }
        }
      }
    }
  }
}

void main() {
  A.B.print();
}

(Try it on DPaste: https://dpaste.dzfl.pl/0fafe316f739)
May 13, 2016
On 5/13/16 3:23 PM, tsbockman wrote:
> On Friday, 13 May 2016 at 06:05:14 UTC, Andrew Edwards wrote:
>> Additionally, what's the best way to handle nested #ifdef's? Those
>> that appear inside structs, functions and the like... I know that
>> global #ifdef's are turned to version blocks but versions blocks
>> cannot be used inside classes, stucts, functions, etc.
>
> `static if` and `version()` can be nested, and both work just fine
> inside classes, structs, functions, etc.:
>
> module app;
>
> version = withPrint;
>
> struct A {
>    version(withPrint) {
>      class B {
>        static if(size_t.sizeof == 4) {
>          static void print() {
>            import std.stdio : writeln;
>            version(unittest) {
>              writeln("Hello, 32-bit world of unit tests!");
>            } else {
>              writeln("Hello, 32-bit world!");
>            }
>          }
>        } else {
>          static void print() {
>            import std.stdio : writeln;
>            version(unittest) {
>              writeln("Hello, presumably 64-bit world of unit tests!");
>            } else {
>              writeln("Hello, presumably 64-bit world!");
>            }
>          }
>        }
>      }
>    }
> }
>
> void main() {
>    A.B.print();
> }

Not sure what I was doing wrong earlier. Works perfectly fine now. Glad I asked because I usually just get frustrated and put it aside and usually never return to it. Thanks for the assist.

> (Try it on DPaste: https://dpaste.dzfl.pl/0fafe316f739)

May 13, 2016
On 5/13/16 12:59 AM, Andrew Edwards wrote:
> On 5/13/16 8:40 AM, Andrew Edwards wrote:
>>> That seems wrong. You can't assign to an enum. Besides, doesn't your
>>> declaration of MIN shadow whatever other definitions may be currently in
>>> effect?
>>
>> Okay, got it. It seams I just hadn't hit that bug yet because of other
>> unresolved issues.
>>
>>> Perhaps what you meant is something like this?
>>>
>>>     static if (!is(typeof(MIN) : int))
>>>         enum MIN = 99;
>>
>> This seems to do the trick.
>
> But not exactly the way it's expected to. In the snippets below, C
> outputs 10 while D outputs 100;
>
> min.c
> =========================
>      #define MIN 10 // [1]
>
>      #include "mild.h"
>
>      int main()
>      {
>              print();
>              return 0;
>      }
>
> min.h
> =========================
>      #include <stdio.h>
>
>      #ifndef MIN
>      #define MIN 100
>      #endif
>
>      void print()
>      {
>          printf("%d\n", MIN);
>      }
>
> minA.d
> =========================
>      enum MIN = 10; // [1]
>
>      import minB;
>
>      void main()
>      {
>          print();
>      }
>
> minB.d
> =========================
>      static if (!is(typeof(MIN) : int))
>          enum MIN = 100;
>
>      void print()
>      {
>              import std.stdio: writeln;
>              writeln(MIN);
>      }
>
> Is there a way to reproduce the same behavior? Are there reason's for
> not allowing this functionality or am I just misunderstanding and going
> about things the wrong way?

Code like this is FUBAR.

I have seen abuse of pre-processor in many places, and it never justifies the cleverness of how it is done.

Note that min.h is providing an inlined function. Essentially, min.h is like a template with the definition of the template parameter defined by the including file. But you can only ever include min.h ONCE in your entire project, or you will get linker errors.

D will always compile a module without external configuration. That is, print is compiled ONCE and only in the context that minA.d defines. Inlining can replace the print call with inline functions, but it will still be compiled according to the module's definitions, not external.

TL;DR: there isn't a good way to port this code, because it's shit code, and D doesn't do that :)

-Steve
May 13, 2016
On Thursday, 12 May 2016 at 22:51:17 UTC, Andrew Edwards wrote:
> The following preprocessor directives are frequently encountered in C code, providing a default constant value where the user of the code has not specified one:
>
> 	#ifndef MIN
> 	#define MIN     99
> 	#endif
>
> 	#ifndef MAX
> 	#define MAX     999
> 	#endif

If you're ok with runtime values

int MIN=99, MAX=999;

And let user assign different values to them.
« First   ‹ Prev
1 2