Static Arrays in Structs/Classes and Dynamic Array Sizes

Marc Schütz via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Jan 18 11:41:33 PST 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`...


More information about the Digitalmars-d-learn mailing list