Constructor protection: package ctors, UFCS, static methods?

aldanor via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Fri Dec 26 07:56:32 PST 2014


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!


More information about the Digitalmars-d-learn mailing list