In order to create a generic factory a need to manage generic creation params was raised.
This is the first step:
struct Foo {
int num_;
class FooKey {};
public:
Foo(int num, FooKey) : num_(num) {}
static std::unique_ptr<Foo> create(int i) {
return std::make_unique<Foo>(i, FooKey());
}
int num() const { return num_; }
};
template<class Obj>
class Factory {
public:
template<typename... ObjParams>
auto createObj(ObjParams&&... objectCreationParams) {
// the use of C++17 std::invoke / std::apply is for conveniency
// it can be done in a more cumbersome way with C++11/C++14
return std::invoke(&Obj::create, objectCreationParams...);
}
};
int main() {
Factory<Foo> f;
auto fObj = f.createObj(7);
std::cout << fObj->num() << std::endl;
}
But we want to allow the factory to 'remember' the creation params.
So we add variadic params and go for a creation function for the factory:
template<class Obj, class... ObjParams>
class Factory {
std::tuple<ObjParams...> _objectCreationParams;
public:
Factory(ObjParams&& ... params) {
_objectCreationParams =
std::make_tuple<ObjParams...>(std::forward<ObjParams>(params)...);
}
auto createObj() {
return std::apply(&Obj::create, _objectCreationParams);
}
};
template<class Obj, class... ObjParams>
Factory<Obj, ObjParams...> createFactory(ObjParams&&... objParams) {
return Factory<Obj, ObjParams...>{std::forward<ObjParams>(objParams)...};
}
int main() {
auto factory = createFactory<Foo>(7);
auto fObj = factory.createObj();
std::cout << fObj->num() << std::endl;
}
We want to replace now the creation function for the factory with C++17 class template argument deduction, but this fails to compile (g++ 7.2.0). I guess I'm missing something with C++17 class template argument deduction rules.
Any suggestion?
template<class Obj, class... ObjParams>
class Factory {
// ...
public:
template<class... ObjParams_>
Factory(ObjParams_&& ... params) {
_objectCreationParams =
std::make_tuple<ObjParams_...>(std::forward<ObjParams_>(params)...);
}
// ...
};
// user-defined deduction guide
template<class Obj, class... ObjParams>
Factory(ObjParams&& ... params) -> Factory<Obj, ObjParams...>;
int main() {
Factory<Foo> f(7); // compilation error - candidate expects 0 arguments, 1 provided
// shouldn't it auto deduce <class... ObjParams> for Factory?
auto fObj = f.createObj();
std::cout << fObj->num() << std::endl;
}
Edit:
Given that there is no partial class template argument deduction suggesting the following:
template<class Obj>
struct Factory {
template<class... ObjParams>
class WithParams {
std::tuple<ObjParams...> _objectCreationParams;
public:
WithParams(ObjParams&& ... params) {
_objectCreationParams =
std::make_tuple<ObjParams...>(std::forward<ObjParams>(params)...);
}
auto createObj() {
return std::apply(&Obj::create, _objectCreationParams);
}
};
template<class... ObjParams>
auto createObj(ObjParams&& ... params) {
return std::invoke(&Obj::create, std::forward<ObjParams>(params)...);
}
};
int main() {
Factory<Foo>::WithParams f(7);
auto fObj = f.createObj();
std::cout << fObj->num() << std::endl;
Factory<Foo> f2;
auto fObj2 = f2.createObj(8);
std::cout << fObj2->num() << std::endl;
}