Why boost intrusive list 's push_back function requires lvalue? - c++

I am learning intrusive list:
#include <iostream>
#include <list>
#include <boost/intrusive/list.hpp>
struct DummyObject : public boost::intrusive::list_base_hook<>{
double price;
DummyObject(const double a): price(a){
}
};
using IntrusiveListContainer = boost::intrusive::list<DummyObject>;
using NonintrusiveListContainer = std::list<DummyObject>;
int main()
{
IntrusiveListContainer intrusivecontainer;
NonintrusiveListContainer nonintrusivecontainer;
intrusivecontainer.push_back(DummyObject (22.2)); // ERROR
nonintrusivecontainer.push_back(DummyObject (22.2));// compiled
return 0;
}
I understand the basic idea of intrusive list, but I cannot understand why push_back requires lvalue specifically. From a logic perspective, why intrusive list cannot cope with rvalue ?
Does lvalue requirement imply that, the user need to handle the life-circle of DummyObject by himself? In other word, when IntrusiveList pop_front, the pop-ed object will not be destructed ?
Also, event I pass by lvalue:
int main()
{
IntrusiveListContainer intrusivecontainer;
NonintrusiveListContainer nonintrusivecontainer;
DummyObject a(22.2);
intrusivecontainer.push_front(a); // compiled
//nonintrusivecontainer.push_back(DummyObject (22.2));// compiled
return 0;
}
the binary failed one of the assert:
intrusivelist: /usr/include/boost/intrusive/detail/generic_hook.hpp:48: void boost::intrusive::detail::destructor_impl(Hook&, boost::intrusive::detail::link_dispatch<(boost::intrusive::link_mode_type)1>) [with Hook = boost::intrusive::generic_hook<(boost::intrusive::algo_types)0, boost::intrusive::list_node_traits, boost::intrusive::dft_tag, (boost::intrusive::link_mode_type)1, (boost::intrusive::base_hook_type)1>]: Assertion `!hook.is_linked()' failed.

This is simple intrusive container doesn't perform memory management. It is your responsibility to ensure that stored object outlives intrusive container.
This is point out in documentation:
Intrusive and non-intrusive containers - 1.64.0
The user has to manage the lifetime of inserted objects independently from the containers.
Now temporary object will live shorter then intrusive container leading to undefined behavior, intrusive container do not create any copy. so use of r-value is not desired.
Now this version of your example works fine (no crash):
int main()
{
DummyObject a(22.2);
IntrusiveListContainer intrusivecontainer;
NonintrusiveListContainer nonintrusivecontainer;
intrusivecontainer.push_back(a); // ERROR
nonintrusivecontainer.push_back(a);// compiled
return 0;
}
And on other hand this version ends with assertion failure:
int main()
{
IntrusiveListContainer intrusivecontainer;
NonintrusiveListContainer nonintrusivecontainer;
DummyObject a(22.2);
intrusivecontainer.push_back(a); // ERROR
nonintrusivecontainer.push_back(a);// compiled
return 0;
}

I had the same assertion fail when working with intrusive lists, even when using lvalue.The assert fail will happen if the list is not empty when destroyed.
If you pop() all the values before the list destructor is called, should be ok.

Related

how to move unique_ptr object between two STL containers [duplicate]

This question already has answers here:
Move out element of std priority_queue in C++11
(6 answers)
Closed 2 years ago.
#include <utility>
#include<unordered_map>
#include<queue>
using namespace std;
struct Task {
char t;
int cnt;
Task(char ch, int cnt):t(ch), cnt(cnt){};
};
struct CompTask {
bool operator() (const unique_ptr<Task>& a, const unique_ptr<Task>& b) {
return a->cnt < b->cnt;
}
};
class Schedule {
public:
int schedule() {
unordered_map<unique_ptr<Task>, int> sleep_q;
priority_queue<unique_ptr<Task>, vector<unique_ptr<Task>>, CompTask> ready_q; // max heap
ready_q.push(std::make_unique<Task>('A', 1));
auto& ptr = ready_q.top();
//sleep_q.insert({ptr, 1}); // compile error
sleep_q.insert({std::move(ptr), 1}); // compile error
// some other code...
return 1;
}
};
int main() {
return 0;
}
// error:
cpp:38:17: error: no matching member function for call to 'insert'
sleep_q.insert({std::move(ptr), 1}); // compile error
~~~~~~~~^~~~~~
Programming context:
I had a task class and the program attempts to simulate the task scheduling (which involves moving a task back and forth between a ready queue and a sleep queue).
I have two std containers for ready queue and sleep queue respectively, the priority_queue has value type unique_ptr<Task>, the other is
unorder_map (sleep queue) whose key is also unique_ptr<Task>. I had trouble moving the unique_ptr object from priorty_queue to unordered_map (shown in the code).
My questions are:
(1) how to insert an item into the unordered_map, I had compilation errors on doing that.
(2) in the problem context, which type of "pointer" would be preferred? unique_ptr<Task>, shared_ptr<Task>, or just Task*
There seems to be lack of move functionality out of std::priority_queue<>. You can go around it using const_cast, though in rare cases it might cause undefined behavior (when stored element type is const).
int schedule() {
unordered_map<unique_ptr<Task>, int> sleep_q;
priority_queue<unique_ptr<Task>, vector<unique_ptr<Task>>, CompTask> ready_q; // max heap
ready_q.push(std::make_unique<Task>('A', 1));
unique_ptr<Task> ptr =
std::move(const_cast<unique_ptr<Task>&>(ready_q.top()));
// ^ Here. priority_queue::top() returns `const&`.
ready_q.pop(); // remove moved element from priority_queue
sleep_q.insert(std::make_pair(std::move(ptr), 1));
// some other code...
return 1;
}
Reference: This answer
To move a unique_ptr between std containers requires destroying one unique_ptr (stored within the first std container) and passing the backing data to a new unique_ptr. This can be done with std::move. However, it's usually easier to use a shared_ptr. Which avoids the problem by allowing shared_ptr point to the same task to be in two std containers simultaneously. Even if just for a moment.
However, since you could have multiple shared_ptr objects pointing the same task the shared_ptr wouldn't work to uniquely identify the task. To do that, I would recommend creating a Task Id for each task. A simple integer would work for this, as long as you guarantee it's unique to that task. An integer task Id would work well with a map since the Id could be used as the key.

How to model a vector of non-owning raw pointers extracted from unique_ptrs?

Note: Apologies if the title is unclear, I don't quite know how to express the issue in proper terms (improvement suggestions are very welcome).
Code, onlinegdb example of the working version and example of the non-working one first to simplify the explanation:
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int v = 0;
};
void some_library_function(const std::vector<A*>& objects)
{
// do something to each object without taking ownership
for(auto p : objects)
{
p->v = 42;
}
}
class B
{
public:
std::vector<std::shared_ptr<A>> m_objects; // this is a private field in my actual code
B():m_objects{std::make_shared<A>()}{};
void use_library()
{
std::vector<A*> observer_vector(m_objects.size());
for(int i=0; i<m_objects.size(); i++)
{
observer_vector[i] = m_objects[i].get(); // fails here if I use unique_ptr
}
some_library_function(observer_vector);
}
};
int main()
{
B b;
b.use_library();
std::cout << b.m_objects[0]->v;
return 0;
}
I have a library function that operates on a series of objects of class A passed in via std::vector<A*>. These objects are stored in a field of class B that owns the objects. I would like to model the "owns" part via a vector of std::vector<unique_ptr<A>>, but this makes it impossible to pass the objects down to the library function.
using shared_ptrs works, but I'm worried this is not as expressive as the unique_ptrs with regards to object ownership.
Is there a way to use unique_ptrs in the vector and still be able to use the library function?
You're already doing the right thing. A raw pointer is a perfectly reasonable way to model something with unknown or lacking ownership. You're not storing the vector anywhere, so there is no confusion and no risk.
The only problem here really is that you've had to regenerate the entire vector, which seems like a bit of a waste. Ultimately, if you're set on a vector<unique_ptr<A>> at the source, and you're stuck with vector<A*> at the destination, then there's nothing you can do about that. If the vector is small it doesn't really matter though.
observer_vector[i] = m_objects[i].get(); // fails if with unique_ptr because of operator= being deleted
No, that should be valid. You're just assigning a raw pointer.

Why stl vector can't contain coroutine objects?

I use coroutine in boost1.53, see my code below:
boost::coroutines::coroutine<int()> f(std::bind(foo, ...));
std::vector<decltype(f)> container; // it can be compiled
container.push_back(f); // compile error
the error:
no matching function for call to ‘std::vector<boost::coroutines::coroutine<int(),0> >::vector(paracel::coroutine<int>&)’
Update: The error is occured because there are no copy construction/operator in 'boost::coroutines::coroutine', case here is I only want to save the 'f's into a container which map a index to 'f'.
I also tried unordered_map, and emplace_back, it still can not work!
How can I make it work in C++?
Update2:
I tried vector,unordered_map, map together with emplace_back, push_back, std::move and all failed.
But list and deque is ok with push_back/emplace_back and std::move:
std::deque<decltype(f)> container1;
container.push_back(std::move(f)); // ok
std::deque<decltype(f)> container2;
container.emplace_back(std::move(f)); // ok
std::list<decltype(f)> container3;
container.push_back(std::move(f)); // ok
std::list<decltype(f)> container4;
container.emplace_back(std::move(f)); // ok
Why?
It looks as if boost::coroutines::coroutines<int()> doesn't support a copy constructor. You try to push_back() an lvalue, however. You might want to try moving the object into vector, though:
container.push_back(std::move(f));
If you check e.g. this reference you will see that the contained type
T must meet the requirements of CopyAssignable and CopyConstructible.
And
The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type meets the requirements of MoveConstructible and MoveAssignable, but many member functions impose stricter requirements.
If you check the coroutine class you will see that it has neither copy-assignment operator nor a copy-constructor. It do have move variants of those, but as noted by the second paragraph above, it's not always enough.
I use Boost 1.54 and it works for me with both g++4.8.2 and clang-3.4 with libc++:
#include <iostream>
#include <vector>
#include <boost/coroutine/coroutine.hpp>
typedef boost::coroutines::coroutine<int()> coro_t;
void f(coro_t::caller_type& ca)
{
ca(42);
}
int main()
{
std::vector<coro_t> coros;
coro_t foo(&f);
coros.push_back(std::move(foo));
coros.emplace_back(&f);
for(auto& coro : coros)
std::cout << coro.get() << std::endl;
}
I'm going to speculate that you don't have a working standard library or the move assignment in coroutine of boost 1.53 is not noexcept (you can check that with std::is_nothrow_move_assignable).
two possibilities:
1.) allocate the coroutines on freestore:
std::vector< shared_ptr< coroutine< void >::pull_type > v;
v.push_back( new coroutine< void >::pull_type(...) );
2.) use a moveaware-container (boost.container):
boost::container::vector< coroutine< void >::pull_type > v;
coroutine< void >::pull_type c(...)
v.push_back( boost::move( c) );

Is a default value of nullptr in a map of pointers defined behaviour?

The following code seems to always follow the true branch.
#include <map>
#include <iostream>
class TestClass {
// implementation
}
int main() {
std::map<int, TestClass*> TestMap;
if (TestMap[203] == nullptr) {
std::cout << "true";
} else {
std::cout << "false";
}
return 0;
}
Is it defined behaviour for an uninitialized pointer to point at nullptr, or an artifact of my compiler?
If not, how can I ensure portability of the following code? Currently, I'm using similar logic to return the correct singleton instance for a log file:
#include <string>
#include <map>
class Log {
public:
static Log* get_instance(std::string path);
protected:
Log(std::string path) : path(path), log(path) {};
std::string path;
std::ostream log;
private:
static std::map<std::string, Log*> instances;
};
std::map<std::string, Log*> Log::instances = std::map<std::string, Log*>();
Log* Log::get_instance(std::string path) {
if (instances[path] == nullptr) {
instances[path] = new Log(path);
}
return instances[path];
}
One solution would be to use something similar to this where you use a special function provide a default value when checking a map. However, my understanding is that this would cause the complexity of the lookup to be O(n) instead of O(1). This isn't too much of an issue in my scenario (there would only ever be a handful of logs), but a better solution would be somehow to force pointers of type Log* to reference nullptr by default thus making the lookup check O(1) and portable at the same time. Is this possible and if so, how would I do it?
The map always value-initializes its members (in situations where they are not copy-initialized, of course), and value-initialization for builtin types means zero-initialization, therefore it is indeed defined behaviour. This is especially true for the value part of new keys generated when accessing elements with operator[] which didn't exist before calling that.
Note however that an uninizialized pointer is not necessarily a null pointer; indeed, just reading its value already invokes undefined behaviour (and might case a segmentation fault on certain platforms under certain circumstances). The point is that pointers in maps are not uninitialized. So if you write for example
void foo()
{
TestClass* p;
// ...
}
p will not be initialized to nullptr.
Note however that you might want to check for presence instead, to avoid accumulating unnecessary entries. You'd check for presence using the find member function:
map<int, TestClass*>::iterator it = TestMap.find(203);
if (it == map.end())
{
// there's no such element in the map
}
else
{
TestClass* p = it->second;
// ...
}
Yes, that's defined behaviour. If an element isn't yet in a map when you access it via operator[], it gets default constructed.

storing mem_fun in a standard container

Is there a way to create a vector< mem_fun_t< ReturnType, MyClass > > ?
The error i'm seeing is:
error C2512: 'std::mem_fun1_t<_Result,_Ty,_Arg>' : no appropriate default constructor available
I really can't see why it would not work, but it's actually a pretty ugly solution. Just take vector<function<ReturnType(MyClass*)>> and be without those issues present in C++03 binders.
You certainly can create such a vector.
#include <vector>
#include <functional>
#include <iostream>
struct MyClass
{
int a() { return 1; }
int b() { return 2; }
};
int main()
{
std::vector<std::mem_fun_t<int, MyClass> > vec;
vec.push_back(std::mem_fun(&MyClass::a));
vec.push_back(std::mem_fun(&MyClass::b));
MyClass x;
for (size_t i = 0; i != vec.size(); ++i) {
std::cout << vec[i](&x) << '\n';
}
}
If you are having problems, read the error message carefully. For example, std::mem_fun can return all sorts of wrappers, depending on what you pass to it.
Or indeed, switch to boost's or C++0x's function.
Edit: With this particular error message, I assume that you are doing something that invokes the default constructor for contained type (e.g resize or specifying the size with the vector's constructor). You can't use those functions.
mem_fun_t meets the requirements to be stored in a container (it is copy-constructible and assignable), so the answer is yes.
However, it isn't default-constructible or comparable, so there are some things you can't do with a container of them, including:
Resizing, unless you provide a value to fill with
Constructing with a non-zero size, unless you provide a value to fill with
Comparing containers
The error you are seeing comes from trying to either resize, or construct with a size.