www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - scoped classes

reply Alex <sascha.orlov gmail.com> writes:
Hi all,
a short question about an example.
having read this:
https://dlang.org/library/std/typecons/scoped.html

There is a struct B defined in the middle of the example, with a 
scoped class member.
How to define an array of such members (and to put some items to 
it)?
So, I want to have something like

// Use as member variable
struct B
{
     typeof(scoped!A())[] a; // note the trailing parentheses

     this(int i)
     {
         // construct member
         a.length = 5; // doesn't work, as the default constructor 
is disabled.
         a ~= scoped!A(i); // doesn't work, as the type is not 
copyable
         // ???
     }
}
Apr 26 2017
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Thursday, 27 April 2017 at 06:40:49 UTC, Alex wrote:
 Hi all,
 a short question about an example.
 having read this:
 https://dlang.org/library/std/typecons/scoped.html

 There is a struct B defined in the middle of the example, with 
 a scoped class member.
 How to define an array of such members (and to put some items 
 to it)?
 So, I want to have something like

 // Use as member variable
 struct B
 {
     typeof(scoped!A())[] a; // note the trailing parentheses

     this(int i)
     {
         // construct member
         a.length = 5; // doesn't work, as the default 
 constructor is disabled.
         a ~= scoped!A(i); // doesn't work, as the type is not 
 copyable
         // ???
     }
 }
The only "possible" way would be like this: typeof(scoped!A())[] a; a = [ scoped!A(1), scoped!A(2), scoped!A(3) ]; But even so, you shouldn't do that. scoped isn't designed for it.
Apr 27 2017
parent reply Alex <sascha.orlov gmail.com> writes:
On Thursday, 27 April 2017 at 15:06:44 UTC, Stanislav Blinov 
wrote:
 The only "possible" way would be like this:

 typeof(scoped!A())[] a;
 a = [ scoped!A(1), scoped!A(2), scoped!A(3) ];

 But even so, you shouldn't do that. scoped isn't designed for 
 it.
And then, the amount of elements is not known at compile time... But I think, I can generalize the question little bit: void main() { S[] arr; S s = S(42); arr = [s]; // this doesn't work :( } struct S { disable this(); disable this(this); this(size_t dummy){} } Given a struct with an explicit constructor and a postblit. How to make an array of it? Tried stuff with emplace, move and memmove already, nothing worked, at least in the form I had it... It is good, that it is hidden so well, as I'm intend to do this action in a constructor only, but I didn't find not a single solution so far...
Apr 27 2017
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Thursday, 27 April 2017 at 15:47:38 UTC, Alex wrote:

 struct S
 {
 	 disable this();
 	 disable this(this);
 	this(size_t dummy){}
 }

 Given a struct with an explicit constructor and a postblit. How 
 to make an array of it?
You mean with a disabled default ctor and postblit? You can't with built-in arrays. They expect that elements can be default-constructed and copied. Even std.container.Array in its current implementation won't help you there. The only way to get around that is to devise your own array type that carefully deals with uninitialized storage and uses emplace/move/moveEmplace to store elements.
Apr 27 2017
parent reply Alex <sascha.orlov gmail.com> writes:
On Thursday, 27 April 2017 at 16:39:38 UTC, Stanislav Blinov 
wrote:
 On Thursday, 27 April 2017 at 15:47:38 UTC, Alex wrote:

 struct S
 {
 	 disable this();
 	 disable this(this);
 	this(size_t dummy){}
 }

 Given a struct with an explicit constructor and a postblit. 
 How to make an array of it?
You mean with a disabled default ctor and postblit? You can't with built-in arrays. They expect that elements can be default-constructed and copied. Even std.container.Array in its current implementation won't help you there. The only way to get around that is to devise your own array type that carefully deals with uninitialized storage and uses emplace/move/moveEmplace to store elements.
Hm... ok. This is a acceptable answer. So, if such structs exist, it is forced to use pointers on their instances as an indirection... apart from using some reference type things surrounding them... Thanks for helping with perception :)
Apr 27 2017
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
I haven't used it yet but it's worth noting that there is EMSI's 
container library as well:

   http://code.dlang.org/packages/emsi_containers

Ali
Apr 27 2017
parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Thursday, 27 April 2017 at 17:07:05 UTC, Ali Çehreli wrote:
 I haven't used it yet but it's worth noting that there is 
 EMSI's container library as well:

   http://code.dlang.org/packages/emsi_containers
A brief glance at the source of dynamicarray there suggests that it won't help with non-copyables either.
Apr 27 2017
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 04/27/2017 05:47 PM, Alex wrote:
 void main()
 {
     S[] arr;
     S s = S(42);
     arr = [s]; // this doesn't work :(
 }

 struct S
 {
      disable this();
      disable this(this);
     this(size_t dummy){}
 }
1) Construct the S instance directly in the array literal: ---- arr = [S(42]); ---- But I guess that's besides the point. So ... 2) Use std.algorithm.mutation.move [1]: ---- import std.algorithm.mutation: move; S s = S(42); arr = [move(s)]; ---- `move` makes a "destructive copy" here; i.e, it resets `s` to `S.init`. You probably want to append to that array at some point. That's going to be more tricky, because appending potentially involves copying the whole array. This might work: ---- import std.algorithm.mutation: moveEmplace; import std.array: uninitializedArray; /* Make a new, larger array: */ S[] new_arr = uninitializedArray!(S[])(arr.length + 1); /* Copy existing elements; destroy old array: */ foreach (size_t i; 0 .. arr.length) moveEmplace(arr[i], new_arr[i]); /* Copy new element; destroy s: */ moveEmplace(s, new_arr[$ - 1]); /* Replace old array with new one: */ arr = new_arr; ---- Notes: * `moveEmplace` and `uninitializedArray` are completely unsafe. You must ensure safety yourself. * I'm not entirely sure that the code is completely correct. It might be invalid and break some language rule. * I'm pretty sure that the code is going to be invalid when you're dealing with const/immutable data. * The code destroys the old array. References to it (slices of it, pointers into it) will show .init values. * Maybe one can make use of `.capacity` somehow. As it is, the code makes a copy of the whole array every time. * If this "moveAppend" functionality can be done in a valid manner, it might be a worthwhile addition to the "move" function family.
Apr 27 2017
parent reply Alex <sascha.orlov gmail.com> writes:
On Thursday, 27 April 2017 at 17:39:42 UTC, ag0aep6g wrote:
 On 04/27/2017 05:47 PM, Alex wrote:
 void main()
 {
     S[] arr;
     S s = S(42);
     arr = [s]; // this doesn't work :(
 }

 struct S
 {
      disable this();
      disable this(this);
     this(size_t dummy){}
 }
1) Construct the S instance directly in the array literal: ---- arr = [S(42]); ---- But I guess that's besides the point. So ... 2) Use std.algorithm.mutation.move [1]: ---- import std.algorithm.mutation: move; S s = S(42); arr = [move(s)]; ---- `move` makes a "destructive copy" here; i.e, it resets `s` to `S.init`. You probably want to append to that array at some point. That's going to be more tricky, because appending potentially involves copying the whole array. This might work: ---- import std.algorithm.mutation: moveEmplace; import std.array: uninitializedArray; /* Make a new, larger array: */ S[] new_arr = uninitializedArray!(S[])(arr.length + 1); /* Copy existing elements; destroy old array: */ foreach (size_t i; 0 .. arr.length) moveEmplace(arr[i], new_arr[i]); /* Copy new element; destroy s: */ moveEmplace(s, new_arr[$ - 1]); /* Replace old array with new one: */ arr = new_arr; ----
Cool! Works like a charm :)
 Notes:

 * `moveEmplace` and `uninitializedArray` are completely unsafe. 
 You must ensure safety yourself.
Yes, I'm aware of this...
 * I'm not entirely sure that the code is completely correct. It 
 might be invalid and break some language rule.
:-) I'm not sure about what is worse: to be forced to use pointers or to use black magic practices at preparation step only. So... Now, I have at least a choice...
 * I'm pretty sure that the code is going to be invalid when 
 you're dealing with const/immutable data.
Ok... this is important... I tried this out, and the value of the immutable data even remains the same. But for safety reasons, I would reconstruct the structs inside the array anyway, so I can live with this, I think...
 * The code destroys the old array. References to it (slices of 
 it, pointers into it) will show .init values.
That's ok... at creation time, there won't be any references yet...
 * Maybe one can make use of `.capacity` somehow. As it is, the 
 code makes a copy of the whole array every time.
Tried this out for some values, capacity of the new array is always 0.
 * If this "moveAppend" functionality can be done in a valid 
 manner, it might be a worthwhile addition to the "move" 
 function family.
:-) Thanks a lot!

Apr 27 2017
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Thursday, 27 April 2017 at 18:36:08 UTC, Alex wrote:

 * I'm pretty sure that the code is going to be invalid when 
 you're dealing with const/immutable data.
Ok... this is important... I tried this out, and the value of the immutable data even remains the same. But for safety reasons, I would reconstruct the structs inside the array anyway, so I can live with this, I think...
move() is only destructive if the type has custom postblit or destructor, otherwise it's just a bitwise copy. But, as mentioned, there's an issue with it at the moment: it doesn't really care if the type being moved has const/immutable members. uninitializedArray is scary. If you do use it, probably best to localize the usage as much as possible and be very very VERY careful with it ;) Here's a sketch of something that's a little bit more convenient: https://dpaste.dzfl.pl/ee472fd872a5 But I'm not going to pretend it'd survive a review. Also note that, going back to your original question, docs for scoped state that it's illegal to move it, so that particular type you can't really store in any of these arrays.
Apr 27 2017
parent Alex <sascha.orlov gmail.com> writes:
On Thursday, 27 April 2017 at 19:18:14 UTC, Stanislav Blinov 
wrote:
 On Thursday, 27 April 2017 at 18:36:08 UTC, Alex wrote:

 move() is only destructive if the type has custom postblit or 
 destructor, otherwise it's just a bitwise copy. But, as 
 mentioned, there's an issue with it at the moment: it doesn't 
 really care if the type being moved has const/immutable members.

 uninitializedArray is scary. If you do use it, probably best to 
 localize the usage as much as possible and be very very VERY 
 careful with it ;)
:-)
 Here's a sketch of something that's a little bit more 
 convenient: https://dpaste.dzfl.pl/ee472fd872a5
 But I'm not going to pretend it'd survive a review.
Wow! So much work already done... Thanks for so much ideas :)
 Also note that, going back to your original question, docs for 
 scoped state that it's illegal to move it, so that particular 
 type you can't really store in any of these arrays.
yeah... At the beginning of the day, I thought, this would act as a wrapper, which could simplify things... just a wrong direction.
Apr 27 2017