Re: Time for std.reflection

2012-11-19 Thread Malte Skarupke

I like it. First a couple details:


class ClassInfo {
@property:
string name();
string baseName();
string parentName(); // if applicable, null otherwise
string[] interfaces();
bool isShared();
Protection protection();
DataMemberInfo[] data();
MethodInfo[] methods();
Object defaultConstructor();
...
}


Why do you not use recursion here? Why not
ClassInfo base();
ClassInfo parent();
and then you get baseName by calling base.name;


Then for an e.g. method declaration we'd have:

class MethodInfo {
@property:
string name();
bool isStatic(), isFinal(), isOverride();
Protection protection();
string[] parameterTypes();
string[] parameterNames();
}


Do you think there'll be any way to get the actual types of the 
arguments and the return type back from the struct? I think that 
without compiler magic or an uncomfortable amount of templates it 
probably won't be possible, but I might be wrong.



Now to my big complaint:

I really dislike making the runtime api accessible through a 
mixin. I'd much prefer making it a non-member template. Reasons 
are
a) I don't want to store the runtime information in my type. A 
vtable pointer should be enough to get that information. Maybe 
use it as an index into a global immutable associative array. For 
modules and non-class types the name can act as a unique 
identifier.
b) It'd make it possible to get runtime information on types that 
I get from libraries.

c) It would lead to less coupling.
d) It would discourage writing types that rely on runtime 
reflection, but it would not make it impossible.


Re: How do you remove/insert elements in a dynamic array without allocating?

2012-11-08 Thread Malte Skarupke
On Wednesday, 7 November 2012 at 09:45:48 UTC, monarch_dodra 
wrote:
On Wednesday, 7 November 2012 at 03:45:06 UTC, Malte Skarupke 
wrote:
Having no clear ownership for the array is not something I am 
willing to accept.


Strong ownership puts you back into C++'s boat of bordering 
psychotic duplication on every pass by value. In a GC 
language, and in particular, D, that favors pass by value, this 
might not be the best approach.


I'll re-iterate that you may consider looking into 
std.container.Array. It behaves much like would std::vector 
(reserve, etc)... You can extract an actual range from Array, 
but there is a clear container - range distinction.


An added bonus is that it uses implicit reference semantics. 
This means that when you write a = b, then afterwards, you 
have a is b, and they are basically alias. This is a good 
thing, as it avoids payload duplication without your explicit 
consent. The implicit means that it will lazily initialize if 
you haven't done so yet.


You claim you want explicit ownership: Array gives you that, 
but not in the classic RAII sense. If you need to duplicate an 
Array, you call dup manually.



Also, Array uses a deterministic memory model, just like 
vector, that releases content as soon as it goes out of scope. 
I could have done without that, personally, but to each their 
own.


I think we have just had different experiences. In my experience 
a shared_ptr is usually not what you want. Instead I prefer a 
unique_ptr in many cases. Just because multiple things are 
referencing your data, that doesn't mean that they should all 
share ownership of it.


For me well defined ownership is just good coding practice, 
similar to the const keyword. I've seen it prevent bugs and 
therefore I use it for all cases that I can. I prefer to have 
those exceptions where I can not have clear ownership stand out, 
rather than them being the norm.


Re: How do you remove/insert elements in a dynamic array without allocating?

2012-11-06 Thread Malte Skarupke

On Tuesday, 6 November 2012 at 09:10:08 UTC, bearophile wrote:

Malte Skarupke:

I will most certainly never use dynamic arrays or slices again 
for anything that needs to grow or shrink dynamically.


And doing so you will miss an important and handy part of D :-/

Bye,
bearophile


I implemented my own DynamicArray struct which behaves more like 
I want it to and is still giving me all of the benefits that I 
want from dynamic arrays.


Having no clear ownership for the array is not something I am 
willing to accept. Same thing for the non-deterministic copying 
and destruction behavior for the elements. That's the kind of 
thing which looks convenient at first, but will create many small 
performance problems. Once those add up to a big problem, it's 
incredibly frustrating to fix because your slow downs are from 
all over the place.


I don't anticipate that I will ever run into a situation where I 
want to append to a range without caring about whether it 
allocates and copies or not. If I want to append to a range, I 
will write the extra line to create a copy manually.
Because of that I don't need the syntactic ambiguity of treating 
a range the same as an array.


How do you remove/insert elements in a dynamic array without allocating

2012-11-05 Thread Malte Skarupke

Let's say I want to


How do you remove/insert elements in a dynamic array without allocating?

2012-11-05 Thread Malte Skarupke

Following code:

void main()
{
import core.memory;
GC.disable();
scope(exit) GC.enable();

int[] a = [1, 2, 3, 4, 5];
foreach(i; 0 .. 10)
{
--a.length;
a ~= i;
}
}

That loop will keep on allocating in every iteration until your 
memory is full.


Is there a way to do something similar to this without 
allocating? I have also tried slicing:

a = a[0 .. $ - 1]; // instead of (--a.length;)

But neither one works.

How do you work with the dynamic array without having to rely on 
the GC all the time? I want something similar to the stl vector, 
which only re-allocates once your array grows past a certain 
size, not on every append.


Thanks!

Malte


Re: How do you remove/insert elements in a dynamic array without allocating?

2012-11-05 Thread Malte Skarupke
On Tuesday, 6 November 2012 at 01:25:39 UTC, Jonathan M Davis 
wrote:

On Tuesday, November 06, 2012 02:11:06 Malte Skarupke wrote:

Following code:

void main()
{
import core.memory;
GC.disable();
scope(exit) GC.enable();

int[] a = [1, 2, 3, 4, 5];
foreach(i; 0 .. 10)
{
--a.length;
a ~= i;
}
}

That loop will keep on allocating in every iteration until your
memory is full.

Is there a way to do something similar to this without
allocating? I have also tried slicing:
a = a[0 .. $ - 1]; // instead of (--a.length;)


There's no real difference between those two.


But neither one works.

How do you work with the dynamic array without having to rely 
on
the GC all the time? I want something similar to the stl 
vector,

which only re-allocates once your array grows past a certain
size, not on every append.


Arrays do work that way. They have capacity, and they have 
reserve. They only
append when they don't have enough memory or they're not the 
last slice in the
block. The problem is that if you remove elements, the runtime 
doesn't know if
there's another slice pointing to the element which was just 
removed, so it
has to assume that that there is, and it'll be forced to 
reallocate when

appending.

If you _know_ that there are no slices of anything beyond the 
end of the
array, then you can call assumeSafeAppend on the array, and 
then the runtime
will mark it as the last slice in the block, and appending 
won't reallocate
unless it actually runs out of space in the block (or you end 
up removing
another element without calling assumeSafeAppend or you end up 
with a slice
which contains elements beyond the end of that array - e.g. if 
you slice the
array and then append to the slice). So, in this particular 
case,
assumeSafeAppend would solve your problem. Whether it works in 
your program in
general depends on what your program is doing. And if you want 
to make sure
that the array has enough space before appending to it a bunch, 
then you can
use reserve. However, if you're specifically building an array, 
you should
probably look at std.array.Appender. Don't remove elements from 
that though.
It's just for making appending more efficient, not for being 
used once the

array has been fully constructed.

If you haven't read it yet, I'd strongly advise reading this 
article on arrays

in D:

http://dlang.org/d-array-article.html

- Jonathan M Davis


Thanks, that explains it well. I must admit that I didn't fully 
understand slices before. I've read the linked article and am 
baffled that this is seen as acceptable behavior. I will most 
certainly never use dynamic arrays or slices again for anything 
that needs to grow or shrink dynamically.


Re: Const ref and rvalues again...

2012-11-04 Thread Malte Skarupke
On Monday, 5 November 2012 at 03:26:10 UTC, Jonathan M Davis 
wrote:

On Sunday, November 04, 2012 20:43:36 Andrei Alexandrescu wrote:

On 11/4/12 7:58 PM, martin wrote:
 I find it sad that while this topic seems to be of high 
 priority for
 quite a lot of language users, it is seemingly neglected by 
 the head of

 language development (Walter, Andrei etc.).

I was hoping auto ref solves this problem. I think it's 
currently only

implemented for templates.


And when we argued for altering it so that it operated like 
const ref in C++
(which allows const ref in D to continue to function like it 
does now), some
folks complained, because they've found the current semantics 
of auto ref to
be useful (something to do with propagating the exact, original 
type, I

think).

Now, since auto ref currently only works with templates, maybe 
we can keep its
current semantics with templated functions but alter them for 
non-templated
functions so that it works like const ref does in C++. The 
downside is that
the semantics for auto ref between templated functions and 
non-templated
functions are slightly different, but they're close enough that 
I'm not sure

that it matters.

- Jonathan M Davis


Yes, please. Auto ref for non-template functions would solve the 
problem exactly. I also like it because then the intent of the 
programmer is clear.


Re: D vs C++11

2012-11-03 Thread Malte Skarupke

On Friday, 2 November 2012 at 17:03:38 UTC, Erèbe wrote:

Hello student here,

I have started to learn D a few months ago with Andrei's book 
(I really liked arguments about design decisions), but as the 
same time I was learning new features of C++11, and now I'm 
really confused. (As learning projects, I've done an IRC Bot in 
D and an IPv6 stack in C++11)


D is a great language and introduce a lot of features I really 
like (range, property, UFCS, great metaprogramming, ...) but 
C++11 and the new standard librairy are well supported now.


I have some solid experience with C++, so I know how cumbersome 
C++ could be (C++11 and universal reference ?), but D has a lot 
of features too and (as C++) a slow learning curve.


I would like to konw the point of view of the community about 
C++11 in regard of D ? Is the gap between D and C++11 is 
getting thinner ? Do you think D will keep attracting people 
while at the same time C++11 has more support (debugger, IDE, 
Librairies, Documentation) ?


I've learned C++ in the last two years and learned D in the last 
couple months, and I slightly prefer C++ over D. When I started 
using C++11, I took for granted that all the features just work. 
Using D, I realize that that is actually unusual. In D you still 
encounter compiler bugs or inconsistent behavior way too often, 
and I have workarounds all over my code.
I also prefer C++ being much more explicit about everything it 
does. For example I prefer the lambdas with a capture list over D 
delegates.
And in D you've got the core runtime allocating memory without me 
being aware of it all the time. Sure, it gets garbage collected 
at some point, but I'd rather that it didn't do it at all.


D also makes the const keyword more annoying than it should be. 
In C++ you typically use it as an indicator for the intent of the 
interface and to prevent you from making mistakes. There are some 
programmers who find it very annoying and who never use const. In 
D I am leaning towards being one of those programmers, even 
though I belong to the group who uses const more strictly in C++.


That being said I do think that I write better code in D, but I 
wouldn't use it for real projects any time soon. It just isn't 
ready, and I'm sure that my existing code will break several more 
times in future compiler versions.


At the moment the issue is that C++11 is pretty good, and D is 
not ready. So people will start using C++11 and they'll see that 
it's good enough and they'll never migrate. The best features of 
D don't matter if the language is a bit annoying as soon as you 
leave the space of clean and nice examples.


Re: D vs C++11

2012-11-03 Thread Malte Skarupke

On Saturday, 3 November 2012 at 22:45:59 UTC, Tommi wrote:

On Saturday, 3 November 2012 at 22:01:21 UTC, Malte Skarupke
wrote:


D also makes the const keyword more annoying than it should be.


What kind of annoyances regarding const have you encountered in 
D?


To start off it's simple things like this:

void main()
{
struct A
{
this(int x) { this.x = x; }
int x;
}
const(A) a = A(5);
A b = a;
}

This doesn't compile. And it will probably never compile. The 
issue is that struct A has a context pointer and because of 
transitive const, you are not allowed to copy that pointer. And 
you can not specify your own copy constructor because all copy 
constructors happens post-blit, at which point you'd already have 
a non-const pointer. So that will probably never change.


But that's not a big problem.

It's more been stuff like me implementing an equivalent of 
std::function from C++. (as I said I like to be more explicit 
about things than delegates) I uploaded the code for it here: 
http://dpaste.dzfl.pl/60a46049 As you can see there isn't a 
single mention of const in there. All I wanted was a const and an 
immutable version of opCall and better const correctness for 
opAssign, but I just couldn't get it to compile. It'd be great if 
you could have a look at it. If you succeed in getting that code 
to be const correct, please tell me how you did it. (Also worth 
mentioning: I ran into at least two more issues in just this one 
file: 1. I couldn't specify the templated opAssign that C++'s 
std::function has because of a compiler bug (which will be fixed 
in DMD 2.061) and I had to define opAssign twice because there is 
no way to specify a function which accepts both an rvalue and an 
lvalue) That being said the code still is much cleaner than it 
would be in C++.


Another issue I've had was trying to implement my own hashtable 
which has more deterministic memory behavior. In that I found it 
very difficult to get the ranges for byKey and byValue to be 
const correct. I think I had issues with the inout keyword when 
I was passing pointers marked as inout to the range object. I 
think I'll revisit that one tomorrow and maybe I'll then post 
code here.


How do you copy a const struct to a mutable?

2012-10-31 Thread Malte Skarupke

I want to get this to compile:

void main()
{
struct A
{
this(int x) { this.x = x; }
int x;
}
const(A) a = A(5);
A b = a;
}

It should clearly be legal to create a non-const copy of the 
struct. What am I missing in order to do this?


Cheers,

Malte


Re: Const ref and rvalues again...

2012-10-20 Thread Malte Skarupke

On Friday, 19 October 2012 at 13:00:52 UTC, Timon Gehr wrote:

On 10/19/2012 09:53 AM, Jacob Carlborg wrote:

On 2012-10-19 04:48, Timon Gehr wrote:


Then how to specify that the value of x cannot be escaped?
I'm in favour of doing it the other way round and disallow 
escaping of

ref parameters without an unsafe cast.


scope is supposed to be used to prevent this.



Sure, but how?

void goo(scope int* x){
global0 = x; // should clearly be disallowed
}

void foo(scope ref int*** x){
global1 = x; // ?
global2 = x;  // ?
global3 = *x; // ?
globall4 = **x; // ?
}

Maybe we need this:

void foo(scope ref int*** x);   // ?
void foo(ref int(***)scope  x); // no escaping of x, *x, **x
void foo(ref int*(**)scope x);  // may escape **x
void foo(ref int**(*)scope x);  // may escape *x, **x

What about x?


No scope should mean that you can not escape the address of 
something. So


void goo(scope int* x)
{
global0 = x;
}

Should be allowed. Scope in this case applies to the pointer. Not 
to the thing it's pointing to. The scope keyword is not 
transitive. That wouldn't make sense. It is perfectly legal to 
have a scoped pointer to something allocated on the heap.



Whereas

void goo(scope ref int x)
{
global0 = x;
}

Should NOT be allowed. It looks like it's the same code, but in 
this case x is an integer. In the last example x was a pointer to 
an integer.


So it comes down to this:

void goo(scope int* x)
{
global0 = x; // copying is allowed
//global1 = x; // referencing is not allowed
}




Re: Const ref and rvalues again...

2012-10-18 Thread Malte Skarupke

On Thursday, 18 October 2012 at 06:11:26 UTC, monarch_dodra wrote:
On Thursday, 18 October 2012 at 04:30:17 UTC, Jonathan M Davis 
wrote:

On Thursday, October 18, 2012 06:24:08 jerro wrote:

What would be the problem with const ref taking rvalues?


Read the thread that I already linked to:

http://forum.dlang.org/thread/4f84d6dd.5090...@digitalmars.com

- Jonathan M Davis


I read the thread, and not a single one of the problematic 
cases are actually valid C++.


Yes: the faulty MSVC has taught people to do retarded things, 
or be afraid of things that were illegal to begin with (in 
particular, pass an rvalue to a ref, WHICH IS ILLEGAL IN C++), 
such as increment(5).


There is actually nothing wrong with creating a temporary when 
something is bound to a const ref, provided the compiler 
follows the rules:


*Only LValues with an EXACT type match may be passed to a 
reference.
*In regards to *const* references, RValues may be copied in a 
temporary, and that temporary bound the the ref.


I'm not saying we particularly *need* this in D (C++ has a by 
ref paradigm that makes it more important, but D *rarelly* 
ever passes by const ref).


But if the compiler respects the above two rules (which it 
should), then RValue to const ref is both perfectly doable and 
safe (as safe as refs get anyways).


The problem with binding rvalues to const ref is that you could 
take and store the address of it. That's why I'd recommend using 
in ref instead.


@Jonathan: I had already read the linked discussion. There are 
many valid points in there, but also many invalid ones (as 
monarch_dodra has pointed out). But I think all problems in that 
thread should be solved by using in ref instead of const ref 
because then you'd be sure that the passed-in temporary can not 
escape the current function.


@foobar: I like the idea, but it's probably going to break down 
in many cases. If you have a non-trivial copy constructor you 
want the ability to have complete control over when it gets 
copied and when it doesn't. I just don't trust compilers enough 
to think that they'd always make the same choice that I'd make.
And also about losing semantic information: That's why I proposed 
the second point: Give the user the option to provide a function 
which should be preferred for rvalues. That way you don't lose 
semantic information.


Re: Const ref and rvalues again...

2012-10-18 Thread Malte Skarupke

On Friday, 19 October 2012 at 00:03:49 UTC, Timon Gehr wrote:


Const is different in D and in C++. Relating const and rvalues 
is arbitrary and does not make a lot of sense.


Regarding 'in ref'/'scope ref': What should 'scope' apply to in

void foo(scope ref int* x);


Not sure what you mean with relating. I'm not making any claims 
about there being a relationship between rvalues and constness.


This is about finding a way that you can define a function which 
safely accepts lvalues and rvalues without having to make a copy. 
If we specify the argument as ref in, then we can safely pass 
for example the number 5 to it. And this would never break 
existing code, so that something like swap(5, 4) would never be 
possible code.


For the example that you gave you'd be unable to store the 
address of x. So doing


int** storage;
void foo(scope ref int * x)
{
storage = x;
}

would be illegal.

@jerro: the same thing: I'm not trying to fix the problem that 
you mention. I'm trying to define a function which can safely 
accept rvalues and lvalues without having to make a copy.


Re: How mutable is immutable?

2012-10-17 Thread Malte Skarupke

The issue is that you're thinking as you would in Java.

I guess the rule in D for immutable is this: Immutable data won't 
change as long as it exists.


The last part of that sentence would be a stupid thing to say in 
Java because things don't just cease to exist while you're still 
doing something with them. That is not the case in D.


That being said it's very unlikely that you will ever run into 
this situation. You have to end the lifetime of the object 
manually to run into these issues. And in that case it'll be very 
easy to figure out what's wrong.


Const ref and rvalues again...

2012-10-17 Thread Malte Skarupke

Hello,

I realize that this has been discussed before, but so far there 
is no solution and this really needs to be a high priority:


We need a way for a function to declare that it doesn't want it's 
argument to be copied, but it also doesn't care whether the 
argument is an rvalue or an lvalue.


The C++ way of doing this would be to declare the argument as a 
const . Apparently it is not desired that we do the same thing 
for const ref.


Currently, if you want that behavior, you have to write 2^n 
permutations of your function, with n being the number of 
arguments that the function takes.


Here's my attempt at passing a struct to a function that takes 
three arguments without the struct being copied:


int copyCounter = 0;
struct CopyCounter
{
this(this) { ++copyCounter; }
}
void takeThree(ref in CopyCounter a, ref in CopyCounter b, ref in 
CopyCounter c)

{
writeln(took three);
}
void takeThree(in CopyCounter a, ref in CopyCounter b, ref in 
CopyCounter c)

{
takeThree(a, b, c);
}
void takeThree(ref in CopyCounter a, in CopyCounter b, ref in 
CopyCounter c)

{
takeThree(a, b, c);
}
void takeThree(ref in CopyCounter a, ref in CopyCounter b, in 
CopyCounter c)

{
takeThree(a, b, c);
}
void takeThree(in CopyCounter a, in CopyCounter b, ref in 
CopyCounter c)

{
takeThree(a, b, c);
}
void takeThree(in CopyCounter a, ref in CopyCounter b, in 
CopyCounter c)

{
takeThree(a, b, c);
}
void takeThree(ref in CopyCounter a, in CopyCounter b, in 
CopyCounter c)

{
takeThree(a, b, c);
}
void takeThree(in CopyCounter a, in CopyCounter b, in CopyCounter 
c)

{
takeThree(a, b, c);
}
static CopyCounter createCopyCounter()
{
return CopyCounter();
}
void main()
{
CopyCounter first;
CopyCounter second;
CopyCounter third;
takeThree(first, second, third);
takeThree(createCopyCounter(), second, createCopCounter());
assert(copyCounter == 0); // yay, works
}


My propsed solution is this:
- Make functions that take ref in arguments also accept rvalues.
- The user can still provide an overload that accepts an rvalue, 
using the in keyword, and that one will be preferred over the 
ref in version.



What do you think?

Malte


Re: What is the case against a struct post-blit default constructor?

2012-10-11 Thread Malte Skarupke

First of all thank you for the detailed responses.

I wrote a response yesterday but somehow the website seems to 
have swallowed it.


On Thursday, 11 October 2012 at 12:43:31 UTC, Andrei Alexandrescu 
wrote:
We could (after all, C++ does it). There are a few 
disadvantages to doing so, however.


1. Defining static data is more difficult. Currently, all 
static data is statically-initialized. With default 
constructors, we'd need to define the pre-construction state of 
such objects anyway, and then change the compiler to call 
constructors prior to main(). I find the current design simpler 
and easier to use.


This is a good reason. I like the idea of no code gets run 
before main Running code before main only lead to problems in 
C++. However those problems were always merely inconvenient, 
never big issues. I think it's not good to allow people to run 
code before main, but I think it's a bigger problem to have no 
default constructors.


2. Creating a temporary object cannot be anymore assumed to be 
a O(1), no-resources-allocated deal. Instead, generic code must 
conservatively assume that objects are always arbitrarily 
expensive to create. That makes some generic functions more 
difficult to implement.


I think that is OK. The generic algorithm should assume that your 
object is cheap to create. In C++ all algorithms assume this and 
there are few issues. Sure, every now and then you pass an 
expensive-to-create object to an algorithm which creates 
instances, but that bug is very easy to debug.


3. Two-phase object destruction (releasing state and then 
deallocating memory), which is useful, is made more difficult 
by default constructors. Essentially the .init 
pre-default-constructor state intervenes in all such cases 
and makes it more difficult for language users to define and 
understand object states.


I'm not sure that I understand this. My two ways of interpreting 
this are:
A) You mean that the compiler currently assumes that it doesn't 
have to call the destructor for objects that are at init. But 
after introducing a default constructor it would always have to 
call the destructor. I think that's OK. That's an optimization 
that's unlikely to give you much gain for types that need a 
destructor.
B) You mean that if we introduce a default constructor, there 
would still be situations where an object is at init and it's 
destructor gets called. For example if people throw exceptions. 
And users might be confused by this when their destructor gets 
run and their object is at init, instead of the state that they 
expect. I think this is OK. It is the same situation that we 
currently have with static opCall(). Yes, with the static 
opCall() hack people kinda expect that their object isn't always 
initialized, so their destructors probably react better to the 
state being at init, but I think if the documentation states 
clearly there are situations where the destructor will be called 
on an object whose default constructor was not called then 
people can handle that situation just fine.


4. Same as above applies to an object post a move operation. 
What state is the object left after move? C++'s approach to 
this, forced by the existence of default constructors and other 
historical artifacts, has a conservative approach that I 
consider inferior to D's: the state of moved-from object is 
decided by the library, there's often unnecessary copying, and 
is essentially unspecified except that it's valid so the 
moved-from object can continue to be used. This is in effect a 
back-door introduction of a no-resources-allocated state for 
objects, which is what default constructors so hard tried to 
avoid in the first place.


For moving you'd just have to define a state that the source 
object is in after moving. Since it's a destructive move I would 
expect the object to be at init after moving, as if the 
destructor had been called. If that is well defined, then I think 
users will be fine with it. This is actually the situation that 
we currently have, and users seem to be fine with it. This can be 
achieved with a tiny change to the current implementation of 
std.algorithm.move: Make it memcpy from init instead of a 
statically allocated value.



I'd also like it if we could write all structs so that init is a 
valid state of the struct, as Walter suggests. However this is 
going to make certain things impossible in the language. Simple 
things like having shared data between multiple instances of a 
struct. Or counting how often objects of a certain type was 
allocated. Or iterating over all instances of a type.
In fact there are parts of the standard library that don't work 
because they'd need a default constructor. One of the linked 
posts mentions this example from std.typecons:


import std.typecons;
{
RefCounted!(int, RefCountedAutoInitialize.yes) a;
assert(a == 0); // works
RefCounted!(int, RefCountedAutoInitialize.no) b = a;
assert(b 

Re: What is the case against a struct post-blit default constructor?

2012-10-10 Thread Malte Skarupke

Hi, thanks for the detailed answers.

So I am very much for keeping init. I just want that if the user 
specifies a default constructor, it is being called after the 
value has been initialized to init.



On Wednesday, October 10, 2012 21:11:29 foobar wrote:

Arrays - without changing existing syntax we can use these
semantics:

auto a = new int[](5); // compiler calls T() for each instance
int[12] b; // ditto


And what on earth does that buy you over init? That's what init 
_does_. And
since the value needs to be known at compile time (otherwise 
arrays won't work
for directly initializing _anything_ that needs to be 
initialized at compile
time), a constructor really doesn't buy you anything here at 
all. init
provides a nice, consistent way of initializing or assigning a 
value to a
variable as well as providing a consistent default state for a 
type when you
need to be able to set a variable of that type to a consistent, 
safe state
(e.g. with std.algorithm.move). And as built-in types don't 
_have_
constructors, there's no other way for generic code to 
generically initialize

anything.


What does this buy you over init? This would help with structs 
that can't be initialized to a valid value at compile time.

So what I expect to happen in a line like
S[12] s;
Is that they all get blitted to init, followed by a loop that 
calls the default constructor on each of these. If there is no 
default constructor, they stay at init and the loop doesn't 
happen.

This would work with user defined types and with built-in types.
If a constructor throws an exception, then the array is correctly 
initialized up to the element before the throwing one. The 
remaining elements are at init.


For member variables I expect this to happen:
If one of your member variables has a default constructor but you 
do not, then the compiler will generate an empty default 
constructor for you. This gives your object the same behavior in 
arrays as described above, which means that the member variable 
will always be correctly initialized using both init and the 
deafult constructor.
If one of your member variables has a default constructor, and so 
do you, then the member variable's default constructor is called 
before your default constructor.
If an exception is thrown, the containing object remains at the 
state it is in (most likely init)
If you do not want the default constructor of your member 
variable to be called, you can declare it as S s = S.init;


For emplace I expect that the default constructor gets called.

For std.algorithm.move you'd have to define which state the 
source object is in after moving. Since it's a destructive move, 
I'd expect the object to be at init, as if a destructor had been 
called. You could implement that with a tiny change to the 
current implementation. (memcpy from init instead of a statically 
allocated instance)


This should cover all the cases that you mentioned. It is highly 
performant (in fact this is very close to the behavior in C++, 
but faster because it uses init) and it won't affect structs that 
can be initialized at compile time. It will only be used for 
cases where you need to do things at runtime, and the users have 
the full ability to shoot themselves in the foot if they want to.


What is the case against a struct post-blit default constructor?

2012-10-08 Thread Malte Skarupke
So this has been brought up many times 
(http://www.digitalmars.com/d/archives/digitalmars/D/Struct_no-arg_constructor_173172.html 
http://www.digitalmars.com/d/archives/digitalmars/D/learn/Default_constructor_for_structs_20997.html 
http://www.digitalmars.com/d/archives/digitalmars/D/struct_and_default_constructor_150016.html)


However there was never a good answer.

I would like a struct default constructor. My expected behavior 
for it is that the struct gets initialized to .init, then my 
destructor gets called so that I can do additional things if I 
want.
This destructor always gets called when my struct is created 
without arguments.


So for example

struct S
{
this()
{
assert(bar == 10);
foo = [5].ptr;
}
int * foo;
int bar = 10;
}
S s;
assert(*s.foo == 5);

Possible cases against it:
- It would be slower than just initializing to .init. My 
proposed solution: Make the default constructor optional. If I 
have a struct that doesn't define a default constructor, it is 
just intialized to .init. So I can have the speed if I want to. 
Also always run it post-blit (blitted from .init). Meaning I only 
need to use it for things that can only be initialized at runtime.
- There already is a solution in @disable this(); static 
opCall() {...}. This is hacky and working against the language. 
It also makes it so that you can not allocate your struct on the 
heap.
- Structs should be simple. I haven't heard this argument, but 
I could imagine someone making it. I think structs should not be 
simple. They are much too useful for that. Also them having copy 
constructors and opAssign indicates that structs aren't expected 
to be simple.
- There would be a way around it by doing S s = S.init; I don't 
think that's a problem. If users want to shoot themselves in the 
foot, let em. The important part is that they have to go out of 
their way to do it. You could also handle that case in the copy 
constructor or assignment operator. (which is an improvement to 
the current behavior where you have to handle that case in every 
single member function, see for example std.typecons.RefCounted)



So I really can't think of a reason for why you wouldn't want 
this. Yet this discussion has happened several times already. 
There is clear demand for it and very good reasons, such as those 
mentioned in all the linked discussions.


So why is this being rejected?

Cheers,
Malte