I would like to replace the following code with std::lock():
for (mutex* m : mutexes) {
m->lock();
}
Is there anyway I could invoke std::lock () on those mutexes given a std::vector<mutex*>?
Unfortunately the standard library doesn't provide an overload for std::lock that takes a pair of iterators pointing to lockable objects. To use std::lock you must know the number of lockable objects at compile time, and pass them as arguments to the function. However, Boost does provide an overload that takes iterators, and it'll work with std::mutex.
The other piece of scaffolding you'll need is boost::indirect_iterator; this will apply an extra dereference when you dereference the iterator (needed because you have std::vector<std::mutex*> and not std::vector<std::mutex>. The latter would not be very useful anyway since std::mutex cannot be copied or moved.)
#include <boost/thread/locks.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <vector>
#include <mutex>
int main()
{
using mutex_list = std::vector<std::mutex*>;
mutex_list mutexes;
boost::indirect_iterator<mutex_list::iterator> first(mutexes.begin()),
last(mutexes.end());
boost::lock(first, last);
}
Live demo
Related
Ideally I'd want to do something like this, but is this safe/correct? When moving from some_list to other_list, you're editing the contents of some_list, and every subsequent iteration would be invalidated since the objects all contain null contents.
for (auto& object : some_list) {
other_list.emplace_back(std::move(object));
}
Is there any way I could make this code more efficient/safer/better or is this the best way I can possibly do this?
Merely modifying the contained objects doesn't invalidate iterators, so this code is safe. There's no need to write the loop yourself, though, since there's the std::move algorithm:
#include <algorithm>
#include <iterator>
template<typename InContainer, typename OutContainer>
void append_back(InContainer& some_list, OutContainer& other_list)
{
using std::begin;
using std::end;
std::move(begin(some_list), end(some_list), std::back_inserter(other_list);
some_list.clear();
}
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.
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) );
Consider:
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
class Gizmo
{
public:
Gizmo() : foo_(shared_ptr<string>(new string("bar"))) {};
Gizmo(Gizmo&& rhs); // Implemented Below
private:
shared_ptr<string> foo_;
};
/*
// doesn't use std::move
Gizmo::Gizmo(Gizmo&& rhs)
: foo_(rhs.foo_)
{
}
*/
// Does use std::move
Gizmo::Gizmo(Gizmo&& rhs)
: foo_(std::move(rhs.foo_))
{
}
int main()
{
typedef vector<Gizmo> Gizmos;
Gizmos gizmos;
generate_n(back_inserter(gizmos), 10000, []() -> Gizmo
{
Gizmo ret;
return ret;
});
random_shuffle(gizmos.begin(), gizmos.end());
}
In the above code, there are two versions of Gizmo::Gizmo(Gizmo&&) -- one uses std::move to actually move the shared_ptr, and the other just copies the shared_ptr.
Both version seem to work on the surface. One difference (the only difference I can see) is in the non-move version the reference count of the shared_ptr is temporarily increased, but only briefly.
I would normally go ahead and move the shared_ptr, but only to be clear and consistent in my code. Am I missing a consideration here? Should I prefer one version over the other for any technical reason?
The main issue here is not the small performance difference due to the extra atomic increment and decrement in shared_ptr but that the semantics of the operation are inconsistent unless you perform a move.
While the assumption is that the reference count of the shared_ptr will only be temporary there is no such guarantee in the language. The source object from which you are moving can be a temporary, but it could also have a much longer lifetime. It could be a named variable that has been casted to an rvalue-reference (say std::move(var)), in which case by not moving from the shared_ptr you are still maintaining shared ownership with the source of the move, and if the destination shared_ptr has a smaller scope then the lifetime of the pointed object will needlessly be extended.
I upvoted James McNellis' answer. I would like to make a comment about his answer but my comment won't fit in the comment format. So I'm putting it here.
A fun way to measure the performance impact of moving a shared_ptr vs copying one is to use something like vector<shared_ptr<T>> to move or copy a whole bunch of them and time it. Most compilers have a way to turn on/off move semantics by specifying the language mode (e.g. -std=c++03 or -std=c++11).
Here is code I just tested at -O3:
#include <chrono>
#include <memory>
#include <vector>
#include <iostream>
int main()
{
std::vector<std::shared_ptr<int> > v(10000, std::shared_ptr<int>(new int(3)));
typedef std::chrono::high_resolution_clock Clock;
typedef Clock::time_point time_point;
typedef std::chrono::duration<double, std::micro> us;
time_point t0 = Clock::now();
v.erase(v.begin());
time_point t1 = Clock::now();
std::cout << us(t1-t0).count() << "\u00B5s\n";
}
Using clang/libc++ and in -std=c++03 this prints out for me:
195.368µs
Switching to -std=c++11 I get:
16.422µs
Your mileage may vary.
The use of move is preferable: it should be more efficient than a copy because it does not require the extra atomic increment and decrement of the reference count.
I have:
vector of unique_ptrs of ObjectA
vector of newly default constructed vector of ObjectB, and
a function in Object B that has signature void f(unique_ptr<ObjectA> o).
(word Object omitted from here on)
How do I do Bvec[i].f(Avec[i]) for all 0 < i < length in parallel?
I have tried using transform(Bvec.begin(), Bvec.end(), A.begin(), B.begin(), mem_fun_ref(&B::f)), but it gives a bunch of errors and I'm not sure if it would even pass the right A as parameter, let alone allow me to move them. (&B::f(A.begin()) would not work as the last parameter either.
I have also thought of using for_each and then a lambda function, but not sure how to get the corresponding element. I thought of incrementing a counter, but then I don't think that parallelizes well (I could be wrong).
I can, of course, use a for loop from 0 to end, but I am pretty sure there is a simple thing I'm missing, and it is not parallel with a simple for loop.
Thanks.
Here is a non-parallel implementation using a handmade algorithm. I'm sure someone more versed in the functional could come up with a more elegant solution. The problem with transform is, that we cannot use it with functions that return void and I can't remember another stdlib function that takes two ranges and apply them to each other. If you really want to parallelize this, it needs to be done in the apply_to function. Launching an async task (e.g. std::async(*begin++, *begin2++) could work, although I have no experience with this and cannot get it to work on gcc 4.6.2.
#include <iterator>
#include <memory>
#include <vector>
#include <algorithm>
#include <functional>
// this is very naive it should check call different versions
// depending on the value_type of iterator2, especially considering
// that a tuple would make sense
template<typename InputIterator1, typename InputIterator2>
void apply_to(InputIterator1 begin, InputIterator1 end, InputIterator2 begin2) {
while(begin != end) {
(*begin++)(*begin2++);
}
}
struct Foo {
};
struct Bar {
void f(std::unique_ptr<Foo>) { }
};
int main()
{
std::vector< std::unique_ptr<Foo> > foos(10);
std::vector< Bar > bars(10);
std::vector< std::function<void(std::unique_ptr<Foo>) > > funs;
std::transform(bars.begin(), bars.end(), std::back_inserter(funs),
// non-const due to f non-const, change accordingly
[](Bar& b) { return std::bind(&Bar::f, &b, std::placeholders::_1); });
// now we just need to apply each element in foos with funs
apply_to(funs.begin(), funs.end(), std::make_move_iterator(foos.begin()));
return 0;
}