I often write factory which have a signature similar to the following:
std::unique_ptr<AbstractType> createUnique(IDType id);
std::shared_ptr<AbstractType> createShared(IDType id);
Where the former would do something like:
switch (id)
{
case kID1:
return std::make_unique<Type1>();
}
And the latter
switch (id)
{
case kID1:
return std::make_shared<Type1>();
}
Well, the only different between the two functions is the "make" function used (make_shared or make_unique) and the return type (shared_ptr or unique_ptr). This leads to code duplication.
I am wondering how I could write a templated create function which accepts the pointer type and "make" function type and uses those instead. Something like:
template <typename PTR, typename MAKE>
PTR create(IDType id)
{
switch (id)
{
case kID1:
return MAKE<Type1>();
}
}
I am aware that the above is not valid code.
Also, I am aware that a std::shared_ptr could be created from an std::unique_ptr and visa versa, but I this factory would be used in different applications where one might use the shared_ptr and the other might use the unique_ptr. This way, the code is re-usable but also effecienct for the particular use-case.
My advice, only have a single return type of std::unique_ptr<AbstractType>. If you do that then you can use the function with std::unique_ptr and std::shared_ptr like
auto my_ptr = createInterface(id);
which has my_ptr being a unique_ptr or
std::shared_ptr<AbstractType> my_ptr = createInterface(id);
and now the returned unique_ptr is converted into a shared_ptr.
To avoid loosing use of std::make_shared which optimizes use of memory, I would approach this issue this way:
class AbstractType {
public:
virtual ~AbstractType() = default;
virtual int f() const = 0;
};
class Foo : public AbstractType {
public:
int f() const override
{
return 1;
}
};
class Bar : public AbstractType {
public:
int f() const override
{
return 2;
}
};
enum class IDType {
Foo,
Bar,
};
class Factory {
public:
std::unique_ptr<AbstractType> createUnique(IDType id);
std::shared_ptr<AbstractType> createShared(IDType id);
private:
template <typename MakePtr>
auto createUniversal(IDType id);
};
//======================
class WrapMakeUnique {
public:
template <typename Base, typename T, typename... Args>
static auto make(Args... args) -> std::unique_ptr<Base>
{
return std::make_unique<T>(std::forward<Args>(args)...);
}
};
class WrapMakeShared {
public:
template <typename Base, typename T, typename... Args>
static auto make(Args... args) -> std::shared_ptr<Base>
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
};
template <typename MakePtr>
auto Factory::createUniversal(IDType id)
{
switch (id) {
case IDType::Foo:
return MakePtr::template make<AbstractType, Foo>();
case IDType::Bar:
return MakePtr::template make<AbstractType, Bar>();
}
throw std::invalid_argument { "IDType out of range" };
}
std::unique_ptr<AbstractType> Factory::createUnique(IDType id)
{
return createUniversal<WrapMakeUnique>(id);
}
std::shared_ptr<AbstractType> Factory::createShared(IDType id)
{
return createUniversal<WrapMakeShared>(id);
}
https://godbolt.org/z/9f1ddj9zo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With