Skip to content

Instantly share code, notes, and snippets.

@JakobOvrum
Last active March 17, 2016 18:15
Show Gist options
  • Save JakobOvrum/49e6af2e9034a50c2a91 to your computer and use it in GitHub Desktop.
Save JakobOvrum/49e6af2e9034a50c2a91 to your computer and use it in GitHub Desktop.
Factory implementation
import std.traits : FunctionAttribute;
private string faToString(FunctionAttribute fa)
{
import std.algorithm, std.traits;
string result;
foreach (member; __traits(derivedMembers, FunctionAttribute))
{
if (fa & __traits(getMember, FunctionAttribute, member))
result ~= (member.endsWith("_")? member.stripRight('_') : '@' ~ member) ~ " ";
}
return result;
}
// TODO: implicit conversion != inheritance
// TODO: emplace for classes that can propagate attributes
// TODO: Array.insertBack is not @nogc? Exceptions?
// TODO: binary search
// TODO: ignore subsequent registration attempts
// TODO: support shared instances
// TODO: parameterized construction
struct Factory(Base, Allocator, FunctionAttribute constructionAttributes = FunctionAttribute.none)
if (is(Base == class) || is(Base == interface))
{
import std.container.array : Array;
import std.meta;
import std.typecons : Tuple;
import std.traits;
private:
mixin("alias Constructor = Base function(void[]) " ~ faToString(constructionAttributes) ~ ";");
alias Producer = Tuple!(string, "fqn", size_t, "size", Constructor, "ctor");
Array!Producer producers;
public:
static if (is(typeof(Allocator.instance)))
private alias alloc = Allocator.instance;
else
{
private Allocator alloc;
@disable this();
this(Allocator alloc)
{
this.alloc = alloc;
}
}
void register(Ts...)()
if (allSatisfy!(applyRight!(isImplicitlyConvertible, Base), Ts))
{
import std.conv, std.meta, std.traits;
auto producer(T) = Producer(
fullyQualifiedName!T,
__traits(classInstanceSize, T),
buffer => emplace!T(buffer));
alias Deduplicated = NoDuplicates!Ts;
Producer[Deduplicated.length] newEntries = [staticMap!(producer, Deduplicated)];
producers.insertBack(newEntries[]);
}
Base make()(in char[] fqn)
{
import std.algorithm.searching : find;
auto search = producers[].find!((producer, fqn) => producer.fqn == fqn)(fqn);
if (search.empty) return null;
return search.front.ctor(alloc.allocate(search.front.size));
}
void dispose(T : Base)(ref T o)
{
import std.experimental.allocator : dispose;
alloc.dispose(o);
o = null;
}
}
unittest
{
import std.experimental.allocator.mallocator;
import std.traits : fullyQualifiedName;
interface Identifiable
{
string id();
}
Factory!(Identifiable, Mallocator) myFactory;
static class A : Identifiable
{
string id() { return "foo"; }
}
static class B : Identifiable
{
string id_;
this() { this.id_ = "bar"; }
string id() { return id_; }
}
myFactory.register!(A, B);
auto a = myFactory.make(fullyQualifiedName!A);
assert(a);
assert(a.id == "foo");
myFactory.dispose(a);
assert(a is null);
auto b = myFactory.make(fullyQualifiedName!B);
assert(b);
assert(b.id == "bar");
myFactory.dispose(b);
assert(b is null);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment