www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Constructor protection: package ctors, UFCS, static methods?

reply "aldanor" <i.s.smirnov gmail.com> writes:
Hi, I've been wondering if anyone would give some advice on an 
OOP-related question. Assume there's an external library (module 
c_library) that handles IDs for groups and datasets and we want 
to wrap it in a high-level D API. Groups can contain datasets 
identified by names, and each dataset has a parent group; there 
are external functions get_dataset/get_group that provide the 
corresponding ids.

The Group class needs to have a "dataset(name)" method that 
returns a dataset by name and the Dataset needs to have a 
"group()" method that returns the parent group. However 
constructors of Group and Dataset that take an id are really 
meant to be completely internal (i.e. private or protected) which 
leads to the problem: how would those methods be able to access 
those constructors?

--------------------------

Solution 1: set protection level for constructors of 
Group/Dataset to "package" so they are callable from anywhere in 
the package. This feels a bit wrong though as they are really 
meant to be protected; this is also an exploit of the D-specific 
"package" qualifier so e.g. one wouldn't be able to do something 
like this in C++.

/* id.d */

class ID {
     protected int m_id;
     protected this(int id) {
         m_id = id;
     }
     int id()  property const {
         return m_id;
     }
}

/* group.d */

import c_library : get_dataset;
import id : ID;
import dataset : Dataset;

class Group : ID {
     package this(int id) { super(id); } // <-- package
     public this(...) { // high-level public ctor }
     Dataset getDataset(string name) const {
         int dataset_id = get_dataset(this.id, name);
         return Dataset(dataset_id);
}

/* dataset.d */

import c_library : get_group;
import id : ID;
import group : Group;

class Dataset : ID {
     package this(int id) { super(id); } // <-- package
     public this(...) { // high-level public ctor }
     Group group() const {
         int group_id = get_group(this.id);
         return Group(group_id);
     }
}

--------------------------

Solution 2: use UFCS and swap the group() / dataset(name) 
functions between the two modules. This way, e.g. group() will 
have access to protected Group.this(id) due to being in the same 
module. However, now there's a different problem: if you "import 
group", you won't be able to do a "group.dataset(name)" due to it 
being in a different module, so a public import is required to 
fix that -- which also feels a bit ugly (what if there are 15 
different modules and not 2, would they all have to 
cross-public-import each other?). This is also a D-specific 
exploit so it again wouldn't be possible in C++.

/* group.d */

import c_library : get_group;
import id : ID;

public import dataset; // <-- public import due to UFCS

class Group : ID {
     protected this(int id) { super(id); } // <-- protected
}

Group group(in Dataset dataset) {
     return Group(get_group(dataset.id));
}

/* dataset.d */

import c_library : get_dataset;
import id : ID;

public import group; // <-- public import due to UFCS

class Dataset : ID {
     protected this(int id) { super(id); } // <-- protected
}

Dataset dataset(in Group group, string name) {
     return new Dataset(get_dataset(group.id, name));
}

--------------------------

Solution 3: add static methods like Dataset::fromGroup(name) and 
Group::fromDataset() and then do something like this:

/* group.d */

class Group : ID {
     ...
     static typeof(this) fromDataset(in Dataset dataset) {
         return new typeof(this)(get_group(dataset.id));
     }
}


/* dataset.d */

class Dataset {
     ...
     Group group()  property const {
         return Group.fromDataset(this);
     }
}

However, this essentially leads to duplications of every such 
lookup methods, so if there are many such entities, there will be 
a whole bunch of such static methods..

--------------------------

Is this a completely normal situation or is the design flawed and 
there's a clean way around it?

Thanks!
Dec 26 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
Please ignore the missing "new" keywords in the code and other 
minor typos :)
Dec 26 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
On Friday, 26 December 2014 at 15:58:18 UTC, aldanor wrote:
 Please ignore the missing "new" keywords in the code and other 
 minor typos :)
Any opinions please?.. Would be much appreciated. Thanks!
Dec 27 2014
parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sat, 27 Dec 2014 23:34:06 +0000
aldanor via Digitalmars-d-learn <digitalmars-d-learn puremagic.com>
wrote:

 On Friday, 26 December 2014 at 15:58:18 UTC, aldanor wrote:
 Please ignore the missing "new" keywords in the code and other=20
 minor typos :)
=20 Any opinions please?.. Would be much appreciated.
i can't understand a word from it. sorry. ;-) no, really, i'm stupid and the text is big. try to write it with some other words, make it shorter. it's mostly a non-technical question as i can see, so clarity is important there.
Dec 27 2014