Reputation: 1094
I have come across code like
bool open_resource(..., shared_ptr<resource> & res)
{
...
shared_ptr<resource> newResource(new resource(...));
res = move(newResource);
return true;
}
which is then called with
shared_ptr<resource> res;
open_resource(..., res);
and then, as far as I saw, res is NOT used in ways that require sharing the pointer.
Of course I immediately thought of changing
shared_ptr<resource> newResource(new resource(...));
res = move(newResource);
with
res = make_shared<resource>(...)
...but then I hit a roadblock. Now I can no longer advise to change the shared_ptr reference to something more basic; at least not if I want to ensure that, if the caller actually needs a shared_ptr later, the control block efficiently resides on the same allocation as the object. For this to work, it must be a shared_ptr from the beginning.
On the other side, shared_ptr is a "heavy" type; it has two counters and aliasing and all kinds of features that really seem unneeded in most calling sites. And yet if it is shared_ptr in the signature, that they have to use.
The best solution I see is to move the body of the function to a helper function, and then overload.
bool get_resource_parameters(Param1& param1,..., ParamN& paramN)
{
...
}
bool open_resource(..., shared_ptr<resource> & res)
{
Param1 param1;
...
ParamN paramN;
if(!get_resource_parameters(param1,...,paramN))
return false;
res = make_shared<resource>(param1,...,paramN);
return true;
}
bool open_resource(..., unique_ptr<resource> & res)
{
Param1 param1;
...
ParamN paramN;
if(!get_resource_parameters(param1,...,paramN))
return false;
res = unique_ptr<resource>(new resource(param1,...,paramN));
return true;
}
But it's really not satisfying.
Does anyone see a better, more C++ solution?
Edit
Yes, the C++ way would be to return the pointer rather than a bool (and check for null). I cannot overload for shared_ptr in this case, but I can then assign the unique_ptr temporary returned to a shared_ptr varaible, and the appropriate constructor will convert it.
However, this way I lose the single allocation of make_shared. can I save it?
Upvotes: 3
Views: 2289
Reputation: 218268
To allow unique_ptr
/shared_ptr
, you may use template:
// Dispatcher for make_unique/make_shared
template <template <typename...> class Ptr, typename T>
struct make_helper;
template <typename T>
struct make_helper<std::unique_ptr, T>
{
template <typename ...Ts>
std::unique_ptr<T> operator() (Ts&&... args) const {
return std::make_unique<T>(std::forward<Ts>(args)...);
}
};
template <typename T>
struct make_helper<std::shared_ptr, T>
{
template <typename ...Ts>
std::shared_ptr<T> operator() (Ts&&... args) const {
return std::make_shared<T>(std::forward<Ts>(args)...);
}
};
template <template <typename...> class Ptr, typename T, typename ... Ts>
auto make(Ts&&... args)
{
return make_helper<Ptr, T>{}(std::forward<Ts>(args)...);
}
And then
bool get_resource_parameters(Param1& param1,..., ParamN& paramN)
{
//...
}
template <template <typename...> class Ptr>
Ptr<resource> open_resource(...)
{
Param1 param1;
...
ParamN paramN;
if(!get_resource_parameters(param1, ..., paramN))
return nullptr;
return = make<Ptr, resource>(param1, ..., paramN);
}
And check for nullptr
instead of split bool and smart_pointer.
Upvotes: 4
Reputation: 136515
std::shared_ptr
has a converting constructor from std::unique_ptr
. Why don't you make the function return a std::unique_ptr
by value:
unique_ptr<resource> open_resource(...);
This also serves as documentation that this is a factory function that transfers the ownership of the resource
to the caller.
And let the caller decide how they want it:
auto x = open_resource(...);
// or
std::shared_ptr<resource> x{open_resource(...)};
Upvotes: 12