On Monday, 27 May 2019 at 08:40:45 UTC, Amex wrote:
I have some types I've declared and I'd like to magically
extend them and add data. The problem is I inheriting from them
them is bad news.
Suppose, for example, I have an image type that is used in an
application. For a small part of the application it needs to
associate with each image type an xy coordinate type that adds
a coordinate to the image to be used by the part of the app
that needs to have coordinates for the image. The rest of the
app does not care or need the xy coordinates.
Inheritance solves this problem in some sense except it
doesn't. If I want to add new data then it breaks.
class image;
class xy : image; // can only add an image.
class z : ??; ?? could be xy, image, or something else
Inheritance is not the correct thing to do here. I only want to
associate data with an object. The hierarchy does not matter.
I could add a payload type to image but this causes problems
since it would have to be void or of a singular type. Mainly it
causes problems with the serializer I'm using since it can't
handle voids(I might be able to get it to work with a special
type but it will not be easy).
I could, of course, add a parallel struct that somehow
associates the image it's extra data but to keep them in sync
will not be easy since it will have to persist between runs(so
the address could not be used).
One could think of what I'm trying to do is create a tuple that
holds the image and any other data.
tuple(tuple(image, xy), z) // the outer tuple may occur later
without knowing that the image has been "wrapped".
How does one normally handle this? It seems like something
really simple to do(attach data) but it has no real
programmatic way to do naturally that works with oop.
The main thing is that attaching different data should "stack"
and not effect the base type or break the type hierarchy. If
something takes an image it should take any extended object.
I feel like the only way to do this is to create a special type
that can be serialized properly and is part of the base
class... I'll have to do it for all the classes. Hopefully
someone has some better ideas?
There's of course many different ways to skin a cat. Here's my
first idea:
class Image {
// regular image stuff
Metadata[] metadata;
T getMetadata(T : Metadata)() {
foreach (e; metadata) {
if (cast(T)e !is null) {
return cast(T)e;
}
}
return null;
}
}
abstract class Metadata {}
class Coordinates : Metadata {
int x, y;
this(int x, int y) {
this.x = x;
this.y = y;
}
}
unittest {
auto img = new Image();
img.metadata ~= new Coordinates(1,2);
auto coords = img.getMetadata!Coordinates;
assert(coords.x == 1);
assert(coords.y == 2);
}
This could easily be extended to support multiple metadata of the
same type by adding a name field or such.
--
Simen