www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - enum behaivor. another rehash of the topic

reply "Joseph Rice" <ricejm01 gmail.com> writes:
While I must first Admit I'm new to D.   However; I have been a 
C/C++ programmer for 15 years. I am excited about D.  One of the 
features I love is being able to access my legacy code.    But 
there are some cases were a complete re-write/re-factor to D 
makes sense. I've come to it very frustrating code that has been 
stable for years now needs to be changed because of a language 
quirk.  The main selling point about D is C/C++ is almost the 
same syntax,  I see potential in D to replace C/C++.

I've noticed that enum behavior is dramatically different.  This 
is my opinion,  but I see this as a flaw in D.  Again let me 
repeat, My opinion.   While I understand D's approach to enum's.  
It is a hard pill to swallow for an experienced programmer.

The D compiler should know the type of the enum from the symbol.  
You should not have to type the NAME.element when doing a 
comparison or assignment.   Declaring an enum FOO { one, two, 
three} should be the same as declaring a new type.   So that when 
you declare `FOO bar;` in code, assignment and comparison of 
`bar`  should know what bar is comprised of because it is of type 
enum FOO.   Therefore anything of type enum FOO can only comprise 
of the values 'one', 'two', or 'three'.

I've prepared 3 examples, a C, C++, and Finally D.  All 
essentially the same code.  You will notice that the D version, 
has some vast differences. For example you can not define an enum 
and declare the variable all at once.   This gets me to the 
annoying part.  assigning and Comparing.

jrice Wayland:~/prj/enum_example$ gcc -o enum_c enum.c
jrice Wayland:~/prj/enum_example$ g++ -o enum_cpp enum.cpp
jrice Wayland:~/prj/enum_example$ dmd enum.d
jrice Wayland:~/prj/enum_example$ ./enum_c
It's test1
jrice Wayland:~/prj/enum_example$ ./enum_cpp
It's test1
jrice Wayland:~/prj/enum_example$ ./enum
It's test1
jrice Wayland:~/prj/enum_example$ cat enum.c
#include <stdio.h>

void main() {
	enum TEST {
		test1=0,
		test2
	} test;

	test = test1;
	
	switch (test) {
		case test1:
			printf("It's test1\n");
			break;
		case test2:
			printf("It's test2\n");
			break;
		default:
		break;
	}
}
jrice Wayland:~/prj/enum_example$ cat enum.cpp
#include <iostream>
using namespace std;

int main() {
	enum TEST {
		test1=0,
		test2
	} test;

	test = test1;
	
	switch (test) {
		case test1:
			cout << "It's test1" << endl;
			break;
		case test2:
			cout << "It's test2" << endl;
			break;
		default:
		break;
	}
	return 0;
}
jrice Wayland:~/prj/enum_example$ cat enum.d
import std.stdio;

void main() {
	enum TEST {
		test1=0,
		test2
	};

	TEST test = TEST.test1;
	
	switch (test) {
		case TEST.test1:
			writeln("It's test1");
			break;
		case TEST.test2:
			writeln("It's test2");
			break;
		default:
		break;
	}
}

So as you can see, D is just awkward, and it becomes tedious 
especially if you have many many many values in the enum.


So D language Designers and maintainers:

1st

I understand your reasons, it makes it un-ambiguous, becaause you 
know exactly which enum `test1` belongs too.

2nd

What the heck, why do I have to type extra.  It makes porting a 
little mre frustrating, and makes me question using D in the 
first place.

3rd

I'm Pleading to you to consider making both syntax's valid. The 
Old C/C++ way,  and the D way.  If you have too, make compiler 
flag.
Dec 14 2013
next sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Sun, 15 Dec 2013 02:16:39 +0100
schrieb "Joseph Rice" <ricejm01 gmail.com>:

 While I must first Admit I'm new to D.   However; I have been a 
 C/C++ programmer for 15 years. I am excited about D.  One of the 
 features I love is being able to access my legacy code.    But 
 there are some cases were a complete re-write/re-factor to D 
 makes sense. I've come to it very frustrating code that has been 
 stable for years now needs to be changed because of a language 
 quirk.  The main selling point about D is C/C++ is almost the 
 same syntax,  I see potential in D to replace C/C++.
 
 I've noticed that enum behavior is dramatically different.  This 
 is my opinion,  but I see this as a flaw in D.  Again let me 
 repeat, My opinion.   While I understand D's approach to enum's.  
 It is a hard pill to swallow for an experienced programmer.
 
 The D compiler should know the type of the enum from the symbol.  
 You should not have to type the NAME.element when doing a 
 comparison or assignment.   Declaring an enum FOO { one, two, 
 three} should be the same as declaring a new type.   So that when 
 you declare `FOO bar;` in code, assignment and comparison of 
 `bar`  should know what bar is comprised of because it is of type 
 enum FOO.   Therefore anything of type enum FOO can only comprise 
 of the values 'one', 'two', or 'three'.
 
 I've prepared 3 examples, a C, C++, and Finally D.  All 
 essentially the same code.  You will notice that the D version, 
 has some vast differences. For example you can not define an enum 
 and declare the variable all at once.   This gets me to the 
 annoying part.  assigning and Comparing.
 
 jrice Wayland:~/prj/enum_example$ gcc -o enum_c enum.c
 jrice Wayland:~/prj/enum_example$ g++ -o enum_cpp enum.cpp
 jrice Wayland:~/prj/enum_example$ dmd enum.d
 jrice Wayland:~/prj/enum_example$ ./enum_c
 It's test1
 jrice Wayland:~/prj/enum_example$ ./enum_cpp
 It's test1
 jrice Wayland:~/prj/enum_example$ ./enum
 It's test1
 jrice Wayland:~/prj/enum_example$ cat enum.c
 #include <stdio.h>
 
 void main() {
 	enum TEST {
 		test1=0,
 		test2
 	} test;
 
 	test = test1;
 	
 	switch (test) {
 		case test1:
 			printf("It's test1\n");
 			break;
 		case test2:
 			printf("It's test2\n");
 			break;
 		default:
 		break;
 	}
 }
 jrice Wayland:~/prj/enum_example$ cat enum.cpp
 #include <iostream>
 using namespace std;
 
 int main() {
 	enum TEST {
 		test1=0,
 		test2
 	} test;
 
 	test = test1;
 	
 	switch (test) {
 		case test1:
 			cout << "It's test1" << endl;
 			break;
 		case test2:
 			cout << "It's test2" << endl;
 			break;
 		default:
 		break;
 	}
 	return 0;
 }
 jrice Wayland:~/prj/enum_example$ cat enum.d
 import std.stdio;
 
 void main() {
 	enum TEST {
 		test1=0,
 		test2
 	};
 
 	TEST test = TEST.test1;
 	
 	switch (test) {
 		case TEST.test1:
 			writeln("It's test1");
 			break;
 		case TEST.test2:
 			writeln("It's test2");
 			break;
 		default:
 		break;
 	}
 }
 
 So as you can see, D is just awkward, and it becomes tedious 
 especially if you have many many many values in the enum.
void main() { enum TEST { test1, test2 } TEST test = TEST.test1; with(TEST) final switch (test) { case test1: writeln("It's test1"); break; case test2: writeln("It's test2"); break; } } -- Marco
Dec 14 2013
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Joseph Rice:

 import std.stdio;

 void main() {
 	enum TEST {
 		test1=0,
 		test2
 	};

 	TEST test = TEST.test1;
 	
 	switch (test) {
 		case TEST.test1:
 			writeln("It's test1");
 			break;
 		case TEST.test2:
 			writeln("It's test2");
 			break;
 		default:
 		break;
 	}
 }
This is how you usually write that code in D: void main() { import std.stdio; enum Test { t1, t2 } auto test = Test.t1; final switch (test) with (Test) { case t1: writeln("It's t1"); break; case t2: writeln("It's t2"); break; } } The differences: - The import is often better in the function. - Type names (like Test) are better written with just the first letter uppercase. - You often have to carry the type name around, so using shorter names is sometimes OK (like t1 and t2). - In this code you want a final switch. - Using with() you avoid repeating the enum type name. In D you also have anonymous enums: enum { test1, test2 } Also, when you refer to C++, it's better to use the "enum class" of C++11. D enums have some faults, like being weakly typed, having bad error messages, and sometimes being a bit too much long to write (when you pass an enum to a function, the function already knows the name of the enum type. But this is not so bad...). Bye, bearophile
Dec 14 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 15 December 2013 at 01:52:01 UTC, bearophile wrote:
 D enums have some faults...
I've been in enough large scale C++ projects to enjoy D's verbose approach. Sure, C++ is convenient and works 99% of the time. But all you need is for some retard to create a Global enum containing things like the words "Yes, No, True, False, Red, Black, White", and then he's ruined it for *everyone* on the project.
Dec 15 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 15 December 2013 at 08:29:59 UTC, monarch_dodra wrote:
 On Sunday, 15 December 2013 at 01:52:01 UTC, bearophile wrote:
 D enums have some faults...
I've been in enough large scale C++ projects to enjoy D's verbose approach. Sure, C++ is convenient and works 99% of the time. But all you need is for some retard to create a Global enum containing things like the words "Yes, No, True, False, Red, Black, White", and then he's ruined it for *everyone* on the project.
Resulting in people giving name like TestT1, TestT2 as enum values in C++. As a result, you end up with the same verbosity as in D, without the possibility of using 'with'.
Dec 15 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 15 December 2013 at 09:38:28 UTC, deadalnix wrote:
 Resulting in people giving name like TestT1, TestT2 as enum 
 values in C++. As a result, you end up with the same verbosity 
 as in D, without the possibility of using 'with'.
I've usually seen the "namespace" or "struct "approach, eg: namespace CheckerBoardColor // or struct CheckerBoardColor { enum Enumeration { Red, Black, }; }; This allows using "CheckerBoardColor::Red", which (IMO) is nice and verbose. you can use "using CheckerBoardColor" for the equivalent of "with" (namespace only). Unfortunatly, the actual enum "type" is "CheckerBoardColor::Enumeration", which is strangely verbose.
Dec 15 2013
parent reply "Tommi" <tommitissari hotmail.com> writes:
On Sunday, 15 December 2013 at 12:40:53 UTC, monarch_dodra wrote:
 On Sunday, 15 December 2013 at 09:38:28 UTC, deadalnix wrote:
 Resulting in people giving name like TestT1, TestT2 as enum 
 values in C++. As a result, you end up with the same verbosity 
 as in D, without the possibility of using 'with'.
I've usually seen the "namespace" or "struct "approach, eg: namespace CheckerBoardColor // or struct CheckerBoardColor { enum Enumeration { Red, Black, }; }; This allows using "CheckerBoardColor::Red", which (IMO) is nice and verbose. you can use "using CheckerBoardColor" for the equivalent of "with" (namespace only). Unfortunatly, the actual enum "type" is "CheckerBoardColor::Enumeration", which is strangely verbose.
I'd rather do this: namespace CheckerBoardColorNamespace { enum CheckerBoardColor { Red, Black }; }; using CheckerBoardColorNamespace::CheckerBoardColor; auto v = CheckerBoardColor::Red; int main() { using namespace CheckerBoardColorNamespace; auto v = Red; } ...and you get to have a nice name for the enum type.
Dec 15 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 15 December 2013 at 15:01:47 UTC, Tommi wrote:
 I'd rather do this:

 namespace CheckerBoardColorNamespace
 {
     enum CheckerBoardColor { Red, Black };
 };
 using CheckerBoardColorNamespace::CheckerBoardColor;

 auto v = CheckerBoardColor::Red;

 int main()
 {
     using namespace CheckerBoardColorNamespace;
     auto v = Red;
 }

 ...and you get to have a nice name for the enum type.
That's a C++11 extension. "CheckerBoardColor::Red" is not a legal value. There are no scoped enums in C++98/03. That said, nice trick for "using namespace CheckerBoardColorNamespace;" It's a nice way to emulate "with". Too bad this means the enum (AFAIK) can't be strongly C++11-stlye typed. BTH though, if I had C++11 in my workplace, I'd just class it and move on.
Dec 15 2013
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
monarch_dodra:

 I've been in enough large scale C++ projects to enjoy D's 
 verbose approach. Sure, C++ is convenient and works 99% of the 
 time. But all you need is for some retard to create a Global 
 enum containing things like the words "Yes, No, True, False, 
 Red, Black, White", and then he's ruined it for *everyone* on 
 the project.
By the way, I am not criticizing "D verbose approach". I have criticized the weak typing: enum Foo { good, bad } void main() { int x = Foo.good; // Weak typing. } And some people have criticized the verbosity in special situations, like this: enum Foo { good, bad } void bar(Foo f) {} void main() { // bar(bad); // Not enough bar(Foo.bad); } Bye, bearophile
Dec 15 2013
next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 15 December 2013 at 11:27:51 UTC, bearophile wrote:
 By the way, I am not criticizing "D verbose approach". I have 
 criticized the weak typing:

 enum Foo { good, bad }
 void main() {
     int x = Foo.good; // Weak typing.
 }
Hum... Well, it's not actually weak typing. It's strong typing with implicit cast *to* the base type (int by default). For example: enum Foo { a, b, c, } void foo(Foo) {} void main() { foo(1); //Nope. I want a Foo. } Whether this is a good or bad thing I don't know. If D where "just" D, I'd say it's a bad thing (it should require an explicit cast). However, arguably, there might be enough C heritage in D to justify it. As long as we don't have "int to enum" implicit conversion, I think it's fine.
 And some people have criticized the verbosity in special 
 situations, like this:


 enum Foo { good, bad }
 void bar(Foo f) {}
 void main() {
     // bar(bad); // Not enough
     bar(Foo.bad);
 }
I am of those that think this is a good thing.
 Bye,
 bearophile
Dec 15 2013
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
On Sunday, 15 December 2013 at 11:27:51 UTC, bearophile wrote:
 And some people have criticized the verbosity in special 
 situations, like this:

 enum Foo { good, bad }
 void bar(Foo f) {}
 void main() {
     // bar(bad); // Not enough
     bar(Foo.bad);
 }
A related discussion: http://forum.dlang.org/thread/lssmuukdltmooehlnfpf forum.dlang.org
Dec 15 2013
prev sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Sunday, 15 December 2013 at 11:27:51 UTC, bearophile wrote:
 By the way, I am not criticizing "D verbose approach". I have 
 criticized the weak typing:


 enum Foo { good, bad }
 void main() {
     int x = Foo.good; // Weak typing.
 }
This is not a weak typing. An example of weak typing is array literal []. There is nothing wrong with converting enum to base type, since enum is always subset of base type. Explicit cast in such case is useless. What is your point against current behavior?
 And some people have criticized the verbosity in special 
 situations, like this:


 enum Foo { good, bad }
 void bar(Foo f) {}
 void main() {
     // bar(bad); // Not enough
     bar(Foo.bad);
 }

 Bye,
 bearophile
because 'bad' is undefined identifier, but I see your point.
Dec 15 2013
prev sibling next sibling parent Paulo Pinto <pjmlp progtools.org> writes:
Am 15.12.2013 02:16, schrieb Joseph Rice:
 While I must first Admit I'm new to D.   However; I have been a C/C++
 programmer for 15 years. I am excited about D.  One of the features I
 love is being able to access my legacy code.    But there are some cases
 were a complete re-write/re-factor to D makes sense. I've come to it
 very frustrating code that has been stable for years now needs to be
 changed because of a language quirk.  The main selling point about D is
 C/C++ is almost the same syntax,  I see potential in D to replace C/C++.

 I've noticed that enum behavior is dramatically different.  This is my
 opinion,  but I see this as a flaw in D.  Again let me repeat, My
 opinion.   While I understand D's approach to enum's. It is a hard pill
 to swallow for an experienced programmer.

 The D compiler should know the type of the enum from the symbol. You
 should not have to type the NAME.element when doing a comparison or
 assignment.   Declaring an enum FOO { one, two, three} should be the
 same as declaring a new type.   So that when you declare `FOO bar;` in
 code, assignment and comparison of `bar`  should know what bar is
 comprised of because it is of type enum FOO.   Therefore anything of
 type enum FOO can only comprise of the values 'one', 'two', or 'three'.

 I've prepared 3 examples, a C, C++, and Finally D.  All essentially the
 same code.  You will notice that the D version, has some vast
 differences. For example you can not define an enum and declare the
 variable all at once.   This gets me to the annoying part.  assigning
 and Comparing.

 jrice Wayland:~/prj/enum_example$ gcc -o enum_c enum.c
 jrice Wayland:~/prj/enum_example$ g++ -o enum_cpp enum.cpp
 jrice Wayland:~/prj/enum_example$ dmd enum.d
 jrice Wayland:~/prj/enum_example$ ./enum_c
 It's test1
 jrice Wayland:~/prj/enum_example$ ./enum_cpp
 It's test1
 jrice Wayland:~/prj/enum_example$ ./enum
 It's test1
 jrice Wayland:~/prj/enum_example$ cat enum.c
 #include <stdio.h>

 void main() {
      enum TEST {
          test1=0,
          test2
      } test;

      test = test1;

      switch (test) {
          case test1:
              printf("It's test1\n");
              break;
          case test2:
              printf("It's test2\n");
              break;
          default:
          break;
      }
 }
 jrice Wayland:~/prj/enum_example$ cat enum.cpp
 #include <iostream>
 using namespace std;

 int main() {
      enum TEST {
          test1=0,
          test2
      } test;

      test = test1;

      switch (test) {
          case test1:
              cout << "It's test1" << endl;
              break;
          case test2:
              cout << "It's test2" << endl;
              break;
          default:
          break;
      }
      return 0;
 }
 jrice Wayland:~/prj/enum_example$ cat enum.d
 import std.stdio;

 void main() {
      enum TEST {
          test1=0,
          test2
      };

      TEST test = TEST.test1;

      switch (test) {
          case TEST.test1:
              writeln("It's test1");
              break;
          case TEST.test2:
              writeln("It's test2");
              break;
          default:
          break;
      }
 }

 So as you can see, D is just awkward, and it becomes tedious especially
 if you have many many many values in the enum.


 So D language Designers and maintainers:

 1st

 I understand your reasons, it makes it un-ambiguous, becaause you know
 exactly which enum `test1` belongs too.

 2nd

 What the heck, why do I have to type extra.  It makes porting a little
 mre frustrating, and makes me question using D in the first place.

 3rd

 I'm Pleading to you to consider making both syntax's valid. The Old
 C/C++ way,  and the D way.  If you have too, make compiler flag.
It follows the same approach as many other modern languages, even C++ has them nowadays. Obviously, the world at large has come to the conclusion that having enum identifiers creeping into global scope in C was not a good idea. -- Paulo
Dec 15 2013
prev sibling parent Iain Buclaw <ibuclaw gdcproject.org> writes:
On 15 December 2013 01:16, Joseph Rice <ricejm01 gmail.com> wrote:
 So as you can see, D is just awkward, and it becomes tedious especially if
 you have many many many values in the enum.
You are in for a shock when you hear about C++11 http://www.stroustrup.com/C++11FAQ.html#enum That particular FAQ gives a good overview of the rationale of D enums when someone comes from a C++ background, and we should have something similar to describe the difference in the Porting / Interfacing to C/C++ pages.
Dec 15 2013