www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Request assistance converting C's #ifndef to D

reply Andrew Edwards <edwards.ac gmail.com> writes:
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
next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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 12 2016
parent reply Andrew Edwards <edwards.ac gmail.com> writes:
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 12 2016
parent reply Andrew Edwards <edwards.ac gmail.com> writes:
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 12 2016
next sibling parent tsbockman <thomas.bockman gmail.com> writes:
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 12 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent Andrew Edwards <edwards.ac gmail.com> writes:
On 5/14/16 12:35 AM, Steven Schveighoffer wrote:
 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.
This may be the case, but since I am not yet at a level of understanding where I can discern what is justifiable or not. At the moment I'm simply trying to port over 15k LOC so that I can play with it in D and improve my understanding of what's going on.
 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.
This was an extremely simplified example. There is far more going than than this. Just trying not to lose any of the functionality until I understand what why things are done the way they are and how to better do it in D.
 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
prev sibling next sibling parent reply Andrew Edwards <edwards.ac gmail.com> writes:
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 12 2016
parent reply tsbockman <thomas.bockman gmail.com> writes:
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 12 2016
parent Andrew Edwards <edwards.ac gmail.com> writes:
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 12 2016
prev sibling next sibling parent Kagamin <spam here.lot> writes:
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.
May 13 2016
prev sibling next sibling parent sanjayss <dummy dummy.dummy> writes:
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

 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
One thing you could try is compile the C code without the #ifndef's and see if it compiles without issues. If it does, simply use a D enum and don't worry about translating the #ifndef's. (Alternately check the C code to see if there really are differing definitions of MIN/MAX -- if there are, then the code is already messed up and you need to implement your D code taking into consideration the implications of that -- maybe use different variable names in each section of the code for the MIN/MAX that takes the different MIN/MAX values. I've typically seen this kind of ifdef'ing to quick-fix compile issues in new code without having to rewrite a whole bunch of existing code or code-structure and it is never to have differing values for the defines -- if differing values are used with the same name, then the code is bad and it's best to cleanup as you migrate to D.)
May 13 2016
prev sibling parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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
One option here is that the programmer is trying to avoid multiple definitions of MIN and MAX if for some reason this header is included together with another header that also defines a MIN and MAX. So, you might start by checking if any other header/source file does so. It's entirely possible the programmer is just going overkill with the kind of stuff one does to guard against multiple #include's of the same header, and that this header is the _only_ place where MIN and MAX are defined, and that it's ALWAYS valid for them to be 99 and 999 respectively. The other thought is that the programmer might have in mind to be able to choose alternative MIN and MAX at compile time via environment variables (perhaps the project's build scripts make use of this?). If you think so, is that something you want to support? There are probably better ways of achieving the same result. I suspect it'll probably turn out to be fine to just use enum MIN = 99; enum MAX = 999; ... but H. S. Teoh's suggestion looks sane as a more cautious alternative.
May 16 2016