digitalmars.D.learn - Static Arrays in Structs/Classes and Dynamic Array Sizes
- Dennis Croft (9/9) Jan 18 2016 I'm trying to organize a large amount of simple data into
- tsbockman (6/15) Jan 18 2016 Your description is too abstract for me. Can you give a simple
- Marc =?UTF-8?B?U2Now7x0eg==?= (66/66) Jan 18 2016 Here's what I suggest:
I'm trying to organize a large amount of simple data into manageable parcels of information that I can access efficiently. What I want to do is bake a static array into a class but be allowed to do the banking at runtime (because I want each array to be a different fixed length). Barring that, I'd like a way to fix the size of a dynamic array so that it's exactly the size I want and no larger behind the scenes. Anyone know enough about how these things work in D to help me out?
Jan 18 2016
On Monday, 18 January 2016 at 15:15:46 UTC, Dennis Croft wrote:I'm trying to organize a large amount of simple data into manageable parcels of information that I can access efficiently. What I want to do is bake a static array into a class but be allowed to do the banking at runtime (because I want each array to be a different fixed length). Barring that, I'd like a way to fix the size of a dynamic array so that it's exactly the size I want and no larger behind the scenes. Anyone know enough about how these things work in D to help me out?Your description is too abstract for me. Can you give a simple code example illustrating what you're trying to do? It doesn't need to actually compile; just write it how you want it to work and I'll see if I can translate it into valid D for you.
Jan 18 2016
Here's what I suggest: alias T = int; class VariableLengthClass { private: string someMember; size_t length_; T[0] data_; public: static make(Args...)(size_t length, Args args) { static assert( typeof(this).init.data_.offsetof == __traits(classInstanceSize, typeof(this)), ".data_ must be last member"); import core.memory : GC; import std.conv : emplace; const size = __traits(classInstanceSize, typeof(this)) + length * typeof(this).init.data_[0].sizeof; auto buffer = GC.malloc(size, 0, typeid(typeof(this))); auto result = buffer[0 .. size].emplace!(typeof(this))(args); result.length_ = length; return result; } property length() const { return length_; } trusted ref opIndex(size_t index) inout { assert(index < length, "index out of bounds"); return data_.ptr[index]; } size_t opDollar() const { return length_; } trusted opSlice() inout { return data_.ptr[0 .. length]; } trusted opSlice(size_t lower, size_t upper) inout { assert(lower >= 0, "negative indices not allowed"); assert(upper >= lower, "upper bound must be >= lower bound"); assert(upper <= length, "upper bound must not be larger than length"); return data_.ptr[lower .. upper]; } } void main() { import std.stdio; auto p = VariableLengthClass.make(20); //p[6 .. $] = 10; // https://issues.dlang.org/show_bug.cgi?id=15582 p[6] = 1; writeln(p[5 .. $]); } Explanation: We can't use the `new` operator, because the classes size is only known at runtime. Instead, we define a static method `make` that takes a length as its first argument, and then the remaining args for the constructor. It allocates memory from the GC and calls `emplace` to construct the object there. The actual data can be accessed using the operator implementations, to provide some encapsulation and memory safety. See the `main()` function for usage examples. Slice assignment (commented out) currently doesn't work due to a compiler bug. Depending on your requirements, other solutions are possible. You could, for example, make the class a template and the array length a template parameter, then have all instances derive from one common base class. Btw, does anyone know whether it's possible to make `emplace` call a private constructor? It would be better to make them private to prevent a user from accidentally using `new`...
Jan 18 2016