digitalmars.D.learn - Request assistance converting C's #ifndef to D
- Andrew Edwards (24/24) May 12 2016 The following preprocessor directives are frequently encountered in C
- H. S. Teoh via Digitalmars-d-learn (15/35) May 12 2016 [...]
- Andrew Edwards (6/38) May 12 2016 Okay, got it. It seams I just hadn't hit that bug yet because of other
- Andrew Edwards (44/62) May 12 2016 But not exactly the way it's expected to. In the snippets below, C
- tsbockman (127/137) May 12 2016 C's #include directive and D's import statement have
- Steven Schveighoffer (15/70) May 13 2016 Code like this is FUBAR.
- Andrew Edwards (9/93) May 13 2016 This may be the case, but since I am not yet at a level of understanding...
- Andrew Edwards (5/29) May 12 2016 Additionally, what's the best way to handle nested #ifdef's? Those that
- tsbockman (35/40) May 12 2016 `static if` and `version()` can be nested, and both work just
- Andrew Edwards (4/42) May 12 2016 Not sure what I was doing wrong earlier. Works perfectly fine now. Glad
- Kagamin (4/13) May 13 2016 If you're ok with runtime values
- sanjayss (16/41) May 13 2016 One thing you could try is compile the C code without the
- Joseph Rushton Wakeling (22/31) May 16 2016 One option here is that the programmer is trying to avoid
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 12 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:Okay, got it. It seams I just hadn't hit that bug yet because of other unresolved issues.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;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
On 5/13/16 8:40 AM, Andrew Edwards wrote: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.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
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.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.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.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.So what is the current best practice when encountering such statements during porting?
May 12 2016
On 5/13/16 12:59 AM, Andrew Edwards wrote:On 5/13/16 8:40 AM, Andrew Edwards wrote: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 :) -SteveBut 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?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.
May 13 2016
On 5/14/16 12:35 AM, Steven Schveighoffer wrote:On 5/13/16 12:59 AM, Andrew Edwards wrote: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.On 5/13/16 8:40 AM, Andrew Edwards wrote: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.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?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.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
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, AndrewAdditionally, 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
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
On 5/13/16 3:23 PM, tsbockman wrote:On Friday, 13 May 2016 at 06:05:14 UTC, Andrew Edwards wrote: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.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
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 #endifIf you're ok with runtime values int MIN=99, MAX=999; And let user assign different values to them.
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 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, AndrewOne 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
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 #endifOne 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