I want to ensure that instances of Thing
created by different instances of Thing::Factory
cannot be combine
d.
The following code works does just that, at runtime:
#include <cassert>
struct Thing {
struct Factory {
Thing make() {return Thing(this);}
};
static void combine(Thing t1, Thing t2) {
assert(t1.factory == t2.factory);
}
private:
Thing(Factory* factory_) : factory(factory_) {}
Factory* factory;
};
int main() {
Thing::Factory f1;
Thing t11 = f1.make();
Thing t12 = f1.make();
Thing::combine(t11, t12);
Thing::Factory f2;
Thing t21 = f2.make();
Thing t22 = f2.make();
Thing::combine(t21, t22);
Thing::combine(t11, t21); // Assertion failure
}
Question: is there a way to do that during compilation?
I've tried making Thing
a template:
template<typename Tag>
struct Thing {
// Same code as before
};
And modifying client code to:
struct Tag1;
struct Tag2;
int main() {
Thing<Tag1>::Factory f1;
Thing<Tag1> t11 = f1.make();
Thing<Tag1> t12 = f1.make();
Thing<Tag1>::combine(t11, t12);
Thing<Tag2>::Factory f2;
Thing<Tag2> t21 = f2.make();
Thing<Tag2> t22 = f2.make();
Thing<Tag2>::combine(t21, t22);
}
Then there is no way to combine
t11
and t21
. OK.
But there are still issues:
- nothing forbids to create another
Thing<Tag1>::Factory
and tocombine
theThing
s itmake
s witht11
- client has to manually declare tag types
Is there a pattern that could solve those as well?