D - how to live without #define
- Sandor Hojtsy (59/59) Sep 23 2002 One of my programs have a list of boolean options. The options can be lo...
- Walter (16/22) Sep 23 2002 I thought I was the only one to use that technique!
- anderson (4/14) Sep 23 2002 by
- Burton Radons (17/28) Sep 23 2002 No, no, Sandor wasn't advocating metaprogramming or preprocessing, just
- Sandor Hojtsy (21/47) Sep 23 2002 for,
- Walter (8/14) Sep 24 2002 The missing element error is caught if the enum value isn't added but th...
- Burton Radons (68/139) Sep 24 2002 I can't think of a syntax. Scoped enums look like a good thing, but the...
- Sandor Hojtsy (38/126) Sep 25 2002 Good
- Mark Evans (6/6) Sep 25 2002 One of the things that makes Python so powerful is runtime introspection...
- Patrick Down (5/11) Sep 25 2002 Yes it is truly awesome.
- Mark Evans (11/16) Sep 25 2002 But typically one uses a different language, e.g. Perl/Python/Icon, to a...
- Walter (15/25) Oct 02 2002 auto-gen
- Mark Evans (4/7) Oct 02 2002 Well, your mistake is using an ugly cobbled-together excuse for a langua...
- Mac Reiter (3/5) Sep 24 2002 I'm sorry. The phrase "an explicit cast is implied" just struck me as e...
- Sandor Hojtsy (13/26) Sep 23 2002 by
- Walter (3/5) Sep 24 2002 Yes, I know. But the redundancy does give better error checking.
- Mark Evans (10/13) Sep 24 2002 Ugh. I think enums should be improved, too. C enums are just not very ...
- Walter (10/13) Sep 24 2002 localizing
One of my programs have a list of boolean options. The options can be loaded from a text config file, and option values should be immediatly avaiable during execution. In C : enum Option { firstOption, secondOption, .... lastOption, optionQ }; struct OptionDesc bool value; char *name; }; OptionDesc options[optionQ] = { {false, "firstOption"}, {false, "secondOption"}, ... {false, "lastOption"}, } And then loading can be done based on name, and values can be retrieved by indexing the options array with the enum name: bool d = options[someOption].value; But the evil *redundancy* is lurking here! Options are listed two times, and because of this, you can make several errors: - error in order of the options - error in option names - options missing from one of the lists The C solution is #define-s. See: #define OPTION_LIST \ OH(firstOption), \ OH(secondOption), \ .... \ OH(lastOption) #define OH(a) a enum Options { OPTION_LIST, optionQ }; struct OptionDesc bool value; char *name; }; #undef OH #define OH(a) {false, #a} OptionDesc options[optionQ] = { OPTION_LIST } Voila! Ugly, but no redundancy in the source code. Now how you do this in D? Without #defines you have to maintain the consistency of the two lists by yourself. Why can't the complier help in such a mechanical task? What would be the ideal solution? In this particular case: reflection. If the program could access enum value names at runtime, it could load the options from the config file by name. In general: we should consider all the aspects the C #define was used for, and try to provide an alternative for the yet unadressed ones.
Sep 23 2002
"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:ammnbk$1gvd$1 digitaldaemon.com...Voila! Ugly, but no redundancy in the source code.I thought I was the only one to use that technique!Now how you do this in D? Without #defines you have to maintain the consistency of the two lists by yourself. Why can't the complier help in such a mechanical task?In this case, you can do it with named array initializers: OptionDesc options[optionQ] = [ firstOption : {false, "firstOption"}, ... ]; It's not perfect, but it gets you most of the way there.In general: we should consider all the aspects the C #define was used for, and try to provide an alternative for the yet unadressed ones.You can always do some things with a text processor that can't be done reasonably with a symbolic processor, but I think D comes pretty close to having a superior solution for at least the common things done with #define's. I find the C preprocessor inadequate many times, and have turned to specialized C programs that generate C source for table building. You can see an example in idgen.c in the D front end source.
Sep 23 2002
"Walter" <walter digitalmars.com> wrote in message news:amncsh$2a3t$1 digitaldaemon.com...byNow how you do this in D? Without #defines you have to maintain the consistency of the two listsI still think extentable enums would be a better solution.yourself. Why can't the complier help in such a mechanical task?In this case, you can do it with named array initializers: OptionDesc options[optionQ] = [ firstOption : {false, "firstOption"}, ... ]; It's not perfect, but it gets you most of the way there.
Sep 23 2002
Walter wrote:"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:ammnbk$1gvd$1 digitaldaemon.com...No, no, Sandor wasn't advocating metaprogramming or preprocessing, just a high level of introspection. This level of introspection is in my port (I think it's the find method on the EnumInfo, wrong OS to check). But it's had questionable value, as most enums are of the form: typedef int GLenum; enum : GLenum { GL_FALSE = (GLenum) 0, .... } So you can't name it. Oh, and note the cast. Let's make it so that if you're defining an enum, an explicit cast is implied. The good thing about using intermediaries instead of the preprocessor is that it compiles based on the output file, so you get far better debugging. When you really need wacky code processing, the C processor is both a PITA and makes it impossible to tell what goes wrong.In general: we should consider all the aspects the C #define was used for, and try to provide an alternative for the yet unadressed ones.You can always do some things with a text processor that can't be done reasonably with a symbolic processor, but I think D comes pretty close to having a superior solution for at least the common things done with #define's. I find the C preprocessor inadequate many times, and have turned to specialized C programs that generate C source for table building. You can see an example in idgen.c in the D front end source.
Sep 23 2002
"Burton Radons" <loth users.sourceforge.net> wrote in message news:amo25r$1ga$1 digitaldaemon.com...Walter wrote:for,"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:ammnbk$1gvd$1 digitaldaemon.com...In general: we should consider all the aspects the C #define was usedtoand try to provide an alternative for the yet unadressed ones.You can always do some things with a text processor that can't be done reasonably with a symbolic processor, but I think D comes pretty closeturnedhaving a superior solution for at least the common things done with #define's. I find the C preprocessor inadequate many times, and havecanto specialized C programs that generate C source for table building. YouExactly, in this particular case. I think it would be a more ellegant solution. And Walter: Beware! Your (named indexes) solution now include three lists! One list of the enum value names, when defining the enum type, and the two other lists (merged) when initializing the array. Only the possibility of the "order error" was removed, with yet more redundancy. But what about the "missing element error"?see an example in idgen.c in the D front end source.No, no, Sandor wasn't advocating metaprogramming or preprocessing, just a high level of introspection.This level of introspection is in my port (I think it's the find method on the EnumInfo, wrong OS to check).Wow!But it's had questionable value, as most enums are of the form: typedef int GLenum; enum : GLenum { GL_FALSE = (GLenum) 0, .... } So you can't name it.Interesting. That is a workaround for a feature which was considered a Good Think (scoped enum names). Why do we need workaround here? Because the language doesn't have native support for non-scoped enum names, and they are still required! If it had, you would have no problem naming the enum type.Oh, and note the cast. Let's make it so that if you're defining an enum, an explicit cast is implied.I don't understand.
Sep 23 2002
"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:amp0es$11ge$1 digitaldaemon.com...And Walter: Beware! Your (named indexes) solution now include three lists! One list of the enum value names, when defining the enum type, and the two other lists (merged) when initializing the array. Only the possibility of the "order error" was removed, with yet more redundancy. But what about the "missing element error"?The missing element error is caught if the enum value isn't added but the array one is. The missing array value isn't caught, unless you have runtime checking for array values initialized to the default (which you can do in a unittest block). P.S. I've had many subtle bugs from the "order error", so fixing that in D was important to me.
Sep 24 2002
Sandor Hojtsy wrote:"Burton Radons" <loth users.sourceforge.net> wrote in message news:amo25r$1ga$1 digitaldaemon.com...I can't think of a syntax. Scoped enums look like a good thing, but the problem is that lots of the time the enumeration is a language keyword, requiring either "In", "_in", or "IN", none of which look very nice in use. Perhaps: enum typedef GLenum : int { GL_FALSE = 0, .... } Insane, but it works. It doesn't speak "nonscoped enum", but then again, "for" doesn't speak "looping construct"; we only think of it that way because that's what we've learned to associate with it. Oh, right, introspection. I doubt Walter would go for getting the name of an enumeration unless if it has demonstrable necessary value, as it's by far the most memory-intensive part of introspection and hasn't been used in any code of mine yet - and unlike safe varargs, I don't think it's because it's not really useable. Preprocessing could be part of a standard package-building and distribution system. I could have a file like: char [] [] names; int [] values; input syntax line (name is string) (value is integer) { if (type != "line") return; names ~= name; values ~= value; } body { GL_FALSE 0 GL_TRUE 1 ... } output GLenum { println ("typedef int GLenum;"); println ("enum : GLenum\n{"); for (int c; c < names.length; c ++) println (" %s = %d,", names [c], values [c]); println ("}"); } output GLenumToString { char [] [int] match; for (int c = names.length - 1; c >= 0; c --) match [values [c]] = names [c]; println ("char [] GLenumToString (GLenum value)\n{"); println (" switch (value)\n{"); int [] keys = match.keys; for (int c; c <= keys.length; c ++) println (" case 0x%x: return \"%s\";", keys [c], match [keys [c]]); println (" default: return null;"); println (" }\n}"); } Then in the appropriate source: /++ glenums GLenum ++/ /++ glenums GLenumToString ++/ It would then build a preprocessor, run it, and insert the result. One big advantage here is that it doesn't make a mess of debugging; if I were making complex functions, I could trace them.Walter wrote:for,"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:ammnbk$1gvd$1 digitaldaemon.com...In general: we should consider all the aspects the C #define was usedtoand try to provide an alternative for the yet unadressed ones.You can always do some things with a text processor that can't be done reasonably with a symbolic processor, but I think D comes pretty closeturnedhaving a superior solution for at least the common things done with #define's. I find the C preprocessor inadequate many times, and havecanto specialized C programs that generate C source for table building. YouExactly, in this particular case. I think it would be a more ellegant solution. And Walter: Beware! Your (named indexes) solution now include three lists! One list of the enum value names, when defining the enum type, and the two other lists (merged) when initializing the array. Only the possibility of the "order error" was removed, with yet more redundancy. But what about the "missing element error"?see an example in idgen.c in the D front end source.No, no, Sandor wasn't advocating metaprogramming or preprocessing, just a high level of introspection.This level of introspection is in my port (I think it's the find method on the EnumInfo, wrong OS to check).Wow!But it's had questionable value, as most enums are of the form: typedef int GLenum; enum : GLenum { GL_FALSE = (GLenum) 0, .... } So you can't name it.Interesting. That is a workaround for a feature which was considered a Good Think (scoped enum names). Why do we need workaround here? Because the language doesn't have native support for non-scoped enum names, and they are still required! If it had, you would have no problem naming the enum type.My example was "GL_FALSE = (GLenum) 0,"; I can't write that "GL_FALSE = 0,", because that would be an implicit cast to a typedef. For the definition only, this explicit cast should be implied, as there's about a thousand GL enumerations.Oh, and note the cast. Let's make it so that if you're defining an enum, an explicit cast is implied.I don't understand.
Sep 24 2002
"Burton Radons" <loth users.sourceforge.net> wrote in message news:amr5ab$19t2$1 digitaldaemon.com...GoodBut it's had questionable value, as most enums are of the form: typedef int GLenum; enum : GLenum { GL_FALSE = (GLenum) 0, .... } So you can't name it.Interesting. That is a workaround for a feature which was considered aYou can't think of a (new) syntax for defining an unscoped enum? unscoped enum GLenum { GL_FALSE = 0; ... }Think (scoped enum names). Why do we need workaround here? Because the language doesn't have native support for non-scoped enum names, and they are still required! If it had, you would have no problem naming the enum type.I can't think of a syntax.Scoped enums look like a good thing, but the problem is that lots of the time the enumeration is a language keyword,For example?requiring either "In", "_in", or "IN", none of which look very nice inuse.Perhaps: enum typedef GLenum : int { GL_FALSE = 0, .... } Insane, but it works. It doesn't speak "nonscoped enum", but then again, "for" doesn't speak "looping construct"; we only think of it that way because that's what we've learned to associate with it.for speaks "for all" I still can't see how your syntax works. What is the name of the enum type? How many types are you defining here?Oh, right, introspection. I doubt Walter would go for getting the name of an enumeration unless if it has demonstrable necessary value, as it's by far the most memory-intensive part of introspectionI don't mean that all executables should include enum name introspection tables for all enums! Only for the enums the code is actually using in instrospection. I don't see the size difference between automatically including the enum names where necessary, or manually writing them into the source code as string literals. In the end, they should eat the same memory. The executable will be of the same size. Only source code will be shorter and cleaner.and hasn't been used in any code of mine yet - and unlike safe varargs, Idon't thinkit's because it's not really useable. Preprocessing could be part of a standard package-building and distribution system.I think a medium sized project, with an apropriate language should be OK without preprocessing.I could have a file like:Is this a D source code? It uses some features unknown to me.char [] [] names; int [] values; input syntax line (name is string) (value is integer) { if (type != "line") return; names ~= name; values ~= value; } body { GL_FALSE 0 GL_TRUE 1 ... } output GLenum { println ("typedef int GLenum;"); println ("enum : GLenum\n{"); for (int c; c < names.length; c ++) println (" %s = %d,", names [c], values [c]); println ("}"); } output GLenumToString { char [] [int] match; for (int c = names.length - 1; c >= 0; c --) match [values [c]] = names [c]; println ("char [] GLenumToString (GLenum value)\n{"); println (" switch (value)\n{"); int [] keys = match.keys; for (int c; c <= keys.length; c ++) println (" case 0x%x: return \"%s\";", keys [c], match [keys [c]]); println (" default: return null;"); println (" }\n}"); } Then in the appropriate source: /++ glenums GLenum ++/ /++ glenums GLenumToString ++/ It would then build a preprocessor, run it, and insert the result.It? You mean a makefile will build a preprocessor (by invoking the D compiler on what?), the makefile will run the preprocessor, and the preprocessor will insert the result to an other source file, modifying it?One big advantage here is that it doesn't make a mess of debugging; if I were making complex functions, I could trace them.IMHO source code generation makes debugging slow. You find the error in the pretty generated source, then lookup the generator by hand, try to find the error in that file. You should correct it inside strings (no syntax color), where all kinds of escaping is needed such as backslash-backslash-n, backslash-quote, %%, and such kinds of ugly hard-to-read constructs. Actually a code generator will be so ugly you will start to think about generating the generator itself, with some other tool.Yes. But that problem will also be solved by native unscoped enums, won't it?My example was "GL_FALSE = (GLenum) 0,"; I can't write that "GL_FALSE = 0,", because that would be an implicit cast to a typedef. For the definition only, this explicit cast should be implied, as there's about a thousand GL enumerations.Oh, and note the cast. Let's make it so that if you're defining an enum, an explicit cast is implied.I don't understand.
Sep 25 2002
One of the things that makes Python so powerful is runtime introspection. It's truly awesome. I would always vote in favor of that feature in any language. I agree with Sandor's point about escape sequences. They are ugly and error-prone. As usual I don't have an answer, just lots of opinions! Mark
Sep 25 2002
Mark Evans <Mark_member pathlink.com> wrote in news:amt5d2$jgm$1 digitaldaemon.com:One of the things that makes Python so powerful is runtime introspection. It's truly awesome. I would always vote in favor of that feature in any language.Yes it is truly awesome.I agree with Sandor's point about escape sequences. They are ugly and error-prone.It is ugly but often worth it. I think I've litterly saved myself weeks of time just by writing a little code generator for various stuff.
Sep 25 2002
But typically one uses a different language, e.g. Perl/Python/Icon, to auto-gen code in a target language like C, C++, or D. In the time taken to write / test / modify / finalize a code gen tool, I can design preprocessor stuff that will simply compile directly. I have done few projects for which code gen offered a reasonable return on investment. Changes are inevitable, and it's often simpler to make them by hand, than modify a code gen program with all its ugly escape sequences and testing issues. The evolution trend of compilers is toward an incorporation of code generation, such as vtables, templates, and now with .NET, runtime compilation (as I understand .NET). So to ask D for such features is not entirely out of line. MarkI agree with Sandor's point about escape sequences. They are ugly and error-prone.It is ugly but often worth it. I think I've litterly saved myself weeks of time just by writing a little code generator for various stuff.
Sep 25 2002
"Mark Evans" <Mark_member pathlink.com> wrote in message news:amtl9n$152h$1 digitaldaemon.com...But typically one uses a different language, e.g. Perl/Python/Icon, toauto-gencode in a target language like C, C++, or D.Not me - check out \dmd\src\dmd\idgen.c (!) Using some other language is convenient at the time, but usually turns into a significant nuisance 3 years later when you need to build a new version from the source, and can't find a copy of Perl version X.Y.Z or Perl X.Y.Z won't run on the machine you're porting your code to, etc. (That's why I abandoned using Bison.)In the time taken to write / test / modify / finalize a code gen tool, Icandesign preprocessor stuff that will simply compile directly. I have donefewprojects for which code gen offered a reasonable return on investment.Changesare inevitable, and it's often simpler to make them by hand, than modify acodegen program with all its ugly escape sequences and testing issues. The evolution trend of compilers is toward an incorporation of codegeneration,such as vtables, templates, and now with .NET, runtime compilation (as I understand .NET). So to ask D for such features is not entirely out ofline. I agree that the trend is towards ever more powerful constructs.
Oct 02 2002
convenient at the time, but usually turns into a significant nuisance 3 years later when you need to build a new version from the source, and can't find a copy of Perl version X.Y.Z or Perl X.Y.ZWell, your mistake is using an ugly cobbled-together excuse for a language like Perl instead of a real language like Python or Icon....Uh-oh, I just started a war..... :-) Mark
Oct 02 2002
So you can't name it. Oh, and note the cast. Let's make it so that if you're defining an enum, an explicit cast is implied.I'm sorry. The phrase "an explicit cast is implied" just struck me as extremely funny... Mac
Sep 24 2002
"Walter" <walter digitalmars.com> wrote in message news:amncsh$2a3t$1 digitaldaemon.com..."Sandor Hojtsy" <hojtsy index.hu> wrote in message news:ammnbk$1gvd$1 digitaldaemon.com...byVoila! Ugly, but no redundancy in the source code.I thought I was the only one to use that technique!Now how you do this in D? Without #defines you have to maintain the consistency of the two listsenum Option { firstOption, secondOption, ... lastOption, optionQ }yourself. Why can't the complier help in such a mechanical task?In this case, you can do it with named array initializers:OptionDesc options[optionQ] = [ firstOption : {false, "firstOption"}, ... ];You still need the enum type declaration. So now you have 3 lists of the same names. Sandor
Sep 23 2002
"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:amp115$123m$1 digitaldaemon.com...You still need the enum type declaration. So now you have 3 lists of the same names.Yes, I know. But the redundancy does give better error checking.
Sep 24 2002
Ugh. I think enums should be improved, too. C enums are just not very friendly animals. Take a look at the meta-preprocessor stuff in this code package, http://www.codeproject.com/cpp/tcxunitconverter.asp and ask whether there should be a better way. I do not want to define the same things in 3 places! The preprocessor meta-macros in the preceeding are ugly as sin, but have the virtue of localizing all definitions in one place. I am not proposing preprocessor tricks for D, just some new-think about enums. MarkYou still need the enum type declaration. So now you have 3 lists of the same names.Yes, I know. But the redundancy does give better error checking.
Sep 24 2002
"Mark Evans" <Mark_member pathlink.com> wrote in message news:amqpnk$t0j$1 digitaldaemon.com...I do not want to define the same things in 3 places! The preprocessor meta-macros in the preceeding are ugly as sin, but have the virtue oflocalizingall definitions in one place.There's a more general problem there about synchronizing multiple tables, enums, symbols, and values. At its most complex, you've got YACC and LEX. D goes a big step forward by supporting named initializers, but it is never going to support things like token concatenation (because that would prevent the separation of lexing and parsing). Best I can suggest is do to things like \dmd\src\d\idgen.c, where one program generates the source for another.
Sep 24 2002