www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Static Arrays in Structs/Classes and Dynamic Array Sizes

reply Dennis Croft <sdfdsds sdfds.com> writes:
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
next sibling parent tsbockman <thomas.bockman gmail.com> writes:
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
prev sibling parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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