Thread overview
Templates considered impressive
Oct 01
monkyyy
October 01

I had an old atoi() I wrote when I was getting started with D. You know, fixed type, string in, int output. Today for fun I templated the type:

T atoi(T)(string s)

and used static if to add support for signed/unsigned. Easy. Then added floating point so it can parse "123.456" (no exponents... yet). A conversion like:

auto d = atoi!double("123.456");

is about 4k of code. Nice!

Andy

September 30
On Tue, Oct 01, 2024 at 01:00:08AM +0000, Andy Valencia via Digitalmars-d-learn wrote:
> I had an old atoi() I wrote when I was getting started with D.  You know, fixed type, string in, int output.  Today for fun I templated the type:
> 
> T atoi(T)(string s)
[...]

It's all good and fun to try out how awesome D template programming, but why spend the time and effort when you could have just done:

```
import std.conv;
string input = ...;
int i = input.to!int;
uint i = input.to!uint;
short s = input.to!short;
ushort us = input.to!ushort;
float f = input.to!float;
double d = input.to!double;
```

std.conv.to also automatically converts string to enum, handle narrowing int conversions (by inserting runtime checks), alias itself away when the target type implicitly converts from the source type (useful for generic code to eliminate redundant conversions when you don't know the input types), etc..

```
int i = 1234;
byte b = i.to!byte; // throws at runtime
short s = i.to!short; // OK, range check passes

enum E { a=10, b=20 }
E e = "a".to!E;		// returns E.a

string str = "abc";
string ss = str.to!string; // same as ss = str;

char[] ca = "abc";
ss = ca.to!string;	// same as ss = ca.dup;
```

Basically, unless you're one of those GC-phobes, std.conv.to is literally your one-stop shop for all your type conversion needs.  You can even extend it to work for your own custom types by defining the requisite ctors and opCast overloads.  This is D, not C++, you shouldn't need to write your own version of std.conv.to unless you have a *very* good reason to.


T

-- 
Those who don't understand D are condemned to reinvent it, poorly. -- Daniel N
October 01
On Tuesday, 1 October 2024 at 05:44:16 UTC, H. S. Teoh wrote:
> why spend the time and effort when you could have just done:
>
> ```
> import std.conv;

theres a bunch of relivent tradeoffs and phoboes doesnt make a good set of them

October 01
On Tuesday, 1 October 2024 at 11:45:35 UTC, monkyyy wrote:
> On Tuesday, 1 October 2024 at 05:44:16 UTC, H. S. Teoh wrote:
>> why spend the time and effort when you could have just done:
>>
>> ```
>> import std.conv;
>
> theres a bunch of relivent tradeoffs and phoboes doesnt make a good set of them

To be fair, although Phobos sometimes has shockingly large code expansion for modest functions, in this case "123.456".to!double comes to 27k, which seems quite reasonable, even to this old 1802/6502/8080/z80/PDP-11 coder.

Thanks for the pointers to the numerous ways std.conv can be used!

Andy



October 01

On Tuesday, 1 October 2024 at 01:00:08 UTC, Andy Valencia wrote:

>

... A conversion like:

auto d = atoi!double("123.456");

is about 4k of code. Nice!

Congratulations on your initiative. D is very flexible with templates, especially with the mixin templates. For example, you might like this one:

template MyCon (T, string str = "0")
{
    MyCon init = str;

    struct MyCon
    {
        string input;
        T value;

        this(string data)
        {
            import std.conv : to;
            value = data.to!T;
            input = data;
        }

        string toString() => input;

        string convertToHex()
        {
            import std.string : format;
            return input.format!"%-(%02X%)";
        }
    }
}


import std.stdio;
void main()
{
    //mixin MyCon!int;/*
    mixin MyCon!int zero;

    // There is an invisible object here: init (struct MyCon)
    zero.init.writeln;  //* 0 */

    // Here is a new object:
    MyCon!double num1 = "3.14";

    num1.writeln; // 3.14
    assert(num1.value == 3.14);

    // and a convenience function... :)
    num1.convertToHex.writeln;
    assert(num1.convertToHex == "332E3134");
}

SDB@79

October 01

On Tuesday, 1 October 2024 at 16:18:17 UTC, Salih Dincer wrote:

>
// ...
    struct MyCon
    {
        string input;
        T value;

        this(string data)
        {
// ...
        }

// ...
    }
}

Please add this to your MyCon structure:

alias value this;
// assert(num1 == 3.14);

And test it like this too, I think it's awesome!

SDB@79

October 01
On Tue, Oct 01, 2024 at 04:30:27PM +0000, Salih Dincer via Digitalmars-d-learn wrote: [...]
> > ```
> Please add this to your MyCon structure:
> ```d
> alias value this;
> // assert(num1 == 3.14);
> ```
> And test it like this too, I think it's awesome!
[...]

IMO it's not a good idea to recommend the use of `alias this`.  It's a neat trick that solves the short-term problem, but in the long term it actually becomes a maintenance burden.  I would recommend instead avoiding such tricks that save a bit of typing in the short-term but causes problems down the road.  (I used to love using `alias this`, but over time have become convinced that it's not worth it.  It makes code needlessly obscure and reduces understandability.)


T

-- 
The trouble with TCP jokes is that it's like hearing the same joke over and over.
October 02

On Tuesday, 1 October 2024 at 17:30:20 UTC, H. S. Teoh wrote:

>

On Tue, Oct 01, 2024 at 04:30:27PM +0000, Salih Dincer wrote:

>

Please add this to your MyCon structure:

alias value this;
// assert(num1 == 3.14);

And test it like this too, I think it's awesome!
[...]

IMO it's not a good idea to recommend the use of alias this. It's a neat trick that solves the short-term problem...

You may be right about the alias. Let's go a little further and put the convertToHex() function between the template and the struct:

template MyCon(T, string str = "0")
{
    MyCon convert = str;
    struct MyCon
    {
        T value; //alias value this;
        
        this(R)(R data)
        {
            import std.conv : to;
            value = data.to!T;
        }
        mixin DownRange;
    }

    enum _input = str;
    string convertToHex()
    {
        import std.string : format;
        return _input.format!"%-(%02X%)";
    }
    
}

import std.stdio;
void main()
{
    mixin MyCon!(double, "3.141") piNumber;
    piNumber.convertToHex().write(": ");
    piNumber.convert.writeln;
  
    auto iNumber = MyCon!double("0.123");
    piNumber.convertToHex().write(": ");
    iNumber.value.writeln;
  
    // mixin MyCon!(int, "123") testError;
    mixin myStatement!(int, "Hello");
    assert(_value == 42); // no error
    
    mixin myStatement!(double, "D!");
    // assert(_value == 42);
}

template myStatement(T, string value = "")
{
    auto doIt()
    {
        T.stringof.writeln(": ", value);
        return 42;
    }

    auto _value = doIt();
}

template DownRange()
{
    auto empty() => value <= 0;
    auto front() => value;
    auto popFront() => --value;
}

struct S
{
    int value;
    mixin DownRange;
}

I also added another template called DownRange (InputRange functions) inside a template. Since the structures of this mixin are static codes, unfortunately everything looks like const and cannot be changed. Please play around in the playground. I tried to show many things in the code above.

Here is the output of the code:

/*
332E313431: [3.141, 2.141, 1.141, 0.141]
332E313431: 0.123
int: Hello
double: D!
*/

Please note iNumber, not piNumber.

SDB@79