Passing large or complex data structures to threads

Ali Çehreli acehreli at yahoo.com
Mon May 27 23:56:12 PDT 2013


On 05/27/2013 06:55 PM, Joseph Rushton Wakeling wrote:

 > On 05/27/2013 11:33 PM, Simen Kjaeraas wrote:

 >> Short answer: If you will have mixed arrays, no. There's no way to make
 >> that safe. If you don't have mixed arrays, there are ways.
 >
 > So you mean there's no way to have one member variable be immutable, 
the rest
 > mutable, without hardcoding that into the class/struct design?

That is a difficult situation to manage though: What operations are 
valid under the 8 total mutable combinations of 3 members? The compiler 
must know what operations to be applied on what type of data so that it 
can both check the code and compile it accordingly.

void foo(MyDataStore my)
{
     my.someData[0] = 1.5;    // valid if someData is mutable
     my.someMoreData[0] = 42; // valid if someMoreData is mutable
}

Clearly, the code above can be compiled only if the two members of 
MyDataStore are mutable. The compiler does not have anything other than 
the static definition of MyDataStore, which implies that the type 
qualifiers of the members must be known at compile time.

To have such a flexibility, the type must be templatized. When it is 
templatized though, different instantiations of the template are 
different types, which means that they cannot take part in a collection 
of a specific type, unless they implement a certain interface like 
DataStore below:

import std.typecons;

interface DataStore
{
     void doWork();
}

class MyDataStoreImpl(SomeDataT,
                       SomeMoreDataT,
                       ImportedDataT) : DataStore
{
     SomeDataT[] someData;
     SomeMoreDataT[] someMoreData;
     ImportedDataT[][] importedData;

     this(SomeDataT[] sd, SomeMoreDataT[] smd, ImportedDataT[][] id)
     {
         someData = sd;
         someMoreData = smd;
         importedData = id;
     }

     void doWork()
     {
         // What can we do here?
         // There are 8 combinations of type qualifiers.
     }
}

auto makeMyDataStore(SomeDataT,
                      SomeMoreDataT,
                      ImportedDataT)(SomeDataT[] sd,
                                     SomeMoreDataT[] smd,
                                     ImportedDataT[][] id)
{
     return new MyDataStoreImpl!(SomeDataT,
                                 SomeMoreDataT,
                                 ImportedDataT)(sd, smd, id);
}

void foo(DataStore[] store)
{
     foreach (data; store) {
         data.doWork();
     }
}

void main()
{
     DataStore[] store;

     // The combination: mutable, const, immutable
     store ~= makeMyDataStore(new float[1],
                              new const(double)[3],
                              [ [ immutable(Tuple!(size_t, size_t))() ] ]);

     // Another combination: immutable, mutable, const
     store ~= makeMyDataStore(new immutable(float)[2],
                              new double[4],
                              [ [ const(Tuple!(size_t, size_t))() ] ]);

     foo(store);
}

There is the big question of how to implement MyDataStoreImpl.doWork(). 
How do we support every valid combination?

Conditional compilation is one way:

class MyDataStoreImpl(SomeDataT,
                       SomeMoreDataT,
                       ImportedDataT) : DataStore
{
// ...

     void doWork()
     {
         static if (is (SomeMoreDataT == const) &&
                    is (ImportedDataT == immutable)) {
             // ...
             writeln("case 1");

         } else static if(is (SomeDataT == immutable) &&
                          is (ImportedDataT == const)) {
             // ...
             writeln("case 2");

         } else {
            // ...
         }
     }
}

Perhaps mixins can be used to inject different functionality depending 
on different type qualifiers if the functionality is as simple as in the 
following case:

import std.stdio;

// This template contains code for a mutable slice and the function that 
goes
// with it:
template Foo(T)
     if (!is (T == immutable) &&
         !is (T == const))
{
     T data[];

     void doWork()
     {
         writeln("called for mutable data");

         if (data.length < 1) {
             data.length = 1;
         }

         data[0] = T.init;
     }
}

// This one is for immutable and const data:
template Foo(T)
     if (is (T == immutable) ||
         is (T == const))
{
     T[] data;

     void doWork()
     {
         // We cannot modify data[0] here...
         writeln("called for immutable data");
     }
}

interface DataStore
{
     void doWork();
}

class MyDataStoreImpl(T) : DataStore
{
     mixin Foo!T;
}

void main()
{
     DataStore[] store;

     store ~= new MyDataStoreImpl!(double)();
     store ~= new MyDataStoreImpl!(immutable(double))();

     foreach (data; store) {
         data.doWork();
     }
}

Ali



More information about the Digitalmars-d-learn mailing list