Template-style polymorphism in table structure

ZombineDev via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Sep 4 07:06:25 PDT 2016


On Sunday, 4 September 2016 at 09:55:53 UTC, data pulverizer 
wrote:
> I am trying to build a data table object with unrestricted 
> column types. The approach I am taking is to build a generic 
> interface BaseVector class and then a subtype GenericVector(T) 
> which inherits from the BaseVector. I then to build a Table 
> class which contains columns that is a BaseVector array to 
> represent the columns in the table.
>
> My main question is how to return GenericVector!(T) from the 
> getCol() method in the Table class instead of BaseVector.
>
> Perhaps my Table implementation somehow needs to be linked to 
> GenericVector(T) or maybe I have written BaseTable instead and 
> I need to do something like a GenericTable(T...). However, my 
> previous approach created a tuple type data object but once 
> created, the type structure (column type configuration) could 
> not be changed so no addition/removal of columns.
>
>
> ------------------------------------------------
> import std.stdio : writeln, write, writefln;
> import std.format : format;
>
> interface BaseVector{
>     BaseVector get(size_t);
> }
>
> class GenericVector(T) : BaseVector{
>     T[] data;
>     alias data this;
>     GenericVector get(size_t i){
>         return new GenericVector!(T)(data[i]);
>     }
>     this(T[] arr){
>         this.data = arr;
>     }
>     this(T elem){
>         this.data ~= elem;
>     }
>     void append(T[] arr){
>         this.data ~= arr;
>     }
>
>     override string toString() const {
>         return format("%s", data);
>     }
> }
>
> class Table{
> private:
>     BaseVector[] data;
> public:
>     // How to return GenericVector!(T) here instead of 
> BaseVector
>     BaseVector getCol(size_t i){
>         return data[i];
>     }
>     this(BaseVector[] x ...){
>         foreach(col; x)
>             this.data ~= col;
>     }
>     this(BaseVector[] x){
>         this.data ~= x;
>     }
>     this(Table x, BaseVector[] y ...){
>         this.data = x.data;
>         foreach(col; y){
>             this.data ~= col;
>         }
>     }
>     void append(BaseVector[] x ...){
>         foreach(col; x)
>             this.data ~= x;
>     }
> }
>
>
> void main(){
>     auto index = new GenericVector!(int)([1, 2, 3, 4, 5]);
>     auto numbers = new GenericVector!(double)([1.1, 2.2, 3.3, 
> 4.4, 5.5]);
>     auto names = new GenericVector!(string)(["one", "two", 
> "three", "four", "five"]);
>     Table df = new Table(index, numbers, names);
>     // I'd like this to be GenericVector!(T)
>     writeln(typeid(df.getCol(0)));
> }

Since BaseVector is a polymorphic type you can't know in advance 
(at compile-time) the type of the object at a particular index. 
The only way to get a typed result is to specify the type that 
you expect, by providing a type parameter to the function:

The cast operator will perform a dynamic cast at runtime which 
will return an object of the requested type, or null, if object 
is of some other type.

GenericVector!ExpectedType getTypedCol(ExpectedType)(size_t i){

     assert (cast(GenericVector!ExpectedType)data[i],
         format("The vector at col %s is not of type %s, but %s", 
i,
          ExpectedType.stringof, typeof(data[i])));

     return cast(GenericVector!ExpectedType)data[i];
}

void main(){
      auto index = new GenericVector!(int)([1, 2, 3, 4, 5]);
      auto numbers = new GenericVector!(double)([1.1, 2.2, 3.3, 
4.4, 5.5]);
      auto names = new GenericVector!(string)(["one", "two", 
"three", "four", "five"]);
      Table df = new Table(index, numbers, names);

      if (typeid(df.getCol(0) == typeid(string))
          writeln(df.getTypedCol!string(0).data);

      else if (typeid(df.getCol(0) == typeid(int))
          writeln(df.getTypedCol!int(0).data);

      // and so on...
  }

Another way to approach the problem is to keep your data in an 
Algebraic.
(https://dpaste.dzfl.pl/7a4e9bf408d1):

import std.meta : AliasSeq;
import std.variant : Algebraic, visit;
import std.stdio : writefln;

alias AllowedTypes = AliasSeq!(int[], double[], string[]);
alias Vector = Algebraic!AllowedTypes;
alias Table = Vector[];

void main()
{
     Vector indexes = [1, 2, 3, 4, 5];
     Vector numbers = [1.1, 2.2, 3.3, 4.4, 5.5];
     Vector names = ["one", "two", "three", "four", "five"];

     Table table = [indexes, numbers, names];

     foreach (idx, col; table)
         col.visit!(
             (int[] indexColumn) =>
                  writefln("An index column at %s. Contents: %s", 
idx, indexColumn),
             (double[] numberColumn) =>
                  writefln("A number column at %s. Contents: %s", 
idx, numberColumn),
             (string[] namesColumn) =>
                  writefln("A string column at %s. Contents: %s", 
idx, namesColumn)
         );
}

Application output:
An index column at 0. Contents: [1, 2, 3, 4, 5]
A number column at 1. Contents: [1.1, 2.2, 3.3, 4.4, 5.5]
A string column at 2. Contents: ["one", "two", "three", "four", 
"five"]


More information about the Digitalmars-d-learn mailing list