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`...

Reply via email to