Comments 8
Как-то я не уловил смысла в этой VariantFactory
. Для разных типов нужны разные наборы по разному подготавливаемых аргументов. Тот, кто их предоставляет, может сразу и создавать вариант соответствующего им типа. Единственное применение, которое я вижу для подобной фабрики, - это обобщённая (де)сериализация. Но ваше решение для этого не применимо.
Переписал ваш код на нормальном языке:
import std.stdio, std.format, std.sumtype, std.range;
struct Point {
int x, y;
int area() { return 0; }
string toString() {
return "[%s,%s]".format( this.x, this.y );
}
}
struct Rect {
Point pos, size;
int area() { return this.size.x * this.size.y; }
string toString() {
return "[rect:%s#%s]".format( this.pos, this.size );
}
}
struct Line {
Point from, to;
int area() { return 0; }
string toString() {
return "[line:%s->%s]".format( this.from, this.to );
}
}
alias Shape = SumType!( Point, Rect, Line );
void main() {
Shape[] shapes;
shapes ~= Shape( Point(1,2) );
shapes ~= Shape( Rect( Point(1,2), Point(1,2) ) );
shapes ~= Shape( Line( Point(1,2), Point(3,4) ) );
foreach( shape; shapes ) shape.writeln;
pragma(msg,Shape.Types);
}
(Point, Rect, Line)
[1,2]
[rect:[1,2]#[1,2]]
[line:[1,2]->[3,4]]
Считайте, что Shape - это и есть ваша фабрика.
Не совсем понятно, чем явное указания class type из enum лучше чем указание самого класса при вставка в вариант.
Возникает следующий вопрос: а как мы можем абстрагировать, отделить способ создания от самого объекта?
У меня возникает другой вопрос: зачем? В какой ситуации код
using VF = VariantFactory<Point, Line, Rect>;
using ShapeType = VF::ResultType;
ShapeType shape;
VF{}( &shape, kUniqueId<line>, 1, 1, 4, 5);
будет лучше, чем две строки ниже?
using ShapeType = std::variant<Point, Line, Rect>;
ShapeType shape{std::in_place_type_t<Line>, 1, 1, 4, 5};
Этот вопрос применим и к обычной фабрике - там тоже можно просто вызывать конструкторы.
Стандартные выгоды: вынос общего бойлерплейта (логгирование, кэширование, пул памяти) или возможность передать фабрику как внешний параметр для другого шаблона вроде аллокатора из STL.
Клиентский код не всегда может вызывать конструкторы -- например, мы можем хотеть иметь возможность подменять тип создаваемого объекта или клиентский код может не иметь значений параметров. В данном же случае фабрике из клиента передаётся и тип, и параметры. Точнее, ID типа, но он взаимно однозначно соответствует типу.
Для выноса бойлерплейта достаточно шаблонной функции в три строчки, а в случае передачи этой фабрики извне я опять же не понимаю зачем.
Следите за руками:
Shape shape( Type, Args ... )( Args args ) {
"create %s%s".format( Type.stringof, [args] ).writeln;
return Type( args ).Shape;
}
shapes ~= Point(1,2).Shape; // no log
shapes ~= shape!Point( 1, 2 ); // create Point[1, 2]
Неужели не хочется писать простой и лаконичный код?
Фабрика для std::variant: как объединить compile-time и run-time, чтобы получить выигрыш от обоих