unique_ptr boost equivalent?
It's not possible to create something like unique_ptr
without C++0x (where it's part of the standard library, and so Boost doesn't need to provide it).
Specifically without rvalue references, which are a feature in C++0x, a robust implementation of unique_ptr
is impossible, with or without Boost.
In C++03, there are a few possible alternatives, although each have their flaws.
boost::shared_ptr
is probably the simplest replacement in terms of capabilites. You can safely use it anywhere you'd otherwise use aunique_ptr
and it'd work. It just wouldn't be as efficient, because of the added reference counting. But if you're looking for a simple drop-in replacement that's able to handle everythingunique_ptr
can do, this is probably your best bet. (Of course, ashared_ptr
can do a lot more as well, but it can also simply be used as a drop-in replacement forunique_ptr
.)boost::scoped_ptr
is similar tounique_ptr
but does not allow transfer of ownership. It works great as long as the smart pointer is meant to retain exclusive ownership throughout its lifetime.std::auto_ptr
works very similar tounique_ptr
, but has a few limitations, mainly that it can not be stored in standard library containers. If you're simply looking for a pointer that allows transfer of ownership, but which is not meant to be stored in containers or copied around, this is probably a good bet.
Using a unique_ptr without C++11
Well, boost::movelib::unique_ptr
is part of the Boost.Move library which offers "Portable move semantics for C++03 and C++11 compilers". Since unique_ptr
clearly needs move semantic, this looks like your best choice.
Boost graph bundled properties with std::unique_ptr
Why is the copy constructor is necessary for list initialization in the first place? Is there something I do not understand about it, or is this a shortcoming of boost-graph?
That's not a thing and also not what's happening.
Aside: in copy-initialization the assignment can be elided by the compiler, but operator= still needs to be accessible for it to be valid code.
But in this case it's just about the library code not being move-aware. You have to realize that "bundled" properties are separately stored. The bundle (as any property) is required to be default constructible anyways (so add_vertex(g)
works) so the implementation is simplified by always assigning to the default-constructed property.
Since it's not move-aware, assignment will not forward the rvalue and things don't compile.
OPTIONS
The linked answer already showd:
if(g[v].node_logic) {
g[v].node.reset(new custom_node(g[v].vertex_name, 0, standby, normal));
}More options:
VertexProperties props {0,
std::make_unique<custom_node>("inner", 2)};
auto vd = boost::add_vertex(g);
g[vd] = std::move(props);Wrap yo' shit! You can create any interface you prefer:
auto add_vertex = [&g](VertexProperties&& props) {
auto vd = boost::add_vertex(g);
g[vd] = std::move(props);
return vd;
};
add_vertex({0, std::make_unique<custom_node>("inner", 2)});You can even elaborate on that:
auto add_vertex = [&g](int id, std::string name, int capacity = -1) {
auto vd = boost::add_vertex(g);
g[vd] = { id, std::make_unique<custom_node>(name, capacity) };
return vd;
};
add_vertex(0, "inner", 2);
add_vertex(1, "outer", 3);
add_vertex(2, "other");
All the above options Live On Coliru
The latter interface is far superior anyways, if you ask me.
If you want you can use ADL to make it available to others:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <memory>
namespace MyLib { // for ADL demo
struct custom_node {
custom_node(std::string name, int capacity)
: name(std::move(name)), capacity(capacity) {}
std::string name = "uninitialized";
int capacity = -1;
friend std::ostream& operator<<(std::ostream& os, custom_node const& cn) {
return os << "{" << std::quoted(cn.name) << ", " << cn.capacity << "}";
}
};
struct VertexProperties {
int id{};
std::unique_ptr<custom_node> node;
friend std::ostream& operator<<(std::ostream& os, VertexProperties const& vp) {
os << vp.id;
if (vp.node)
os << ", " << *vp.node;
return os;
}
};
template <typename G>
auto add_vertex(G& g, int id, const std::string& name, int capacity = -1) {
auto vd = boost::add_vertex(g);
g[vd] = { id, std::make_unique<custom_node>(name, capacity) };
return vd;
}
}
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, MyLib::VertexProperties>;
using custom_vertex = Graph::vertex_descriptor;
using custom_edge = Graph::edge_descriptor;
int main() {
Graph g;
add_vertex(g, 10, "inner", 2);
add_vertex(g, 11, "outer", 3);
add_vertex(g, 12, "other");
for (auto vd : boost::make_iterator_range(vertices(g))) {
std::cout << g[vd] << "\n";
}
}
Prints
10, {"inner", 2}
11, {"outer", 3}
12, {"other", -1}
SOMEONE MENTIONED DESIGN?
If all you want is unique ownership with optional/lazy construction, why not:
struct VertexProperties {
int id{};
std::optional<custom_node> node;
};
Or even just
struct VertexProperties {
int id{};
custom_node node;
};
The ownership semantics will be the same, without the costs:
Graph g;
add_vertex({10, custom_node{"inner", 2}}, g);
add_vertex({11, custom_node{"outer", 3}}, g);
add_vertex({12, custom_node{"other"}}, g);
That's just using the standard boost::add_vertex
overloads from BGL. Without optional<>
it can become even simpler:
add_vertex({10, {"inner", 2}}, g);
add_vertex({11, {"outer", 3}}, g);
add_vertex({12, {"other"}}, g);
Also Live On Coliru (without std::optional: Live)
#include <boost/graph/adjacency_list.hpp>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <memory>
#include <optional>
struct custom_node {
custom_node(std::string name, int capacity = -1)
: name(std::move(name)), capacity(capacity) {}
std::string name = "uninitialized";
int capacity = -1;
friend std::ostream& operator<<(std::ostream& os, custom_node const& cn) {
return os << "{" << std::quoted(cn.name) << ", " << cn.capacity << "}";
}
};
struct VertexProperties {
int id{};
std::optional<custom_node> node;
friend std::ostream& operator<<(std::ostream& os, VertexProperties const& vp) {
os << vp.id;
if (vp.node) os << ", " << *vp.node;
return os;
}
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexProperties>;
using custom_vertex = Graph::vertex_descriptor;
using custom_edge = Graph::edge_descriptor;
int main() {
Graph g;
add_vertex({10, custom_node{"inner", 2}}, g);
add_vertex({11, custom_node{"outer", 3}}, g);
add_vertex({12, custom_node{"other"}}, g);
for (auto vd : boost::make_iterator_range(vertices(g))) {
std::cout << g[vd] << "\n";
}
}
Prints
10, {"inner", 2}
11, {"outer", 3}
12, {"other", -1}
stl container with std::unique_ptr's vs boost::ptr_container
They really solve two similar but different problems.
A pointer container is a way to store objects in a container that just so happen to be pointers to allocated memory rather than values. They do everything in their power to hide the fact that they are a container of pointers. This means:
- Entries in the container cannot be NULL.
- Values you get from iterators and functions are references to the type, not pointers to the type.
- Working with many standard algorithms can be... tricky. And by "tricky", I mean broken. Pointer containers have their own built-in algorithms.
However, the fact that pointer containers know that they're containers of pointers, they can offer some new functionality:
- A
clone
member function that performs a deep copy, via the use of a certain "Cloneable" concept on the type of the object. - The ability of a container to release ownership of its objects (after a shallow copy, for example).
- Built-in functions to transfer ownership to other containers.
They really are quite different concepts. There is a lot of stuff you would have to do manually that pointer containers can do automatically with specialized functions.
If you really need a container of pointers, then you can use containers of unique_ptr
. But if you need to store a bunch of objects that you happen to heap allocate, and you want to play special games with them involving ownership and such, then the pointer containers are not a bad idea.
Boost.Python: How to expose std::unique_ptr
In short, Boost.Python does not support move-semantics, and therefore does not support std::unique_ptr
. Boost.Python's news/change log has no indication that it has been updated for C++11 move-semantics. Additionally, this feature request for unique_ptr
support has not been touched for over a year.
Nevertheless, Boost.Python supports transferring exclusive ownership of an object to and from Python via std::auto_ptr
. As unique_ptr
is essentially a safer version of auto_ptr
, it should be fairly straight forward to adapt an API using unique_ptr
to an API that uses auto_ptr
:
- When C++ transfers ownership to Python, the C++ function must:
- be exposed with CallPolicy of
boost::python::return_value_policy
with aboost::python::manage_new_object
result converter. - have
unique_ptr
release control viarelease()
and return a raw pointer
- be exposed with CallPolicy of
- When Python transfers ownership to C++, the C++ function must:
- accept the instance via
auto_ptr
. The FAQ mentions that pointers returned from C++ with amanage_new_object
policy will be managed viastd::auto_ptr
. - have
auto_ptr
release control to aunique_ptr
viarelease()
- accept the instance via
Given an API/library that cannot be changed:
/// @brief Mockup Spam class.
struct Spam;
/// @brief Mockup factory for Spam.
struct SpamFactory
{
/// @brief Create Spam instances.
std::unique_ptr<Spam> make(const std::string&);
/// @brief Delete Spam instances.
void consume(std::unique_ptr<Spam>);
};
The SpamFactory::make()
and SpamFactory::consume()
need to be wrapped via auxiliary functions.
Functions transferring ownership from C++ to Python can be generically wrapped by a function that will create Python function objects:
/// @brief Adapter a member function that returns a unique_ptr to
/// a python function object that returns a raw pointer but
/// explicitly passes ownership to Python.
template <typename T,
typename C,
typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...))
{
return boost::python::make_function(
[fn](C& self, Args... args) { return (self.*fn)(args...).release(); },
boost::python::return_value_policy<boost::python::manage_new_object>(),
boost::mpl::vector<T*, C&, Args...>()
);
}
The lambda delegates to the original function, and releases()
ownership of the instance to Python, and the call policy indicates that Python will take ownership of the value returned from the lambda. The mpl::vector
describes the call signature to Boost.Python, allowing it to properly manage function dispatching between the languages.
The result of adapt_unique
is exposed as SpamFactory.make()
:
boost::python::class_<SpamFactory>(...)
.def("make", adapt_unique(&SpamFactory::make))
// ...
;
Generically adapting SpamFactory::consume()
is a more difficult, but it is easy enough to write a simple auxiliary function:
/// @brief Wrapper function for SpamFactory::consume_spam(). This
/// is required because Boost.Python will pass a handle to the
/// Spam instance as an auto_ptr that needs to be converted to
/// convert to a unique_ptr.
void SpamFactory_consume(
SpamFactory& self,
std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python.
{
return self.consume(std::unique_ptr<Spam>{ptr.release()});
}
The auxiliary function delegates to the original function, and converts the auto_ptr
provided by Boost.Python to the unique_ptr
required by the API. The SpamFactory_consume
auxiliary function is exposed as SpamFactory.consume()
:
boost::python::class_<SpamFactory>(...)
// ...
.def("consume", &SpamFactory_consume)
;
Here is a complete code example:
#include <iostream>
#include <memory>
#include <boost/python.hpp>
/// @brief Mockup Spam class.
struct Spam
{
Spam(std::size_t x) : x(x) { std::cout << "Spam()" << std::endl; }
~Spam() { std::cout << "~Spam()" << std::endl; }
Spam(const Spam&) = delete;
Spam& operator=(const Spam&) = delete;
std::size_t x;
};
/// @brief Mockup factor for Spam.
struct SpamFactory
{
/// @brief Create Spam instances.
std::unique_ptr<Spam> make(const std::string& str)
{
return std::unique_ptr<Spam>{new Spam{str.size()}};
}
/// @brief Delete Spam instances.
void consume(std::unique_ptr<Spam>) {}
};
/// @brief Adapter a non-member function that returns a unique_ptr to
/// a python function object that returns a raw pointer but
/// explicitly passes ownership to Python.
template <typename T,
typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (*fn)(Args...))
{
return boost::python::make_function(
[fn](Args... args) { return fn(args...).release(); },
boost::python::return_value_policy<boost::python::manage_new_object>(),
boost::mpl::vector<T*, Args...>()
);
}
/// @brief Adapter a member function that returns a unique_ptr to
/// a python function object that returns a raw pointer but
/// explicitly passes ownership to Python.
template <typename T,
typename C,
typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...))
{
return boost::python::make_function(
[fn](C& self, Args... args) { return (self.*fn)(args...).release(); },
boost::python::return_value_policy<boost::python::manage_new_object>(),
boost::mpl::vector<T*, C&, Args...>()
);
}
/// @brief Wrapper function for SpamFactory::consume(). This
/// is required because Boost.Python will pass a handle to the
/// Spam instance as an auto_ptr that needs to be converted to
/// convert to a unique_ptr.
void SpamFactory_consume(
SpamFactory& self,
std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python.
{
return self.consume(std::unique_ptr<Spam>{ptr.release()});
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Spam, boost::noncopyable>(
"Spam", python::init<std::size_t>())
.def_readwrite("x", &Spam::x)
;
python::class_<SpamFactory>("SpamFactory", python::init<>())
.def("make", adapt_unique(&SpamFactory::make))
.def("consume", &SpamFactory_consume)
;
}
Interactive Python:
>>> import example
>>> factory = example.SpamFactory()
>>> spam = factory.make("a" * 21)
Spam()
>>> spam.x
21
>>> spam.x *= 2
>>> spam.x
42
>>> factory.consume(spam)
~Spam()
>>> spam.x = 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
None.None(Spam, int)
did not match C++ signature:
None(Spam {lvalue}, unsigned int)
Is there a boost::shared_ptrT equivalent in C#?
boost::shared_ptr allows for reference counted pointers in an environment which is not garbage collected. The .NET runtime allows for full garbage collection, so there is no need for a reference counting pointer container - simply store your object references.
Related Topics
Image Edge Smoothing with Opencv
Do I Have to Bind a Udp Socket in My Client Program to Receive Data? (I Always Get Wsaeinval)
Convert Eigen Matrix to C Array
Are There Any Downsides to Using Upx to Compress a Windows Executable
How to Return Array from C++ Function to Python Using Ctypes
Implicit Conversion When Overloading Operators for Template Classes
How to Sort a Linked List Using Bubble-Sort
Incrementing Iterators: Is ++It More Efficient Than It++
Error: 'Int32_Max' Was Not Declared in This Scope
How Does C++ Link Template Instances
Is 1.0 a Valid Output from Std::Generate_Canonical
Show Two Digits After Decimal Point in C++
Advantages of Classes with Only Static Methods in C++
Is C++ Static Member Variable Initialization Thread-Safe
How to Parallelize a for Loop Through a C++ Std::List Using Openmp