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