www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - `ref T` should be a type!!

reply Victor Porton <porton narod.ru> writes:
That `ref T` (where T is a type) is not a type is a serious 
design error, because so `ref T` cannot be used as a template 
argument.

It is a very serious problem.

Even in C++ despite of all its silliness, T& is a type and can be 
used as a template argument.

Can the language be changed to resolve this problem?
Mar 29 2019
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious 
 design error, because so `ref T` cannot be used as a template 
 argument.

 It is a very serious problem.

 Even in C++ despite of all its silliness, T& is a type and can 
 be used as a template argument.

 Can the language be changed to resolve this problem?
Could you show an example, where an alias parameter is not enough?
Mar 29 2019
next sibling parent Rubn <where is.this> writes:
On Friday, 29 March 2019 at 16:18:59 UTC, Alex wrote:
 On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious 
 design error, because so `ref T` cannot be used as a template 
 argument.

 It is a very serious problem.

 Even in C++ despite of all its silliness, T& is a type and can 
 be used as a template argument.

 Can the language be changed to resolve this problem?
Could you show an example, where an alias parameter is not enough?
An alias and a type are two different things and have different use cases.
Mar 29 2019
prev sibling parent reply Victor Porton <porton narod.ru> writes:
On Friday, 29 March 2019 at 16:18:59 UTC, Alex wrote:
 On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious 
 design error, because so `ref T` cannot be used as a template 
 argument.

 It is a very serious problem.

 Even in C++ despite of all its silliness, T& is a type and can 
 be used as a template argument.

 Can the language be changed to resolve this problem?
Could you show an example, where an alias parameter is not enough?
The following does not work: class C(alias T) { } C(ref int) a;
Mar 29 2019
parent bauss <jj_1337 live.dk> writes:
On Friday, 29 March 2019 at 20:49:20 UTC, Victor Porton wrote:
 On Friday, 29 March 2019 at 16:18:59 UTC, Alex wrote:
 On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious 
 design error, because so `ref T` cannot be used as a template 
 argument.

 It is a very serious problem.

 Even in C++ despite of all its silliness, T& is a type and 
 can be used as a template argument.

 Can the language be changed to resolve this problem?
Could you show an example, where an alias parameter is not enough?
The following does not work: class C(alias T) { } C(ref int) a;
Why do you expect it to work? What exactly are you supposed to do with that? ref can only be used as parameters and thus it would only ever work like: class C(alias T) { void foo(T bar) { ... } } C!(ref int) baz; Which is just ugly and makes no sense. And also in that case alias is unnecessary. class C(T) { void foo(T bar) { ... } } C!(ref int) baz; Which still makes no sense. Is there a reason why you cannot do the following? class C(T) { void foo(ref T bar) { ... } } Because why would you ever want the implementation of a function to behave differently depending on whether its parameters are passed by reference or not. In fact instead there should be a ref overload and one without.
Mar 29 2019
prev sibling next sibling parent reply Rubn <where is.this> writes:
On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious 
 design error, because so `ref T` cannot be used as a template 
 argument.

 It is a very serious problem.

 Even in C++ despite of all its silliness, T& is a type and can 
 be used as a template argument.

 Can the language be changed to resolve this problem?
You can't have reference variables in D, and I doubt walter will allow them. It doesn't make much sense to have a type that allows ref if you can't declare a variable with it.
Mar 29 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/29/2019 12:55 PM, Rubn wrote:
 You can't have reference variables in D, and I doubt walter will allow them.
It 
 doesn't make much sense to have a type that allows ref if you can't declare a 
 variable with it.
You can't declare a pointer to a ref in C++, either. It's not a real type.
Mar 31 2019
parent reply Rubn <where is.this> writes:
On Monday, 1 April 2019 at 00:18:40 UTC, Walter Bright wrote:
 On 3/29/2019 12:55 PM, Rubn wrote:
 You can't have reference variables in D, and I doubt walter 
 will allow them. It doesn't make much sense to have a type 
 that allows ref if you can't declare a variable with it.
You can't declare a pointer to a ref in C++, either. It's not a real type.
It's a type with a restriction, you can literally use it everywhere else you can use a type. A reference is just a pointer with a dress on, having a naked pointer go with a dressed pointer doesn't make much sense. Taking the address of a reference returns a pointer to the object the reference points to, not a pointer to the reference. It's impossible to get the address of the actual reference (* without some hack). But sure it's not a type because you can't have a pointer to it. std::is_same_v<int, int&>;
Mar 31 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Mar 31 2019
next sibling parent reply Manu <turkeyman gmail.com> writes:
On Sun, Mar 31, 2019 at 6:20 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error
It could though with some innovative design, and that would be *awesome*.
 A C++ ref can only appear at the top of a type AST, which is unlike any other
 type. Which exactly matches the only place a storage class can be!
So, what you're saying is, C++ was able to implement storage class semantics, but without distinguishing storage class from the type, thereby making it accessible to any type construction or introspection machinery that the language has available? Apparently C++ was genius in this way; why would you invent storage class specifically to exclude ref from the ability to interact with type construction and introspection machinery? ref sucks because it can't interact with type construction or introspection. Every meta-interaction with ref is via kludgey hacks. You mention the complexity of ref in C++, but that needs to be balanced against the entire concept of 'storage class' in D, and literally every single rule relating to it, since that's the counter-weight. If you take the aggregate of all rules and semantics related to 'storage class' in D, especially when including consideration that we have lost the ability to do type construction or introspection in storage classes, is that REALLY simpler than C++?
Mar 31 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/31/2019 6:30 PM, Manu wrote:
 A C++ ref can only appear at the top of a type AST, which is unlike any other
 type. Which exactly matches the only place a storage class can be!
So, what you're saying is, C++ was able to implement storage class semantics, but without distinguishing storage class from the type, thereby making it accessible to any type construction or introspection machinery that the language has available? Apparently C++ was genius in this way; why would you invent storage class specifically to exclude ref from the ability to interact with type construction and introspection machinery? ref sucks because it can't interact with type construction or introspection. Every meta-interaction with ref is via kludgey hacks.
Um, have you looked at the special, kludgey rules for refs that permeate the C++ spec? Just about every rule for types has a special case for refs. For example, C++ does type inference in various contexts. Sometimes the ref is inferred, sometimes it is skipped. Can you describe when it is inferred and when it isn't, and why?
 You mention the complexity of ref in C++, but that needs to be
 balanced against the entire concept of 'storage class' in D, and
 literally every single rule relating to it, since that's the
 counter-weight. If you take the aggregate of all rules and semantics
 related to 'storage class' in D, especially when including
 consideration that we have lost the ability to do type construction or
 introspection in storage classes, is that REALLY simpler than C++?
Yes. Keep in mind that I implemented ref in C++, every last gory detail. I've earned my opinion of it.
Apr 01 2019
parent reply Manu <turkeyman gmail.com> writes:
On Mon, Apr 1, 2019 at 3:40 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 3/31/2019 6:30 PM, Manu wrote:
 A C++ ref can only appear at the top of a type AST, which is unlike any other
 type. Which exactly matches the only place a storage class can be!
So, what you're saying is, C++ was able to implement storage class semantics, but without distinguishing storage class from the type, thereby making it accessible to any type construction or introspection machinery that the language has available? Apparently C++ was genius in this way; why would you invent storage class specifically to exclude ref from the ability to interact with type construction and introspection machinery? ref sucks because it can't interact with type construction or introspection. Every meta-interaction with ref is via kludgey hacks.
Um, have you looked at the special, kludgey rules for refs that permeate the C++ spec? Just about every rule for types has a special case for refs. For example, C++ does type inference in various contexts. Sometimes the ref is inferred, sometimes it is skipped. Can you describe when it is inferred and when it isn't, and why?
I understand the rules are complex. But I'm saying that 1; I don't believe we *must* implement the complexity exactly the same way if ref were part of the type. and 2; if you're being honest, you need to balance that complexity you describe against the complexity introduced by the _idea_ of storage class, every single detail relating to its implementation, and the loss of language to interact with type construction and introspection. Storage class and all related implementation and semantics are *hugely* complicated, and I think it's worse because it's a parallel system with an unrelated set of rules that you have to remember distinctly, and no language for meaningful interaction. You can't choose to ignore details about meta-interaction with storage class as part of that complexity, they are major. Whereas C++ ref rules are founded in type construction, which, while needing to understand some special cases, don't require understanding a whole parallel ecosystem. I find it hard to judge that the storage class solution can be considered simpler than the complexity that you refer to from C++, and I also think it's a false comparison, because I suspect we could paint the rules simpler than C++ if we tried. Storage class == text mixin, and the worst kinds of text mixins, because they usually affect the function signature. Because text mixins must form a complete expression, if you ever need to do text-mixin on a function signature, the **entire function** must be written as a string which is unreasonably hideous. Storage class is the worst thing in D. I love to make over-inflated claims, but I say this with absolute conviction. There is nothing about D more upsetting than storage class; it is functionally incompatible with introspection and generative meta, and that's D's strongest value proposition. How can you say a feature that's fundamentally incompatible with meta-programming is great?
 You mention the complexity of ref in C++, but that needs to be
 balanced against the entire concept of 'storage class' in D, and
 literally every single rule relating to it, since that's the
 counter-weight. If you take the aggregate of all rules and semantics
 related to 'storage class' in D, especially when including
 consideration that we have lost the ability to do type construction or
 introspection in storage classes, is that REALLY simpler than C++?
Yes. Keep in mind that I implemented ref in C++, every last gory detail. I've earned my opinion of it.
The goal is to make language painless and enjoyable for authors, not necessarily for compiler developers, that's just a nice bonus. I'm not saying it *must* be like C++ (I suspect a less-complex set of rules is possible), I'm just saying it's better. What it should NOT be, is a completely separate mechanism outside of the language like storage class. Perhaps you can invent another meta-language for interacting with storage-class... I've tried to imagine it, but I really struggle to picture something satisfying.
Apr 04 2019
parent Daniel N <no public.email> writes:
On Thursday, 4 April 2019 at 07:21:34 UTC, Manu wrote:
 Perhaps you can invent another meta-language for interacting 
 with storage-class... I've tried to imagine it, but I really 
 struggle to picture something satisfying.
I wanted this a long time, declarations as template params. fun!(ref int x); Every part should be possible to introspect including storage class and variable name.
Apr 04 2019
prev sibling parent reply Dein <dwin d.com> writes:
On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Seriously? You should know as well as I do what that function actually translates to: void test(int&* const a); So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
Apr 01 2019
next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 1 April 2019 at 13:09:28 UTC, Dein wrote:
 On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Seriously? You should know as well as I do what that function actually translates to: void test(int&* const a); So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
Bad example, but the point stands: ---- // foo.cpp int fun() { int& foo[5]; // doesn't compile } ---- I'd never even thought of it until Walter mentioned it that one can't have an array of references. Huh.
Apr 01 2019
next sibling parent reply Rubn <where is.this> writes:
On Monday, 1 April 2019 at 13:57:17 UTC, Atila Neves wrote:
 On Monday, 1 April 2019 at 13:09:28 UTC, Dein wrote:
 On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Seriously? You should know as well as I do what that function actually translates to: void test(int&* const a); So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
Bad example, but the point stands: ---- // foo.cpp int fun() { int& foo[5]; // doesn't compile } ---- I'd never even thought of it until Walter mentioned it that one can't have an array of references. Huh.
Arrays in C++ are just pointers. If you can't have a pointer to something, it is only natural you can't have array either. void* ptr; // ok void arr[5]; // error So I guess Walter agrees that void isn't a type then right? You can't have an array to it so that's the deciding factor.
Apr 01 2019
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/1/2019 1:00 PM, Rubn wrote:
 Arrays in C++ are just pointers.
No, they're not.
 If you can't have a pointer to something, it is 
 only natural you can't have array either.
g++ -c test.cpp test.cpp: In function void test(): test.cpp:1:24: error: declaration of p as array of references void test() { int& p[3]; } ^ As I said, C++ ref can only appear at the top of an AST, which is what a "storage class" is. C++ tries to have it be both a floor wax and a desert topping, making for a device that nobody understands. Be careful what you wish for.
Apr 01 2019
next sibling parent reply Rubn <where is.this> writes:
On Monday, 1 April 2019 at 20:51:44 UTC, Walter Bright wrote:
 On 4/1/2019 1:00 PM, Rubn wrote:
 Arrays in C++ are just pointers.
No, they're not.
Yes they are. The only time an array exists in C++ is (oddly) only when you pass it by reference :). Otherwise every operation you do on array, any time you pass an array to a function that isn't a reference it is just a pointer. This is why buffer overflows exist in C++, because an is literally just a pointer -- it doesn't even know it's own size! How can you call that an array?
 If you can't have a pointer to something, it is only natural 
 you can't have array either.
g++ -c test.cpp test.cpp: In function void test(): test.cpp:1:24: error: declaration of p as array of references void test() { int& p[3]; } ^ As I said, C++ ref can only appear at the top of an AST, which is what a "storage class" is. C++ tries to have it be both a floor wax and a desert topping, making for a device that nobody understands.
So you are saying void is also a "storage class" ?
 Be careful what you wish for.
I'm not wishing for anything. You entered the discussion and stated that it isn't a type. And that's what I've been arguing against.
Apr 01 2019
next sibling parent Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 2 April 2019 at 00:00:34 UTC, Rubn wrote:
 On Monday, 1 April 2019 at 20:51:44 UTC, Walter Bright wrote:
 On 4/1/2019 1:00 PM, Rubn wrote:
 Arrays in C++ are just pointers.
No, they're not.
Yes they are. The only time an array exists in C++ is (oddly) only when you pass it by reference :). Otherwise every operation you do on array, any time you pass an array to a function that isn't a reference it is just a pointer. This is why buffer overflows exist in C++, because an is literally just a pointer -- it doesn't even know it's own size! How can you call that an array?
Every word in this paragraph is wrong, including "the", "yes", and "it".
Apr 01 2019
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 04/01/2019 05:00 PM, Rubn wrote:
 On Monday, 1 April 2019 at 20:51:44 UTC, Walter Bright wrote:
 On 4/1/2019 1:00 PM, Rubn wrote:
 Arrays in C++ are just pointers.
No, they're not.
Yes they are.
Sensational but wrong. For example, there is no pointer in the following C++ code yet there is an array: #include <iostream> using namespace std; struct S { int arr[10]; }; int main() { cout << sizeof(S) << '\n'; } I know that you can qualify your statement to make it correct in some contexts but you are not doing so (yet?). Ali
Apr 01 2019
prev sibling parent John Colvin <john.loughran.colvin gmail.com> writes:
On Tuesday, 2 April 2019 at 00:00:34 UTC, Rubn wrote:
 On Monday, 1 April 2019 at 20:51:44 UTC, Walter Bright wrote:
 On 4/1/2019 1:00 PM, Rubn wrote:
 Arrays in C++ are just pointers.
No, they're not.
Yes they are. The only time an array exists in C++ is (oddly) only when you pass it by reference :). Otherwise every operation you do on array, any time you pass an array to a function that isn't a reference it is just a pointer. This is why buffer overflows exist in C++, because an is literally just a pointer -- it doesn't even know it's own size! How can you call that an array?
I love it, stridently arguing with Walter about the basics of C++, one of the few people in the world who groks the basics of the language well enough to implement a compiler for it*! Obviously there's always a chance he's wrong about things, but on a topic like this? Very bad odds. :) * Memorizing the standard & writing a million lines of C++ code would be no guarantee of getting to this level.
Apr 02 2019
prev sibling parent reply Rubn <where is.this> writes:
On Monday, 1 April 2019 at 20:51:44 UTC, Walter Bright wrote:
 On 4/1/2019 1:00 PM, Rubn wrote:
 Arrays in C++ are just pointers.
No, they're not.
 If you can't have a pointer to something, it is only natural 
 you can't have array either.
g++ -c test.cpp test.cpp: In function void test(): test.cpp:1:24: error: declaration of p as array of references void test() { int& p[3]; } ^ As I said, C++ ref can only appear at the top of an AST, which is what a "storage class" is. C++ tries to have it be both a floor wax and a desert topping, making for a device that nobody understands. Be careful what you wish for.
Let's put it this way, is const a type? No? You would say it is a "sturage class". Considering the following now though: What do you think the following code is going to print? import std.stdio; void foo(T)(T a) { writeln(T.stringof); } template is_same(T, G) { enum is_same = is(T == G); } void main() { int a; const int b; foo( a ); foo( b ); writeln( "is_same: ", is_same!(typeof(a), typeof(b)) ); }
Apr 01 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/1/2019 5:20 PM, Rubn wrote:
 Let's put it this way, is const a type?
No. It's a "type qualifier".
Apr 02 2019
parent reply Rubn <where is.this> writes:
On Tuesday, 2 April 2019 at 07:58:21 UTC, Walter Bright wrote:
 On 4/1/2019 5:20 PM, Rubn wrote:
 Let's put it this way, is const a type?
No. It's a "type qualifier".
Enough so that it is called a storage class in the DMD source code.
Apr 03 2019
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03.04.19 21:56, Rubn wrote:
 On Tuesday, 2 April 2019 at 07:58:21 UTC, Walter Bright wrote:
 On 4/1/2019 5:20 PM, Rubn wrote:
 Let's put it this way, is const a type?
No. It's a "type qualifier".
Enough so that it is called a storage class>
Syntactically, it is both. However, annotating a variable with `const` will apply the type qualifier to its type without affecting how it is stored.
 in the DMD source code.
Check out mtype.d.
Apr 03 2019
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03.04.19 22:36, Timon Gehr wrote:
 On 03.04.19 21:56, Rubn wrote:
 On Tuesday, 2 April 2019 at 07:58:21 UTC, Walter Bright wrote:
 On 4/1/2019 5:20 PM, Rubn wrote:
 Let's put it this way, is const a type?
No. It's a "type qualifier".
Enough so that it is called a storage class>
Syntactically, it is both. However, annotating a variable with `const` will apply the type qualifier to its type without
_necessarily_
 affecting how it is stored.
(But it can, e.g. static const variables will be put into the readonly data section.)
Apr 03 2019
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 1 April 2019 at 20:00:34 UTC, Rubn wrote:
 On Monday, 1 April 2019 at 13:57:17 UTC, Atila Neves wrote:
 On Monday, 1 April 2019 at 13:09:28 UTC, Dein wrote:
 On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Seriously? You should know as well as I do what that function actually translates to: void test(int&* const a); So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
Bad example, but the point stands: ---- // foo.cpp int fun() { int& foo[5]; // doesn't compile } ---- I'd never even thought of it until Walter mentioned it that one can't have an array of references. Huh.
Arrays in C++ are just pointers. If you can't have a pointer to something, it is only natural you can't have array either.
Arrays in C (and by extension C++) are most definitely not "just pointers". This is a common misconception due to arrays decaying to pointers in function calls. There are such things as array pointers in C, and C++ adds array references to the mix for, in all likelihood, consistency. I haven't met many C programmers that know of their existence.
     void* ptr;   // ok
     void arr[5]; // error

 So I guess Walter agrees that void isn't a type then right? You 
 can't have an array to it so that's the deciding factor.
Funnily enough your example here shows how pointers and arrays aren't the same thing. The reason you can't have an array of "voids" in C is because void has no size and the whole thing is nonsensical. It's a good point though that void is treated differently in C's type system.
Apr 01 2019
next sibling parent reply Rubn <where is.this> writes:
On Monday, 1 April 2019 at 23:04:15 UTC, Atila Neves wrote:
 On Monday, 1 April 2019 at 20:00:34 UTC, Rubn wrote:
 On Monday, 1 April 2019 at 13:57:17 UTC, Atila Neves wrote:
 On Monday, 1 April 2019 at 13:09:28 UTC, Dein wrote:
 On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a 
 type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Seriously? You should know as well as I do what that function actually translates to: void test(int&* const a); So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
Bad example, but the point stands: ---- // foo.cpp int fun() { int& foo[5]; // doesn't compile } ---- I'd never even thought of it until Walter mentioned it that one can't have an array of references. Huh.
Arrays in C++ are just pointers. If you can't have a pointer to something, it is only natural you can't have array either.
Arrays in C (and by extension C++) are most definitely not "just pointers". This is a common misconception due to arrays decaying to pointers in function calls. There are such things as array pointers in C, and C++ adds array references to the mix for, in all likelihood, consistency. I haven't met many C programmers that know of their existence.
     void* ptr;   // ok
     void arr[5]; // error

 So I guess Walter agrees that void isn't a type then right? 
 You can't have an array to it so that's the deciding factor.
Funnily enough your example here shows how pointers and arrays aren't the same thing. The reason you can't have an array of "voids" in C is because void has no size and the whole thing is nonsensical. It's a good point though that void is treated differently in C's type system.
I never said they are the same thing. An array can only (with a minor exception) be represented by a pointer, if a pointer can't exist then neither can the array.
Apr 01 2019
parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 2 April 2019 at 00:06:23 UTC, Rubn wrote:
 On Monday, 1 April 2019 at 23:04:15 UTC, Atila Neves wrote:
 On Monday, 1 April 2019 at 20:00:34 UTC, Rubn wrote:
 On Monday, 1 April 2019 at 13:57:17 UTC, Atila Neves wrote:
 On Monday, 1 April 2019 at 13:09:28 UTC,
 Funnily enough your example here shows how pointers and arrays 
 aren't the same thing. The reason you can't have an array of 
 "voids" in C is because void has no size and the whole thing 
 is nonsensical. It's a good point though that void is treated 
 differently in C's type system.
I never said they are the same thing.
"Arrays in C++ are just pointers".
 An array can only (with a minor exception) be represented by a 
 pointer, if a pointer can't exist then neither can the array.
Factually incorrect.
Apr 01 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/1/2019 5:55 PM, Atila Neves wrote:
 Factually incorrect.
I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection. I wrote an article about it calling this "C's Biggest Mistake", as this has led to decades of crashes and malware infestations: https://www.digitalmars.com/articles/b44.html
Apr 02 2019
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/2/19 4:11 AM, Walter Bright wrote:
 On 4/1/2019 5:55 PM, Atila Neves wrote:
 Factually incorrect.
I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection.
You forgot to mention that top-level "const" is ignored in function parameters (for the signature and mangling but not the implementation). Now the fact that "42" is just ignored in the declaration below is indistinguishable from conspiracy: void fun(int a[42]);
Apr 02 2019
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/2/2019 4:05 AM, Andrei Alexandrescu wrote:
 You forgot to mention that top-level "const" is ignored in function parameters 
 (for the signature and mangling but not the implementation).
Curiously, it is present in the name mangling for dmc++ and dmd (for extern(D)), but it should be fixed for dmd. https://issues.dlang.org/show_bug.cgi?id=19785
 Now the fact that 
 "42" is just ignored in the declaration below is indistinguishable from
conspiracy:
 
 void fun(int a[42]);
Ironically, it is not ignored in the C++ mangling if it's a ref to int[42].
Apr 02 2019
parent reply Rubn <where is.this> writes:
On Tuesday, 2 April 2019 at 20:43:14 UTC, Walter Bright wrote:
 On 4/2/2019 4:05 AM, Andrei Alexandrescu wrote:
 You forgot to mention that top-level "const" is ignored in 
 function parameters (for the signature and mangling but not 
 the implementation).
Curiously, it is present in the name mangling for dmc++ and dmd (for extern(D)), but it should be fixed for dmd. https://issues.dlang.org/show_bug.cgi?id=19785
 Now the fact that "42" is just ignored in the declaration 
 below is indistinguishable from conspiracy:
 
 void fun(int a[42]);
Ironically, it is not ignored in the C++ mangling if it's a ref to int[42].
Not ironically, because you can't have a reference to a pointer :). It makes sense if you understand that: void foo( int a[] ); Is not a parameter to an array. After your whole spiel about ferraris, engines, and how their users won't see the internal workings of the engine. I'd have hoped better from you than you pointing at the top level fluffy user-friendly error message and saying LOOK LOOK it says array so it must be an array!
Apr 03 2019
parent reply Rubn <where is.this> writes:
On Wednesday, 3 April 2019 at 20:10:08 UTC, Rubn wrote:
 On Tuesday, 2 April 2019 at 20:43:14 UTC, Walter Bright wrote:
 On 4/2/2019 4:05 AM, Andrei Alexandrescu wrote:
 You forgot to mention that top-level "const" is ignored in 
 function parameters (for the signature and mangling but not 
 the implementation).
Curiously, it is present in the name mangling for dmc++ and dmd (for extern(D)), but it should be fixed for dmd. https://issues.dlang.org/show_bug.cgi?id=19785
 Now the fact that "42" is just ignored in the declaration 
 below is indistinguishable from conspiracy:
 
 void fun(int a[42]);
Ironically, it is not ignored in the C++ mangling if it's a ref to int[42].
Not ironically, because you can't have a reference to a pointer :). It makes sense if you understand that: void foo( int a[] ); Is not a parameter to an array. After your whole spiel about ferraris, engines, and how their users won't see the internal workings of the engine. I'd have hoped better from you than you pointing at the top level fluffy user-friendly error message and saying LOOK LOOK it says array so it must be an array!
A pointer to a reference*.
Apr 03 2019
parent reply Rose <rose sarajevo.com> writes:
On Wednesday, 3 April 2019 at 20:11:02 UTC, Rubn wrote:
 On Wednesday, 3 April 2019 at 20:10:08 UTC, Rubn wrote:
 On Tuesday, 2 April 2019 at 20:43:14 UTC, Walter Bright wrote:
 On 4/2/2019 4:05 AM, Andrei Alexandrescu wrote:
 You forgot to mention that top-level "const" is ignored in 
 function parameters (for the signature and mangling but not 
 the implementation).
Curiously, it is present in the name mangling for dmc++ and dmd (for extern(D)), but it should be fixed for dmd. https://issues.dlang.org/show_bug.cgi?id=19785
 Now the fact that "42" is just ignored in the declaration 
 below is indistinguishable from conspiracy:
 
 void fun(int a[42]);
Ironically, it is not ignored in the C++ mangling if it's a ref to int[42].
Not ironically, because you can't have a reference to a pointer :). It makes sense if you understand that: void foo( int a[] ); Is not a parameter to an array. After your whole spiel about ferraris, engines, and how their users won't see the internal workings of the engine. I'd have hoped better from you than you pointing at the top level fluffy user-friendly error message and saying LOOK LOOK it says array so it must be an array!
A pointer to a reference*.
The Dunning-Kruger is strong with this one. Arrogance completes ignorance.
Apr 03 2019
parent Rubn <where is.this> writes:
On Wednesday, 3 April 2019 at 20:28:35 UTC, Rose wrote:
 On Wednesday, 3 April 2019 at 20:11:02 UTC, Rubn wrote:
 On Wednesday, 3 April 2019 at 20:10:08 UTC, Rubn wrote:
 On Tuesday, 2 April 2019 at 20:43:14 UTC, Walter Bright wrote:
 On 4/2/2019 4:05 AM, Andrei Alexandrescu wrote:
 You forgot to mention that top-level "const" is ignored in 
 function parameters (for the signature and mangling but not 
 the implementation).
Curiously, it is present in the name mangling for dmc++ and dmd (for extern(D)), but it should be fixed for dmd. https://issues.dlang.org/show_bug.cgi?id=19785
 Now the fact that "42" is just ignored in the declaration 
 below is indistinguishable from conspiracy:
 
 void fun(int a[42]);
Ironically, it is not ignored in the C++ mangling if it's a ref to int[42].
Not ironically, because you can't have a reference to a pointer :). It makes sense if you understand that: void foo( int a[] ); Is not a parameter to an array. After your whole spiel about ferraris, engines, and how their users won't see the internal workings of the engine. I'd have hoped better from you than you pointing at the top level fluffy user-friendly error message and saying LOOK LOOK it says array so it must be an array!
A pointer to a reference*.
The Dunning-Kruger is strong with this one. Arrogance completes ignorance.
Not as much as walter and andrei. I really hope I never reach a point where I think I know more than anyone else that I would simply dismiss people's discussions because of some accomplishment. If you think you have nothing more to learn, then step aside for those that do.
Apr 03 2019
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On Tue, Apr 2, 2019 at 4:10 AM Andrei Alexandrescu via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 4/2/19 4:11 AM, Walter Bright wrote:
 On 4/1/2019 5:55 PM, Atila Neves wrote:
 Factually incorrect.
I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection.
You forgot to mention that top-level "const" is ignored in function parameters (for the signature and mangling but not the implementation).
Actually, it's sadly not ignored, and MSVC mangles it! It's been a massive PITA when writing some of the STL bindings in druntime! I've had to manually build mangled function names to extern, because I can't express it in D >_< https://github.com/dlang/druntime/blob/master/src/core/stdcpp/allocator.d#L50 <- you're loving it!
Apr 04 2019
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/4/19 4:33 AM, Manu wrote:
 On Tue, Apr 2, 2019 at 4:10 AM Andrei Alexandrescu via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 4/2/19 4:11 AM, Walter Bright wrote:
 On 4/1/2019 5:55 PM, Atila Neves wrote:
 Factually incorrect.
I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection.
You forgot to mention that top-level "const" is ignored in function parameters (for the signature and mangling but not the implementation).
Actually, it's sadly not ignored, and MSVC mangles it!
I'd be surprised. It would be a gross bug. It didn't in 2004 when I still was using it. Herb advocates using top-level const in implementation, pointing out that it's ignored in the signature.
 It's been a
 massive PITA when writing some of the STL bindings in druntime!
 I've had to manually build mangled function names to extern, because I
 can't express it in D >_<
 
 https://github.com/dlang/druntime/blob/master/src/core/stdcpp/allocator.d#L50
   <- you're loving it!
Could that be a "pointer to const"? That's not top-level.
Apr 04 2019
parent reply Manu <turkeyman gmail.com> writes:
On Thu, Apr 4, 2019 at 4:15 AM Andrei Alexandrescu via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 4/4/19 4:33 AM, Manu wrote:
 On Tue, Apr 2, 2019 at 4:10 AM Andrei Alexandrescu via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 4/2/19 4:11 AM, Walter Bright wrote:
 On 4/1/2019 5:55 PM, Atila Neves wrote:
 Factually incorrect.
I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection.
You forgot to mention that top-level "const" is ignored in function parameters (for the signature and mangling but not the implementation).
Actually, it's sadly not ignored, and MSVC mangles it!
I'd be surprised. It would be a gross bug. It didn't in 2004 when I still was using it. Herb advocates using top-level const in implementation, pointing out that it's ignored in the signature.
Herb appears to be wrong. And this is his native STL, he should know ;)
 It's been a
 massive PITA when writing some of the STL bindings in druntime!
 I've had to manually build mangled function names to extern, because I
 can't express it in D >_<

 https://github.com/dlang/druntime/blob/master/src/core/stdcpp/allocator.d#L50
   <- you're loving it!
Could that be a "pointer to const"? That's not top-level.
Straight from the STL: xmemory0:989 template<class _Ty> class allocator { // generic allocator for objects of class _Ty public: ... void deallocate(_Ty * const _Ptr, const size_t _Count) { // deallocate object at _Ptr // no overflow check on the following multiply; we assume _Allocate did that check _Deallocate<_New_alignof<_Ty>>(_Ptr, sizeof(_Ty) * _Count); } } I spent hours trying to work out why the mangling was failing before I noticed the const was in the wrong place in the C++ code. Only solution is the code I linked above. Also, FWIW, I think this is a fairly recent change (ie, last 5 years).
Apr 04 2019
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/4/19 1:20 PM, Manu wrote:
 Also, FWIW, I think this is a fairly recent change (ie, last 5 years).
Good to know, thanks! Found this, too: https://stackoverflow.com/questions/32447978/odd-vs-name-mangling-behavior/32448512 I'll email Herb.
Apr 04 2019
parent reply Herb Sutter <hsutter microsoft.com> writes:
On Thursday, 4 April 2019 at 17:25:05 UTC, Andrei Alexandrescu 
wrote:
 On 4/4/19 1:20 PM, Manu wrote:
 Also, FWIW, I think this is a fairly recent change (ie, last 5 
 years).
Good to know, thanks! Found this, too: https://stackoverflow.com/questions/32447978/odd-vs-name-mangling-behavior/32448512 I'll email Herb.
Andrei sent me mail and I did a little digging. Much of the following is courtesy Jonathan Caves (thanks Jon!). Pasting what I sent to Andrei with light edits: Short answer: Yes, but most C++ code won't (and doesn't) notice because to see the different mangling you have to declare the function differently in two translation units, *and* have different "first" declarations in those TUs. So this is invisible for any function you declare in a shared header (because everyone sees the same first declaration). And it's invisible when you declare without top-level const and then define with top-level const on a parameter (because everyone uses the first declaration). Longer answer: Yes, this is a long standing known issue – our name decorator was developed long before C++98 codified a lot of stuff. Note that there are two mangling issues involved that come up in your example (or minor variations of it): [dcl.fct]/5 says that function parameters ignore top-level const and treat [] as *:
 After determining the type of each parameter, any parameter of 
 type
 “array of T” or of function type T is adjusted to be “pointer 
 to T”.
 After producing the list of parameter types, any top-level 
 cv-qualifiers
 modifying a parameter type are deleted when forming the 
 function type.
So these are supposed to be equivalent redeclarations of f and g: ~~~ // Case 1 void f(int); void f(int const); // same, redeclaration // Case 2 void g(int*); void g(int[]); // same, redeclaration ~~~ And in VC++ they are correctly equivalent, in the language. However, for name mangling, VC++ selects mangling based on the first declaration it seems, and it does mangle each of these pairs differently, so if two TUs have different first declarations of each of f and g, they can’t link. Again, to make this actually visible to users, they have to see different *first* declarations. For starters that means they’re not using a shared header. I understand that someone you know found it caused trouble with linking foreign code with C++, though presumably that’s because they had to write their own declaration by hand (in C++, or in the other language)? I’ve mostly seen that sort of thing when people have to write their own declarations, and in those cases they also had general fragility (e.g., writing int vs long, that sort of thing – because they have to be careful to get the declarations exactly right because they’re writing them manually, same trouble that Java JNI and .NET P/Invoke have in general, you have to make the parameters perfect). The worry about ‘fixing’ the name decorator is that it would be an ABI breaking change and so we have always be reticent in making any changes. That said, if we had customer examples that this was causing serious problems, we’d consider it. If you have production examples you could share or link to that would be helpful. Mostly, we don’t hear about this and I couldn’t find a report of it in our current online bug tracking systems (but I may have missed it) – most people are like you and rarely notice it even though it’s been like this for ~25 years. :) Because you have to be writing code in a fairly special way, effectively writing somebody else's function declaration by hand instead of using the .h they provided, and write it differently than in that .h, to be able to notice.
Apr 06 2019
parent Manu <turkeyman gmail.com> writes:
On Sat, Apr 6, 2019 at 8:30 AM Herb Sutter via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 4 April 2019 at 17:25:05 UTC, Andrei Alexandrescu
 wrote:
 On 4/4/19 1:20 PM, Manu wrote:
 Also, FWIW, I think this is a fairly recent change (ie, last 5
 years).
Good to know, thanks! Found this, too: https://stackoverflow.com/questions/32447978/odd-vs-name-mangling-behavior/32448512 I'll email Herb.
Andrei sent me mail and I did a little digging. Much of the following is courtesy Jonathan Caves (thanks Jon!). Pasting what I sent to Andrei with light edits: Short answer: Yes, but most C++ code won't (and doesn't) notice because to see the different mangling you have to declare the function differently in two translation units, *and* have different "first" declarations in those TUs. So this is invisible for any function you declare in a shared header (because everyone sees the same first declaration). And it's invisible when you declare without top-level const and then define with top-level const on a parameter (because everyone uses the first declaration). Longer answer: Yes, this is a long standing known issue – our name decorator was developed long before C++98 codified a lot of stuff. Note that there are two mangling issues involved that come up in your example (or minor variations of it): [dcl.fct]/5 says that function parameters ignore top-level const and treat [] as *:
 After determining the type of each parameter, any parameter of
 type
 “array of T” or of function type T is adjusted to be “pointer
 to T”.
 After producing the list of parameter types, any top-level
 cv-qualifiers
 modifying a parameter type are deleted when forming the
 function type.
So these are supposed to be equivalent redeclarations of f and g: ~~~ // Case 1 void f(int); void f(int const); // same, redeclaration // Case 2 void g(int*); void g(int[]); // same, redeclaration ~~~ And in VC++ they are correctly equivalent, in the language. However, for name mangling, VC++ selects mangling based on the first declaration it seems, and it does mangle each of these pairs differently, so if two TUs have different first declarations of each of f and g, they can’t link. Again, to make this actually visible to users, they have to see different *first* declarations. For starters that means they’re not using a shared header. I understand that someone you know found it caused trouble with linking foreign code with C++, though presumably that’s because they had to write their own declaration by hand (in C++, or in the other language)? I’ve mostly seen that sort of thing when people have to write their own declarations, and in those cases they also had general fragility (e.g., writing int vs long, that sort of thing – because they have to be careful to get the declarations exactly right because they’re writing them manually, same trouble that Java JNI and .NET P/Invoke have in general, you have to make the parameters perfect). The worry about ‘fixing’ the name decorator is that it would be an ABI breaking change and so we have always be reticent in making any changes. That said, if we had customer examples that this was causing serious problems, we’d consider it. If you have production examples you could share or link to that would be helpful. Mostly, we don’t hear about this and I couldn’t find a report of it in our current online bug tracking systems (but I may have missed it) – most people are like you and rarely notice it even though it’s been like this for ~25 years. :) Because you have to be writing code in a fairly special way, effectively writing somebody else's function declaration by hand instead of using the .h they provided, and write it differently than in that .h, to be able to notice.
Thanks for the background. For my effort (binding to STL), a bigger problem with MSVC compatibility isn't that this mangling is weird (I have an awkward solution in this case), it's that the MS C++ runtime ABI has historically been somewhat volatile. The significant ABI-breaking change was when allocator<T>::destroy() was changed from `T*` to `T* const`, which appears to have happened perhaps vc2013->2015? (I don't have those old headers on my system anymore to check). There are numerous other cases of ABI breakage, but this one happened to introduce `* const`, which I couldn't express. Fortunately, it seems VS2015 and onward has made greater efforts towards ABI stability, so I plan to simply deprecate distant backwards-compatibility, and hope for a continued commitment from your side ;) For future reference, if you're making this decision about your ABI when authoring the runtime; for foreign languages trying to link to C++, I expect `T*` is generally much simpler. I suspect many FFI's would have trouble with `* const`, and D is one such case. We cross-language linker-er's would certainly appreciate trying to avoid `* const` in public ABI's since MSVC has this odd mangling rule.
Apr 06 2019
prev sibling parent Rubn <where is.this> writes:
On Tuesday, 2 April 2019 at 08:11:31 UTC, Walter Bright wrote:
 On 4/1/2019 5:55 PM, Atila Neves wrote:
 Factually incorrect.
I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection.
I have not said they are the same, do regex search of "the same", and you will see only you and someone else have said this formation of words.
 I wrote an article about it calling this "C's Biggest Mistake", 
 as this has led to decades of crashes and malware infestations:

 https://www.digitalmars.com/articles/b44.html
Yes arrays only being able to be represented as pointers is indeed a downfall of C/C++.
Apr 03 2019
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/1/2019 4:04 PM, Atila Neves wrote:
 It's a good point though that void is 
 treated differently in C's type system.
Void is treated as a degenerate type, and as you say, rejected constructs like arrays of void are rejected because they are nonsense. Arrays of functions are similarly rejected. Not at all like what goes on with ref in C++, because an array of refs could have perfectly reasonable semantics. But they are not allowed in C++, because ref is a storage class pretending to be a type.
Apr 02 2019
parent Rubn <where is.this> writes:
On Tuesday, 2 April 2019 at 08:05:25 UTC, Walter Bright wrote:
 But they [arrays of reference] are not allowed in C++, because 
 ref is a storage class pretending to be a type.
And we've come back fully circle. This is not the reason at all why an array of references is not allowed. You can't have pointers to references, and without pointers you can't have a full array implementation. How much sense would it make if you could only pass an array by reference? Not very much.
Apr 03 2019
prev sibling parent Manu <turkeyman gmail.com> writes:
On Mon, Apr 1, 2019 at 7:00 AM Atila Neves via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Monday, 1 April 2019 at 13:09:28 UTC, Dein wrote:
 On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
 On 3/31/2019 5:35 PM, Rubn wrote:
 you can literally use it everywhere else you can use a type.
No, you can't. An array of refs won't compile, either. void test(int& a[]); // error A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
Seriously? You should know as well as I do what that function actually translates to: void test(int&* const a); So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
Bad example, but the point stands: ---- // foo.cpp int fun() { int& foo[5]; // doesn't compile } ---- I'd never even thought of it until Walter mentioned it that one can't have an array of references. Huh.
You could totally invent language where that was possible though, and it would be *awesome*!
Apr 04 2019
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/1/2019 6:09 AM, Dein wrote:
 Not sure if you are trying to just deceive me with some syntax sugar in 
 C++ or what.
You can try it yourself: ---------- g++ -c test.cpp test.cpp:1:17: error: cannot declare pointer to int& void test(int&* p); ^ ---------- g++ -c test.cpp test.cpp:1:18: error: declaration of p as array of references void test(int& p[]); ^ ----------
Apr 01 2019
parent reply Rubn <where is.this> writes:
On Monday, 1 April 2019 at 20:37:03 UTC, Walter Bright wrote:
 On 4/1/2019 6:09 AM, Dein wrote:
 Not sure if you are trying to just deceive me with some syntax 
 sugar in C++ or what.
You can try it yourself: ---------- g++ -c test.cpp test.cpp:1:17: error: cannot declare pointer to int& void test(int&* p); ^ ---------- g++ -c test.cpp test.cpp:1:18: error: declaration of p as array of references void test(int& p[]); ^ ----------
You can try it yourself: void foo(int* const p) {} void foo(int p[]) {} ----------------------------------------------- test.cpp:3:6: error: redefinition of 'foo' void foo(int p[]) {} ^ test.cpp:2:6: note: previous definition is here void foo(int* const p) {} ^ 1 error generated.
Apr 01 2019
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/1/19 7:43 PM, Rubn wrote:
 On Monday, 1 April 2019 at 20:37:03 UTC, Walter Bright wrote:
 On 4/1/2019 6:09 AM, Dein wrote:
 Not sure if you are trying to just deceive me with some syntax sugar 
 in C++ or what.
You can try it yourself: ---------- g++ -c test.cpp test.cpp:1:17: error: cannot declare pointer to int&  void test(int&* p);                  ^ ----------  g++ -c test.cpp test.cpp:1:18: error: declaration of p as array of references  void test(int& p[]);                   ^ ----------
You can try it yourself: void foo(int* const p) {} void foo(int p[]) {} ----------------------------------------------- test.cpp:3:6: error: redefinition of 'foo' void foo(int p[]) {}      ^ test.cpp:2:6: note: previous definition is here void foo(int* const p) {}      ^ 1 error generated.
The fact that we have somebody teaching Walter Bright the C language to is supremely ironic in wake of the recent discussions.
Apr 01 2019
next sibling parent Rubn <where is.this> writes:
On Tuesday, 2 April 2019 at 00:13:13 UTC, Andrei Alexandrescu 
wrote:
 On 4/1/19 7:43 PM, Rubn wrote:
 On Monday, 1 April 2019 at 20:37:03 UTC, Walter Bright wrote:
 On 4/1/2019 6:09 AM, Dein wrote:
 Not sure if you are trying to just deceive me with some 
 syntax sugar in C++ or what.
You can try it yourself: ---------- g++ -c test.cpp test.cpp:1:17: error: cannot declare pointer to int&  void test(int&* p);                  ^ ----------  g++ -c test.cpp test.cpp:1:18: error: declaration of p as array of references  void test(int& p[]);                   ^ ----------
You can try it yourself: void foo(int* const p) {} void foo(int p[]) {} ----------------------------------------------- test.cpp:3:6: error: redefinition of 'foo' void foo(int p[]) {}      ^ test.cpp:2:6: note: previous definition is here void foo(int* const p) {}      ^ 1 error generated.
The fact that we have somebody teaching Walter Bright the C language to is supremely ironic in wake of the recent discussions.
Guess he's spent too much time in the D he's become a bit rust-y..
Apr 01 2019
prev sibling next sibling parent Frank Fuente <frfu anywhere.com> writes:
On Tuesday, 2 April 2019 at 00:13:13 UTC, Andrei Alexandrescu 
wrote:
 On 4/1/19 7:43 PM, Rubn wrote:
 On Monday, 1 April 2019 at 20:37:03 UTC, Walter Bright wrote:
 [...]
You can try it yourself: void foo(int* const p) {} void foo(int p[]) {} ----------------------------------------------- test.cpp:3:6: error: redefinition of 'foo' void foo(int p[]) {}      ^ test.cpp:2:6: note: previous definition is here void foo(int* const p) {}      ^ 1 error generated.
The fact that we have somebody teaching Walter Bright the C language to is supremely ironic in wake of the recent discussions.
Here's an egg and this is how you suck it...
Apr 02 2019
prev sibling parent Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Tuesday, 2 April 2019 at 00:13:13 UTC, Andrei Alexandrescu 
wrote:
 On 4/1/19 7:43 PM, Rubn wrote:
 On Monday, 1 April 2019 at 20:37:03 UTC, Walter Bright wrote:
 [...]
You can try it yourself: void foo(int* const p) {} void foo(int p[]) {} ----------------------------------------------- test.cpp:3:6: error: redefinition of 'foo' void foo(int p[]) {}      ^ test.cpp:2:6: note: previous definition is here void foo(int* const p) {}      ^ 1 error generated.
The fact that we have somebody teaching Walter Bright the C language to is supremely ironic in wake of the recent discussions.
+1000! But at least it's funny! :-P
Apr 02 2019
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On Fri, Mar 29, 2019 at 9:10 AM Victor Porton via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 That `ref T` (where T is a type) is not a type is a serious
 design error, because so `ref T` cannot be used as a template
 argument.

 It is a very serious problem.

 Even in C++ despite of all its silliness, T& is a type and can be
 used as a template argument.

 Can the language be changed to resolve this problem?
1. I couldn't possibly agree with you more!!! Sadly that's not how it is. 2. You can use `auto ref` on function arguments of template functions to capture the ref-ness of the calling argument: void fun(T)(auto ref T arg) 3. I have often deployed something like this (I just typed this in the email as example, I have no idea if this compiles or runs): Ref(T) { T* value; this(ref T val) { value = &val; } ref T opAssign(U)(auto ref U rh) { *value = rh; } ref T get() { return *value; } inout; alias get this; } ... template isRef(T) = is(T == Ref!U, U); void fun(T)(T arg) { pragma(msg, isRef!T ? "is a ref" : "not a ref"); } int i; auto ri = Ref!int(i); fun(i); // > "not a ref" fun(ri); // > "is a ref" Writing that code is like stabbing myself in the hands with rusty forks... I feel emotionally unstabilised, but it can solve your problems in various applications.
Mar 31 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/31/2019 3:48 PM, Manu wrote:
 Even in C++ despite of all its silliness, T& is a type and can be
 used as a template argument.

 Can the language be changed to resolve this problem?
1. I couldn't possibly agree with you more!!! Sadly that's not how it is.
Although ref can be captured with C++ templates, it isn't really a type. For example, you cannot create a pointer to a ref. Furthermore, the spec for it is crammed with wacky special cases to make it behave like a storage class instead of a type. I defy anyone to give a list of cases when C++ ref is inferred and when it is not, and if anyone did, I'm sure it would be wrong. Yes, it's that bad.
 Writing that code is like stabbing myself in the hands with rusty
 forks...
I'm convinced that if C++ ref didn't exist, and we added it to D, you'd hate it :-)
 I feel emotionally unstabilised, but it can solve your
 problems in various applications.
Please show a use case. I can't think of one.
Mar 31 2019
parent reply Manu <turkeyman gmail.com> writes:
On Sun, Mar 31, 2019 at 5:20 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 3/31/2019 3:48 PM, Manu wrote:
 Even in C++ despite of all its silliness, T& is a type and can be
 used as a template argument.

 Can the language be changed to resolve this problem?
1. I couldn't possibly agree with you more!!! Sadly that's not how it is.
Although ref can be captured with C++ templates, it isn't really a type. For example, you cannot create a pointer to a ref. Furthermore, the spec for it is crammed with wacky special cases to make it behave like a storage class instead of a type. I defy anyone to give a list of cases when C++ ref is inferred and when it is not, and if anyone did, I'm sure it would be wrong. Yes, it's that bad.
Are you saying that it's impossible to design a solution where ref is part of the type and that's less complex than C++? Is the complexity is that ecosystem inherent, or is it somewhat informed by backwards-compatibility, and other C++ design constraints? The fact that I can write `struct Ref(T)` and that it propagates the type system in a natural way suggests to me that the design is not impossible.
 Writing that code is like stabbing myself in the hands with rusty
 forks...
I'm convinced that if C++ ref didn't exist, and we added it to D, you'd hate it :-)
I think you probably have me backwards... irrespective of C++, and there was some discussion about ref vs pointers, I would push 110% for ref and eliminate pointers entirely. I would be much happier to write this hack in the event I need a pointer, than to hack around ref as I do today: struct Pointer(T) { size_t addr; this(ref T val) { addr = __traits(addressOf, val); } ref T get() { return __traits(addressAsRef, T, addr); } alias get this; Pointer!T opBinary(string op : "+")(size_t offset) { Pointer!T r; r.addr = addr + offset * T.sizeof; return r; } // etc... } Pointers are mostly useless and unsafe at best, especially when arrays are a first-class type.
 I feel emotionally unstabilised, but it can solve your
 problems in various applications.
Please show a use case. I can't think of one.
A use case for propagating ref in the type? You can't imagine why a template argument might want to distinguish `int` and `ref int`? I don't know how you've never found yourself static-if-ing on ref-ness of stuff in every bit of functional meta you've ever written. Ref creates an out-of-language (we have no language features to interact with 'storage class') suite of conditions that I frequently have to wrangle my way through. We have strong language mechanisms to reason about the type system, there is no language to reason about storage class that's not a kludge of awkward hacks and ugly meta utilities.
Mar 31 2019
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/31/2019 6:19 PM, Manu wrote:
 Are you saying that it's impossible to design a solution where ref is
 part of the type and that's less complex than C++?
I couldn't think of a way. I could have simply copied the C++ design. But why copy a nightmare of kludges?
 Is the complexity is that ecosystem inherent, or is it somewhat
 informed by backwards-compatibility, and other C++ design constraints?
There was no backwards compatibility issue when ref was designed. It's just a poor choice (like volatile).
 The fact that I can write `struct Ref(T)` and that it propagates the
 type system in a natural way suggests to me that the design is not
 impossible.
I have no idea why you want to do such things, but apparently you have a solution that works for you, so that's all good.
 Please show a use case. I can't think of one.
A use case for propagating ref in the type? You can't imagine why a template argument might want to distinguish `int` and `ref int`?
Indulge me. Why would it?
 I don't know how you've never found yourself static-if-ing on ref-ness
 of stuff in every bit of functional meta you've ever written. Ref
 creates an out-of-language (we have no language features to interact
 with 'storage class') suite of conditions that I frequently have to
 wrangle my way through.
 We have strong language mechanisms to reason about the type system,
 there is no language to reason about storage class that's not a kludge
 of awkward hacks and ugly meta utilities.
If I was faced with that, I'd step back and re-assess why the design required such machinations. It reminds me of when I peruse Phobos, I'm disturbed by the overly complex code there - something Andrei referred to in the "generality creep" thread. If C++ does it better, I don't see how. I find the implementation code of the STL to be horrifying in its complexity. I don't know how to write such code without thinking "something has gone terribly wrong". If I may, I once talked to a Ferrari mechanic. He said that most engines inside, where customers didn't see the innards, were sloppily made. But that the Ferrari engine was a thing of beauty inside and a joy to work on. Ironically, Ferraris are also known for cheap, poorly done cab interiors. An unusual focus for a car company :-) but one that appeals to my inner engineer. The engine in my Dodge was built the same way, it's blueprinted with all top quality parts inside it, but the outside is plain jane, no chrome parts, no dress-up, etc.
Apr 01 2019
next sibling parent Rubn <where is.this> writes:
On Monday, 1 April 2019 at 11:09:01 UTC, Walter Bright wrote:
 If I may, I once talked to a Ferrari mechanic. He said that 
 most engines inside, where customers didn't see the innards, 
 were sloppily made. But that the Ferrari engine was a thing of 
 beauty inside and a joy to work on. Ironically, Ferraris are 
 also known for cheap, poorly done cab interiors. An unusual 
 focus for a car company :-) but one that appeals to my inner 
 engineer. The engine in my Dodge was built the same way, it's 
 blueprinted with all top quality parts inside it, but the 
 outside is plain jane, no chrome parts, no dress-up, etc.
So your mechanic friend is comparing hand-built and individually tuned super-sport performance Ferraris to mass produced production-line commuter cars? When you don't have to focus on practicality with an inflated price tag you aren't limited with how you design your vehicle. You will be overpaying for maintenance costs though. I really doubt your Dodge engine was built the same way as a Ferrari's was unless maybe you are driving around a Dodge Viper. Please tell me your not one of those people that drive around a truck. American brand vehicles (yes not only cars) aren't well known for their reliability either.
Apr 01 2019
prev sibling parent Manu <turkeyman gmail.com> writes:
On Mon, Apr 1, 2019 at 4:10 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 3/31/2019 6:19 PM, Manu wrote:
 Are you saying that it's impossible to design a solution where ref is
 part of the type and that's less complex than C++?
I couldn't think of a way. I could have simply copied the C++ design. But why copy a nightmare of kludges?
 Is the complexity is that ecosystem inherent, or is it somewhat
 informed by backwards-compatibility, and other C++ design constraints?
There was no backwards compatibility issue when ref was designed. It's just a poor choice (like volatile).
 The fact that I can write `struct Ref(T)` and that it propagates the
 type system in a natural way suggests to me that the design is not
 impossible.
I have no idea why you want to do such things, but apparently you have a solution that works for you, so that's all good.
 Please show a use case. I can't think of one.
A use case for propagating ref in the type? You can't imagine why a template argument might want to distinguish `int` and `ref int`?
Indulge me. Why would it?
Propagating compile time information as types is the fundamental of D's meta. Not being able to propagate ref's is really problematic. Not being able to declare a local ref is really problematic. void fun(ref int x) { bool b = is(typeof(x) == ref); // nope! seems so obvious (you should watch their faces when I show them how to do it) int i = 10; ref int j = i; // nope! again, watch their faces when I say they can't do this ref auto resolveLongExpression = long.expression().that[0].we().dont_want_to.repeat; // nope! foreach(i; things) { resolveLongExpression++; // repeated access to a deep expression // use a pointer you say? pointers change semantics; assignment and increment mean something different now // if there's any meta involved, then pointer's semantic distinction is something you're gonna have to write `static if()` nonsense to detect and special-case! } // other kinds of expressions that we don't want to repeat, but may want to carry the ref-ness auto ref x = returnsValue(); ++x; // increment local auto ref y = returnsRef(); ++y; // increment reference } alias Param = Parameters!(fun)[0]; void g(Param arg) // nope! { ... lots of code } this is obviously much better: bool isRef(alias fn, int argIndex, int i = 0) { static if (i == __traits(getParameterStorageClasses, fn, argIndex).length) enum isRef = false; else enum isRef = __traits(getParameterStorageClasses, fn, argIndex)[i] == "ref" ? true : isRef!(fn, argIndex, i + 1); } string myFun = "void g(" ~ (isRef!(fun, 0) ? "ref " : "") ~ "Param arg)\n" ~ "{\n" ~ " lots;\n" ~ " of;\n" ~ " code;\n" ~ "}\n"; mixin (myFun); Whole function (or any other construction that fits this pattern) is a string because some storage class appears in the declaration. Say goodbye to autocomplete, debugging, refactoring, syntax highlighting, readability, and editability/maintainability. Sorry, I don't have a real-world case at hand right now, I didn't start the thread complaining this time; I'm just supporting someone who is feeling the same feels. I've learned to try and avoid this stuff in D, because D just can't reasonably do it. But it's very annoying to avoid, and it's a major weakness of D's meta, and it's very disappointing to new users when they work this out.
 I don't know how you've never found yourself static-if-ing on ref-ness
 of stuff in every bit of functional meta you've ever written. Ref
 creates an out-of-language (we have no language features to interact
 with 'storage class') suite of conditions that I frequently have to
 wrangle my way through.
 We have strong language mechanisms to reason about the type system,
 there is no language to reason about storage class that's not a kludge
 of awkward hacks and ugly meta utilities.
If I was faced with that, I'd step back and re-assess why the design required such machinations.
This is like a general argument against meta-programming. You don't get to decide when this stuff comes up in the wild; it just does. The worst cases are usually when automating bindings between systems; that might be to foreign languages, scripting solutions, tooling/editor interactions, or perhaps some high-level application scaffolding. One of D's biggest attractions is the ability to use powerful in-language meta-programming features to solve binding and boilerplate tasks that would traditionally require external tooling. When I use D, I lean into that promise HARD. The reason to choose D is to solve particular categories of problems I can't possibly solve in other languages. Ref (and other storage classes) bite me in the arse every single time. Imagine how much worse this will be when 'scope' proliferates!
 It reminds me of when I peruse Phobos, I'm disturbed by the overly complex code
 there - something Andrei referred to in the "generality creep" thread.
I'm not commenting on phobos complexity (which is indeed hideous, and I avoid phobos). There are a lot of programming tasks; one programming task is writing programs, but another overwhelmingly popular and most tedious and painful task is developing and maintaining bindings and boilerplate interaction to large established ecosystems. Basically everyone I know who has been drawn to D are initially attracted by the ability to automate boilerplate and terrible cruft from external tooling and reducing build complexity. That's the stuff that sends them packing in search of a brighter future. A lot of meta is used to automate boilerplate and binding machinery. If a user is 'just writing programs', then C++ is just fine. They're attracted to D by the promise of simplifying or addressing the mountain of code that C++ _can't_ express. Boilerplate meta is one of the most common tasks that stokes initial interest in D. To dismiss that, is to misunderstand D's value proposition to a huge audience. This is certainly the case where D is discussed in the project I'm work on right now, and has been the case since I ever started discussing D with colleagues. It's the most immediate and painful problem they see an obvious solution for. You should value that use-case if you want to attract users. Everything else D has to offer is a nice bonus that these people would appreciate in time, but they need to be drawn in by successfully solving the problems immediately before them that D appears to be well-placed to solve. I have watched so many cases of people trying out some such solution, and then they encounter ref, I start to explain to them what they need to do, they are surprised, then they're angry at me for wasting their time, and then they lose interest. Storage class is really, *really* bad. The terribleness of it in terms of the impression it leaves on people is really important. I've been trying to tell you this for so long, I don't think there's anything else I can do.
 If C++ does it better, I don't see how. I find the implementation code of the
 STL to be horrifying in its complexity. I don't know how to write such code
 without thinking "something has gone terribly wrong".
It's not about STL. I hope I've made that clear. But that said, you have invented the most powerful meta-programming language out there, you've gotta own that, and you can't be upset when people try to apply it to solve specific problems it's uniquely well-fit for. I agree there's a HUGE danger of abusing D's meta-programming features, I constantly have to restrain myself, but the kind of work I refer to is not of that kind.
 If I may, I once talked to a Ferrari mechanic. He said that most engines
inside,
 where customers didn't see the innards, were sloppily made. But that the
Ferrari
 engine was a thing of beauty inside and a joy to work on. Ironically, Ferraris
 are also known for cheap, poorly done cab interiors. An unusual focus for a car
 company :-) but one that appeals to my inner engineer. The engine in my Dodge
 was built the same way, it's blueprinted with all top quality parts inside it,
 but the outside is plain jane, no chrome parts, no dress-up, etc.
That's a nice thought, but that's not important to this particular class of problem. What I'm talking about here is attracting users who are able to successfully solve their long-standing and terribly painful problems. Cases where storage class comes up as a problem is almost always of that category. Apparently I seem to suffer this category of task 10x more than the average guy. I've tried to make this point before, and I'll try again; Most programmers are not 'great' programmers, they're just programmers, they're employees of businesses, at 5pm they go home and hang out with the kids and shag the wife. They're not too worried about building a ferrari engine; they mostly just wanna get paid, they have a job to do, and they almost invariably have an established ecosystem of existing cruft they interact with. There is a high probability they have this rats-next of hand-maintained boilerplate and tooling that they HATE maintaining, and that's the angle I've continually had the most success gaining interest from new users. Our goal is to attract these people, and ideally, improve their ability to do a good job more quickly. We want them to feel more productive and make less mistakes, but we can't help them if they never come to the party! D's key advantage out-of-the-gate as I have seen it for so many years, is that it's *theoretically* possible to build bridges between existing ecosystems where we can take a foothold against 40 years of establishment. This is the land of boilerplate and connective meta. I've written a lot of it. They won't come to us if they need to swim the crocodile infested river. We must build a bridge to them and make crossing the river unreasonably simple and appear like the obvious thing to do. Storage class hurts more than anything else towards this end. Anyway... I'm going to bed. I'll complain about it again next year.
Apr 04 2019
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 1 April 2019 at 01:19:41 UTC, Manu wrote:
 On Sun, Mar 31, 2019 at 5:20 PM Walter Bright via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 On 3/31/2019 3:48 PM, Manu wrote:
<snip> I don't know how you've never found yourself static-if-ing on ref-ness of stuff in every bit of functional meta you've ever written. Ref creates an out-of-language (we have no language features to interact with 'storage class') suite of conditions that I frequently have to wrangle my way through.
One can inspect the ref-ness of function parameters: --------------- void main() { fun(5); int i; fun(i); } void fun(T)(auto ref T value) { import std.stdio; writeln("T: ", T.stringof, " ref? ", __traits(isRef, value)); } --------------- Output: T: int ref? false T: int ref? true Unfortunately for me, this is a thing though: https://issues.dlang.org/show_bug.cgi?id=19507 *I* think that's a bug (which is why I filed it), but I'm not even sure.
Apr 01 2019
prev sibling next sibling parent reply David Bennett <davidbennett bravevision.com> writes:
On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 Can the language be changed to resolve this problem?
Currently working on a project that works with a lot on pointers to fixed length arrays and its currently very easy to forget to de-refrence the pointer because the pointer and array syntax is the same. For example I have code that looks something like this: --- float[32]* myarray = magicFunction(); foreach(el; myarray[0]){ } --- But something like this would me much less prone to error (ie missing the [0] above): --- ref float[32] myarray = magicFunction(); foreach(el; myarray){ } --- If i convert the above code to use slices I loose the compile-time bounds check.
Mar 31 2019
parent ag0aep6g <anonymous example.com> writes:
On 01.04.19 05:38, David Bennett wrote:
 ---
 
      float[32]* myarray = magicFunction();
 
      foreach(el; myarray[0]){
 
      }
 ---
Aside: Is there are reason why you're writing `myarray[0]` instead of `*myarray`? Looks odd.
 But something like this would me much less prone to error (ie missing 
 the [0] above):
 
 ---
 
      ref float[32] myarray = magicFunction();
 
      foreach(el; myarray){
 
      }
 ---
If you simply forget the `[0]` (or `*`), the code doesn't compile. So that specific mistake can't do any real damage. Doesn't seem like a good example to motivate making the language more complex.
Apr 01 2019
prev sibling parent reply Guillaume Piolat <first.last gmail.com> writes:
On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious 
 design error,
No.
 It is a very serious problem.
No.
 Can the language be changed to resolve this problem?
There is no reason to do so.
Apr 01 2019
parent reply Manu <turkeyman gmail.com> writes:
On Mon, Apr 1, 2019 at 4:25 PM Guillaume Piolat via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious
 design error,
No.
 It is a very serious problem.
No.
 Can the language be changed to resolve this problem?
There is no reason to do so.
I hope I've made my case... again.
Apr 04 2019
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/4/19 4:28 AM, Manu wrote:
 On Mon, Apr 1, 2019 at 4:25 PM Guillaume Piolat via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
 That `ref T` (where T is a type) is not a type is a serious
 design error,
No.
 It is a very serious problem.
No.
 Can the language be changed to resolve this problem?
There is no reason to do so.
I hope I've made my case... again.
It's a problem we don't have a clear vision in the community on at least the obvious matters. Reference types are recognized even within C++ as ho-hum. These debates are also a time-waster, collectively and cumulatively many hours have been spent on this and of course nobody "got convinced" of anything. There are many areas that are positively in need of and receptive to improvement. If only that enthusiasm was channeled that direction...
Apr 04 2019