Jump to page: 1 24  
Page
Thread overview
rval->ref const(T), implicit conversions
Jan 18, 2016
Manu
Jan 18, 2016
bachmeier
Jan 18, 2016
Namespace
Jan 19, 2016
kinke
Jan 18, 2016
tsbockman
Jan 18, 2016
Meta
Jan 18, 2016
Namespace
Jan 18, 2016
bitwise
Jan 18, 2016
Anon
Jan 19, 2016
bitwise
Jan 18, 2016
tsbockman
Jan 18, 2016
tsbockman
Jan 18, 2016
Nick Treleaven
Jan 19, 2016
tsbockman
Jan 19, 2016
bitwise
Jan 19, 2016
tsbockman
Jan 19, 2016
tsbockman
Jan 19, 2016
bitwise
Jan 19, 2016
tsbockman
Jan 19, 2016
tsbockman
Jan 19, 2016
bitwise
Jan 19, 2016
tsbockman
Jan 19, 2016
bitwise
Jan 19, 2016
tsbockman
Jan 19, 2016
bitwise
Jan 19, 2016
tsbockman
Jan 19, 2016
tsbockman
Jan 19, 2016
Timon Gehr
Jan 19, 2016
bitwise
Jan 19, 2016
Timon Gehr
Jan 19, 2016
bitwise
January 19, 2016
One more time...

Assuming:
void func(const CustomString &s1, const CustomString &s2);
void func(ref const(CustomString) s1, ref const(CustomString) s2);

C++:
  func("hello", "world");

D:
  auto dumb_name = CustomString("hello");
  auto another_dumb_name = CustomString("world");
  func(dumb_name, another_dumb_name);

I _hate_ this. Almost every line of my current project's non-systems
code looks like this.
The majority of the code I'm working with is event handlers and glue
logic, which looks like this. Tons of 1 liners and glue. D code is
much larger by volume, and much less readable than C++. The statement
above has about a 1/8 signal to noise ratio, and this is actually
understated; I have a lot of functions that take 4 strings like that!
This problem extends well beyond strings, but it's a classic example.

The main advantage of D is modules, slices, forward referencing and compile times, but I'm seeing trade off of much less efficient code density/brevity for those advantages. D has a meta advantage, but I'm writing C++11/14 code now which is *almost* sufficient, so that advantage is quite diminished in 2016.


In general, I'm finding it hard to do anything useful in this project
without C++ style implicit conversions. Compared to C++ code, I'm
finding D becomes riddled with explicit constructor calls and terrible
stack variable names where C++ implicit construction/conversion would
normally kick in.
This becomes more awkward in some generic function situations, but
it's especially painful (queue broken record) passing such rvalues to
ref-args.
There is truly nothing else in D that has caused so much unrelenting
grief as not being able to pass an rvalue to ref const(T). If we are
never to get a scope-like solution, then consider supporting C++ style
rval->const ref, as a pure practicality. It's been long enough, like,
7 years or something I've been waiting.

So, where are we heading with this? I raise this a couple of times a year. In 6 years, after a lot of talk (and various rejected proposals), as far as I can tell, we aren't any closer to a plan.

In my experience, this has always been and remains the biggest
practical annoyance writing D code, _by far_. It affects a very high
number of my lines of code, of all kinds.
What I don't get is, why does this seem to be unique to me? Perhaps
it's because one common thread among almost all my D applications, is
that I'm working together with C/C++. I can't transition to D unless I
can work effectively against existing/established code. That's all
I've been trying to do for this past 6-7 years, and I'm yet to
successfully produced an acceptable transition path in my years of
trying. This remains a very significant contributor to that failure.
How are we going to resolve this?
Nobody will want to transition if their code gets plainly worse, from
a basic practical standpoint.
January 18, 2016
On Monday, 18 January 2016 at 15:36:09 UTC, Manu wrote:

> Nobody will want to transition if their code gets plainly worse, from
> a basic practical standpoint.

This is a good example of why D should stop trying to convert C++ programmers. There's no way to integrate C++ code into a D project, beyond creating a C interface to some functions.
January 18, 2016
You don't give up, huh? ;)
January 18, 2016
On Monday, 18 January 2016 at 15:36:09 UTC, Manu wrote:
> One more time...
>
> Assuming:
> void func(const CustomString &s1, const CustomString &s2);
> void func(ref const(CustomString) s1, ref const(CustomString) s2);
>
> C++:
>   func("hello", "world");
>
> D:
>   auto dumb_name = CustomString("hello");
>   auto another_dumb_name = CustomString("world");
>   func(dumb_name, another_dumb_name);

Does this do what you want?

import std.stdio;

bool isLValue(T)(ref T val) {
    return true;
}

struct CustomString {
    this(string data) {
        this.data = data;
    }

    string data;
    alias data this;
}

void func(ref CustomString s1, ref CustomString s2) {
    writeln(s1);
    writeln(s2);
    s2 = "universe!";
}

pragma(inline, true)
void arFunc(T, V)(auto ref T s1, auto ref V s2) {
    static if(__traits(compiles, isLValue(s1)))
        alias s1L = s1;
    else
        T s1L = s1;

    static if(__traits(compiles, isLValue(s2)))
        alias s2L = s2;
    else
        V s2L = s2;

    func(s1L, s2L);
}


void main() {
    CustomString b = CustomString("world!");
    arFunc(CustomString("Hello"), b);

    writeln("Hello");
    writeln(b);
}

If so, I probably genericize arFunc better so you can just do this:

void funcImpl(ref const(CustomString) s1, ref const(CustomString) s2);
alias func = arFunc!funcImpl;

January 18, 2016
On Monday, 18 January 2016 at 15:36:09 UTC, Manu wrote:
> One more time...
>
> Assuming:
> void func(const CustomString &s1, const CustomString &s2);
> void func(ref const(CustomString) s1, ref const(CustomString) s2);
>
> C++:
>   func("hello", "world");
>
> D:
>   auto dumb_name = CustomString("hello");
>   auto another_dumb_name = CustomString("world");
>   func(dumb_name, another_dumb_name);
>
> I _hate_ this. Almost every line of my current project's non-systems
> code looks like this.
> The majority of the code I'm working with is event handlers and glue
> logic, which looks like this. Tons of 1 liners and glue. D code is
> much larger by volume, and much less readable than C++. The statement
> above has about a 1/8 signal to noise ratio, and this is actually
> understated; I have a lot of functions that take 4 strings like that!
> This problem extends well beyond strings, but it's a classic example.
>
> The main advantage of D is modules, slices, forward referencing and compile times, but I'm seeing trade off of much less efficient code density/brevity for those advantages. D has a meta advantage, but I'm writing C++11/14 code now which is *almost* sufficient, so that advantage is quite diminished in 2016.
>
>
> In general, I'm finding it hard to do anything useful in this project
> without C++ style implicit conversions. Compared to C++ code, I'm
> finding D becomes riddled with explicit constructor calls and terrible
> stack variable names where C++ implicit construction/conversion would
> normally kick in.
> This becomes more awkward in some generic function situations, but
> it's especially painful (queue broken record) passing such rvalues to
> ref-args.
> There is truly nothing else in D that has caused so much unrelenting
> grief as not being able to pass an rvalue to ref const(T). If we are
> never to get a scope-like solution, then consider supporting C++ style
> rval->const ref, as a pure practicality. It's been long enough, like,
> 7 years or something I've been waiting.
>
> So, where are we heading with this? I raise this a couple of times a year. In 6 years, after a lot of talk (and various rejected proposals), as far as I can tell, we aren't any closer to a plan.
>
> In my experience, this has always been and remains the biggest
> practical annoyance writing D code, _by far_. It affects a very high
> number of my lines of code, of all kinds.
> What I don't get is, why does this seem to be unique to me? Perhaps
> it's because one common thread among almost all my D applications, is
> that I'm working together with C/C++. I can't transition to D unless I
> can work effectively against existing/established code. That's all
> I've been trying to do for this past 6-7 years, and I'm yet to
> successfully produced an acceptable transition path in my years of
> trying. This remains a very significant contributor to that failure.
> How are we going to resolve this?
> Nobody will want to transition if their code gets plainly worse, from
> a basic practical standpoint.

There was this pull request which implemented `auto ref` for non-template functions: https://github.com/D-Programming-Language/dmd/pull/4717

But it was closed. I can't remember the exact reason why.
January 18, 2016
That is mine. I closed it after it was more or less abandoned.
January 18, 2016
On Monday, 18 January 2016 at 15:36:09 UTC, Manu wrote:
> One more time...
>
> Assuming:
> void func(const CustomString &s1, const CustomString &s2);
> void func(ref const(CustomString) s1, ref const(CustomString) s2);
>
> C++:
>   func("hello", "world");
>
> D:
>   auto dumb_name = CustomString("hello");
>   auto another_dumb_name = CustomString("world");
>   func(dumb_name, another_dumb_name);

Actually, I was way overthinking things. Does *this* do what you want?

import std.stdio;

struct CustomString {
    this(string data) {
        this.data = data;
    }

    string data;
    alias data this;
}

void func(ref CustomString s1, ref CustomString s2) {
    writeln(s1);
    writeln(s2);
    s2 = "universe!";
}

pragma(inline, true)
void arFunc(T, V)(auto ref T s1, auto ref V s2) {
    func(s1, s2);
}

void main() {
    CustomString b = CustomString("world!");
    arFunc(CustomString("Hello"), b);

    writeln("Hello");
    writeln(b);
}

Again, I can probably automate generation of the wrapper easily enough.
January 18, 2016
On Monday, 18 January 2016 at 18:08:31 UTC, tsbockman wrote:
> Again, I can probably automate generation of the wrapper easily enough.

Genericized:

template acceptRVals(alias func) {
private:
    import std.traits : arity;
    alias impl = acceptRVals!(arity!func);

public:
    alias acceptRVals = impl!func;
}
template acceptRVals(size_t arity) {
    private enum mixStr = function() {
        import std.conv : to;

        string ctParams = "";
        string rtParams = "";
        string callArgs = "";

        foreach(size_t a; 0 .. arity) {
            string aStr = a.to!string;
            ctParams ~= "T" ~ aStr;
            rtParams ~= "auto ref T" ~ aStr ~ " a" ~ aStr;
            callArgs ~= "a" ~ aStr;

            if(a < (arity - 1)) {
                ctParams ~= ", ";
                rtParams ~= ", ";
                callArgs ~= ", ";
            }
        }

        return "pragma(inline, true) auto acceptRVals(" ~ ctParams ~ ")(" ~ rtParams ~ ") { return func(" ~ callArgs ~ "); }";
    }();

    template acceptRVals(alias func) {
        mixin(mixStr);
    }
}

struct CustomString {
    this(string data) {
        this.data = data;
    }

    string data;
    alias data this;
}

import std.stdio;

alias func = acceptRVals!(function(ref CustomString s1, ref CustomString s2) {
    writeln(s1);
    writeln(s2);
    s2 = "universe!";
});

void main() {
    CustomString b = CustomString("world!");
    func(CustomString("Hello"), b);

    writeln("Hello");
    writeln(b);
}

(I'm sure there are various corner cases not handled properly by this; obviously it would be nice if this was just handled automatically by the compiler like it should be.)
January 18, 2016
On Monday, 18 January 2016 at 18:03:34 UTC, Namespace wrote:
> That is mine. I closed it after it was more or less abandoned.

Yeah.. I was initially for this proposal/PR, but on top of the fact that it will cause confusion because of how it differs from the template auto ref, it simply shouldn't be required at all.

The rationale for not allowing temporaries to be passed as ref params, IIRC, had two parts:
1) passing by ref should mean that the callee is meant to modify the parameter
2) the callee may escape the ref parameter which would be unsafe

I really don't feel the need to reiterate how broken the above logic is, as it's been stated so many times before, and I think it should really be as simple as this:

struct S;

void func(ref S s);
func(S());   // FINE

void func(ref S s) @safe;
func(S());   // ERROR


    Bit
January 18, 2016
On Monday, 18 January 2016 at 15:36:09 UTC, Manu wrote:
> One more time...
>
> Assuming:
> void func(const CustomString &s1, const CustomString &s2);
> void func(ref const(CustomString) s1, ref const(CustomString) s2);
>
> C++:
>   func("hello", "world");
>
> D:
>   auto dumb_name = CustomString("hello");
>   auto another_dumb_name = CustomString("world");
>   func(dumb_name, another_dumb_name);

Seems like a template function wrapper could add the temporaries and do implicit conversions by constructing CustomStrings from string literals as needed by func:

manuCall!func("hello", "world");

Maybe not ideal, but better than the quoted D code.
« First   ‹ Prev
1 2 3 4