www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template/mixin ideas?

reply Chris Katko <ckatko gmail.com> writes:
I've got this simple task but I'm trying to perfect it as best I 
can to learn something in the process.

I have Linux terminal ASCII codes for coloring terminal output.

string red(string) { /* ... */ }

"Hello world".red => "\033[31mHello World\033[0m"

which translates to "[red]Hello World[reset to normal text]".

I have to do some slight trickery so I can chain them. But it all 
works fine. __The function is the same__ no matter what kind of 
color, bold, etc attribute I want. The only difference is the 
tag/prefix string.

So I have a table (or enum):
enum colors{
      reset = "\033[0m",
      red = "\033[31m",
      bold = "\033[1m" //...
      }

Absolute perfection would be some way to add a single line to 
that enum (or table) and magically get a new function. I add 
"blue" with its prefix code to the enum and immediately I can do:

"hello world".blue

Add yellow = "\033..." and I can do:

"hello world".bold.yellow

It's an interesting problem. Right now, I made a generic version 
that accepts the prefix code string directly called "color()" and 
red() translates to a call to color with the red string. blue() 
does the same. And so on. But it's still plenty of boiler plate. 
I do that so I don't have 80+ functions all a half-page 
long--which would be a nightmare to verify.

It's surely nothing mission critical. But I wonder if I can 
distill this simple problem down further, I may be able to learn 
some tricks for later problems.

Thanks.
Oct 03 2018
next sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 3 October 2018 at 11:01:53 UTC, Chris Katko wrote:
 I've got this simple task but I'm trying to perfect it as best 
 I can to learn something in the process.

 I have Linux terminal ASCII codes for coloring terminal output.

 string red(string) { /* ... */ }

 "Hello world".red => "\033[31mHello World\033[0m"

 which translates to "[red]Hello World[reset to normal text]".

 I have to do some slight trickery so I can chain them. But it 
 all works fine. __The function is the same__ no matter what 
 kind of color, bold, etc attribute I want. The only difference 
 is the tag/prefix string.

 So I have a table (or enum):
 enum colors{
      reset = "\033[0m",
      red = "\033[31m",
      bold = "\033[1m" //...
      }

 Absolute perfection would be some way to add a single line to 
 that enum (or table) and magically get a new function. I add 
 "blue" with its prefix code to the enum and immediately I can 
 do:

 "hello world".blue

 Add yellow = "\033..." and I can do:

 "hello world".bold.yellow

 It's an interesting problem. Right now, I made a generic 
 version that accepts the prefix code string directly called 
 "color()" and red() translates to a call to color with the red 
 string. blue() does the same. And so on. But it's still plenty 
 of boiler plate. I do that so I don't have 80+ functions all a 
 half-page long--which would be a nightmare to verify.

 It's surely nothing mission critical. But I wonder if I can 
 distill this simple problem down further, I may be able to 
 learn some tricks for later problems.

 Thanks.
A combination of static introspection with string mixins does the trick: --- enum colors { reset = "\033[0m", red = "\033[31m" } auto GenerateColorFuncs() { string result; static foreach(c; __traits(allMembers, colors)) result ~= "auto "~c~"(string str) { return colors."~c~" ~ str ~ colors.reset; }"; return result; } mixin(GenerateColorFuncs()); void main() { import std.stdio; writeln("bla".red); } --- Although you might want to replace the string concatenation with something more performant if used a lot.
Oct 03 2018
parent reply Chris Katko <ckatko gmail.com> writes:
On Wednesday, 3 October 2018 at 11:51:01 UTC, Sebastiaan Koppe 
wrote:
 On Wednesday, 3 October 2018 at 11:01:53 UTC, Chris Katko wrote:
 [...]
A combination of static introspection with string mixins does the trick: --- enum colors { reset = "\033[0m", red = "\033[31m" } auto GenerateColorFuncs() { string result; static foreach(c; __traits(allMembers, colors)) result ~= "auto "~c~"(string str) { return colors."~c~" ~ str ~ colors.reset; }"; return result; } mixin(GenerateColorFuncs()); void main() { import std.stdio; writeln("bla".red); } --- Although you might want to replace the string concatenation with something more performant if used a lot.
The mixin part wouldn't be slowed by strings, right? So the "slowness" is the invokation part which changes strings and forces GC allocations, I guess? What's the alternative to using strings... for strings?
Oct 03 2018
next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Thursday, 4 October 2018 at 01:12:04 UTC, Chris Katko wrote:
 The mixin part wouldn't be slowed by strings, right? So the 
 "slowness" is the invokation part which changes strings and 
 forces GC allocations, I guess?
Yep, that is right.
 What's the alternative to using strings... for strings?
During the concatenation phase you'll use an Appender. Afterwards you retrieve the resulting string from the appender. It is very similar to a StringBuilder in other languages. See std.array.appender
Oct 05 2018
prev sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Thursday, 4 October 2018 at 01:12:04 UTC, Chris Katko wrote:

 What's the alternative to using strings... for strings?
Since the purpose is terminal output, you could just make a custom formatter, something like this: https://run.dlang.io/is/F51UCZ
Oct 05 2018
prev sibling parent Anonymouse <asdf asdf.net> writes:
On Wednesday, 3 October 2018 at 11:01:53 UTC, Chris Katko wrote:
 I've got this simple task but I'm trying to perfect it as best 
 I can to learn something in the process.

 I have Linux terminal ASCII codes for coloring terminal output.

 string red(string) { /* ... */ }

 "Hello world".red => "\033[31mHello World\033[0m"

 which translates to "[red]Hello World[reset to normal text]".

 I have to do some slight trickery so I can chain them. But it 
 all works fine. __The function is the same__ no matter what 
 kind of color, bold, etc attribute I want. The only difference 
 is the tag/prefix string.

 So I have a table (or enum):
 enum colors{
      reset = "\033[0m",
      red = "\033[31m",
      bold = "\033[1m" //...
      }

 Absolute perfection would be some way to add a single line to 
 that enum (or table) and magically get a new function. I add 
 "blue" with its prefix code to the enum and immediately I can 
 do:

 "hello world".blue

 Add yellow = "\033..." and I can do:

 "hello world".bold.yellow
https://run.dlang.io/gist/run-dlang/e0d0bcebe6c4edcc3cd0c2858183357d?compiler=dmd https://issues.dlang.org/show_bug.cgi?id=19286 prevents us from using static foreaches to declare the aliases.
Oct 05 2018