Thread overview
Template/mixin ideas?
Oct 03, 2018
Chris Katko
Oct 03, 2018
Sebastiaan Koppe
Oct 04, 2018
Chris Katko
Oct 05, 2018
Sebastiaan Koppe
Oct 05, 2018
Stanislav Blinov
Oct 05, 2018
Anonymouse
October 03, 2018
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.
October 03, 2018
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.
October 04, 2018
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?
October 05, 2018
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
October 05, 2018
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.
October 05, 2018
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