Can a D library have some types determined by the client program?
cc
cc at nevernet.com
Fri Mar 8 16:54:48 UTC 2024
On Friday, 8 March 2024 at 06:03:51 UTC, Liam McGillivray wrote:
> A problem I have is that the 3 classes Map, Tile, and Unit
> reference each-other. If I turn Map into a template, than it
> complains about the member variable of Unit declared as `Map
> map;` without arguments. I change this line to `Map!TileType
> map;` but this requires that Unit is also turned into a
> template.
If you don't want Unit to be a template, you can just have Map
derive from a basic interface or abstract class. You can also
have every relevant class share similar templates, you just need
to remember to supply the template arguments everywhere. You'll
need to think about how much interoperation you want between
these classes. Does Unit really need to know what TileType map
is using, or can it just trust that when it asks Map to move, Map
will handle everything related to tile types? Generally it's
best practice to have as much of the inner workings isolated as
possible and just provide methods to access functionality. To
ease some of the template argument!spaghetti, you could insert
aliases into the classes via fully qualified symbol names.
Another alternative, if everything is defined on one file, you
could wrap everything in a single template, but I don't usually
favor this strategy.
```d
//version=Interfaced;
version=AllTemplated;
//version=AllTemplatedAliases;
//version=OneTemplate;
version(Interfaced) {
interface IMap {
Unit addNewUnit();
}
class Map(TileType) : IMap {
Unit[] units;
Unit addNewUnit() {
auto unit = new Unit(this);
units ~= unit;
return unit;
}
}
class Unit {
IMap map;
private this(IMap map) {
this.map = map;
}
}
void main() {
auto map = new Map!uint;
auto unit = map.addNewUnit;
}
} else version(AllTemplated) {
class Map(TileType) {
Unit!TileType[] units;
auto addNewUnit() {
auto unit = new Unit!TileType(this);
units ~= unit;
return unit;
}
}
class Unit(TileType) {
Map!TileType map;
private this(Map!TileType map) {
this.map = map;
}
}
void main() {
auto map = new Map!uint;
auto unit = map.addNewUnit;
}
} else version(AllTemplatedAliases) {
class Map(TileType) {
alias Unit = mymodule.Unit!TileType;
Unit[] units;
auto addNewUnit() {
auto unit = new Unit(this);
units ~= unit;
return unit;
}
}
class Unit(TileType) {
alias Map = mymodule.Map!TileType;
Map map;
private this(Map map) {
this.map = map;
}
}
void main() {
auto map = new Map!uint;
auto unit = map.addNewUnit;
}
} else version(OneTemplate) {
template Map(TileType) {
class Map {
Unit[] units;
auto addNewUnit() {
auto unit = new Unit(this);
units ~= unit;
return unit;
}
}
class Unit {
Map map;
private this(Map map) {
this.map = map;
}
}
}
void main() {
auto map = new Map!uint;
auto unit = map.addNewUnit;
}
}
```
If a given class doesn't really need to know what the template
parameters are to the other class it's interacting with, I would
avoid defining too many template types everywhere and just use
interfaces or abstract parent classes.
> After changing `class Unit` to `class Unit (TileType), it
> complains about the line `Unit* occupant;` in Tile.
Are you sure you need a pointer here? Class objects in D are
already reference-type by default.
More information about the Digitalmars-d-learn
mailing list