Const lock wrapper for STL containers - c++

I'm trying to create a wrapper for an std::vector (or any other container from STL, if possible) that can "lock" and "unlock" the const state of a vector that it's holding.
For example, if I create an object of that wrapper, I want to be able to do something like this:
int main()
{
ConstLockVectorWrapper<int> myWrapper(std::vector<int>{}); // Here I pass an empty vector in the constructor parameters,
// which means that my wrapper will be holding an empty vector
// By default the vector inside my wrapper is not locked,
// I can change its size and the values that it holds
myWrapper.get().push_back(10); // ok
myWrapper.get().push_back(20); // ok
myWrapper.get().at(0) = 5; // ok
print(myWrapper.get()); // Prints 5 20
myWrapper.lock(); // Now I made the vector inside my wrapper unchangable
myWrapper.get().push_back(30); // error, the vector is locked
myWrapper.get().at(0) = 55; // error
print(myWrapper.get()); // ok
myWrapper.unlock(); // Now I can change my vector's size and its values again
_getch();
return 0;
}
The only solution (that's not working, unfortunately) I've got, is to create a const reference (const std::vector<T> &) and a regular reference (td::vector<T> &) inside a wrapper class, and bound them to the main vector in our wrapper class.
So, this is what I've done:
template <typename T>
class ConstLockVectorWrapper {
public:
ConstLockVectorWrapper(const std::vector<T> & vec)
: wrappedVector(vec), wrappedVectorRef(wrappedVector), wrappedVectorConstRef(wrappedVector), constLock(false)
{}
void lock()
{
if (constLock) // if the vector is already locked, we just exit the function
return;
// else we lock the vector
constLock = true;
}
void unlock()
{
if (!constLock) // if the vector is already unlocked (changable), we just exit the function
return;
// else we unlock the vector
constLock = false;
}
return_type get() // I need to return a const std::vector<T> & if constLock == true, and std::vector<T> & otherwise, what return type should I put in here?
{
if (constLock)
return wrappedVectorConstRef;
else
return wrappedVectorRef;
}
private:
bool constLock;
std::vector<T> wrappedVector;
// refs
std::vector<T> & wrappedVectorRef;
const std::vector<T> & wrappedVectorConstRef;
};
Of course, it doesn't work. Just because I don't know what to put in the return type of my get() fucntion.
I've tried using trailing return type, didn't work:
template <typename T>
class ConstLockVectorWrapper {
public:
// ...
private:
bool constLock;
std::vector<T> wrappedVector;
// refs
std::vector<T> & wrappedVectorRef;
const std::vector<T> & wrappedVectorConstRef;
public:
auto get() -> decltype((constLock ? wrappedVectorConstRef : wrappedVectorRef))
{
if (constLock)
return wrappedVectorConstRef;
else
return wrappedVectorRef;
}
};
I can't come up with any solution that will actually work, because I'm not so good at C++ yet.
So I'm asking for your help with my problem. Any suggestions or hints to solve this problem would be appreciated!
Thanks
PS
My main goal is to make my wrapper container-type-independent, so it can "lock" and "unlock" the const state of the container it's holding, independently of its type.
And here's the print() function I used in the first code snippet:
template <typename Container>
void print(const Container & c)
{
for (const auto & var : c)
std::cout << var << std::endl;
}

Fundamentally, a method always returns the same thing. The same type. Every time. It's not possible, in C++, to have a method sometimes return one type, and another type at other times. C++ does not work this way.
So, the initial approach would be to have get() return a proxy object with a state. Using, roughly, the same classes and names from your question:
class return_type {
bool is_const;
std::vector<T> &wrapped_vec;
public:
return_type(bool is_constArg,
std::vector<T> &wrapped_vecArg)
: is_const(is_constArg), wrapped_vec(wrapped_vecArg)
{
}
void push_back(T &&t)
{
if (is_const)
throw std::runtime_error(); // Or, whatever...
wrapped_vec.push_back(std::forward<T>(t));
}
// return_type will have to implement, and baby-sit all other
// methods you wish to invoke on the underlying vector.
};
return_type get()
{
return return_type(constLock);
}
This is simple, but crude and somewhat tedious. You would have to implement every std::vector method you need to use in the return_type proxy.
A better approach would be to take advantage of C++11 lambdas. This will avoid the need to reimplement every wheel, at an expense of some additional code bloat. But, big deal. RAM is cheap, these days. Instead of get() and return_type, you will now be implementing two template methods in your wrapper: get_const() and get_mutable(). Each one of them takes a lambda parameter and invokes it and, if all goes well, passing it the wrapped vector as an argument:
template<typename lambda>
void get_mutable(lambda &&l)
{
if (constLock)
throw std::runtime_error(); // Or, whatever...
l(wrapped_vec);
}
template<typename lambda>
void get_const(lambda &&l)
{
l(const_cast<const std::vector<T> &>(wrapped_vec));
}
The only thing you now need to decide is whether you need access a mutable or a constant vector, and pick the right getter:
myWrapper.get_mutable( [&](std::vector<int> &v) { v.push_back(10); } );
get_mutable() throws an exception if the vector is locked at this time. Otherwise it passes the vector to your lambda. Your lambda does whatever the heck it wants with it, which can be push_back(), or anything else, then returns.
But if you only need read-only access to the vector, use get_const():
int s;
myWrapper.get_const( [&](const std::vector<int> &v) { s=v.size(); } );
Note that get_const() takes care to const_cast the vector, before invoking the lambda, so the lambda will not be able to modify it. This will be enforced at compile-time.
With some additional work, it would also be possible to clean this up a little bit, and have the getter also return whatever lambda returns to the caller, making it possible to do something like this:
int s=myWrapper.get_const( [&](const std::vector<int> &v) { return v.size(); } );
It's possible to have get_const() and get_mutable() be smart enough to figure out if the lambda returns something, and happily pass it back to the caller, whatever it is. And how to do that, I suppose, will have to be another question on stackoverflow.com
P.S. If you don't have C++11, you can just have get_const() and get_mutable() return the wrapped vector (with get_mutable() verifying that it's not locked). This really accomplishes the same thing. The key point is that due to the way that C++ works, you will have to disambiguate, in advance, whether you need constant or mutable access.

I was working on a similar problem a while back. In multithreaded environment sometimes its more efficient to have different types of lock depending on whether you are reading or writing. But the locking is entirely cooperative. It is possible to obtain a read-only lock but still accidentally write to the object.
One solution I am exploring is, instead of obtaining a read-only lock from an object, getting a read-only wrapper of my object so that not only is the object read-only locked it is also only possible to call read-only (const) methods on the object.
The basic wrapper I used was something like this:
template<typename T>
class ConstWrapper
{
T& v;
public:
ConstWrapper(T& v): v(v) {}
T const& operator* () const { return v; } // return const reference
T const* operator->() const { return &v;} // return const pointer
};
By overloading the * and -> operators you get a kind of pass through ability to call the enclosed objects methods - but using pointer semantics (though its not a pointer).
std::vector<int> v {1, 2, 3, 4}; // not const
ConstWrapper<std::vector<int>> cv(v); // const wrapper
std::cout << cv->at(0) << '\n'; // okay at() is a const method
cv->push_back(8); // ILLEGAL!! push_back() is not a const method

Related

Implicit conversion of pointer to std::unique_ptr in foreach loop

I have an api call which populates an array of raw pointers to be used by the caller. This function heap allocates each raw pointer but does not allocate the array.
I cannot change this API function regardless of how bad it is.
Calling the api function code looks something like this:
size_t response_count = api.getResponseCount();
std::vector<Response*> responses(response_count);
api.getResponses(responses.data());
for(auto response : responses) {
// Do some processing with response
delete response;
}
I would like to wrap each response in a unique_ptr such that it is still cleaned up at the end of the loop iteration without having to explicitly call delete. Ideally, this would look something like:
for(std::unique_ptr<Response> response : responses) {
// Do some processing with response
// No need to delete response, it will be cleaned up as it goes out of scope
}
This does not compile because the compiler cannot convert a pointer to a unique_ptr:
error: conversion from ‘Response*’ to non-scalar type ‘std::unique_ptr<Response>’ requested
Is there way to cast each element of the container to a smart pointer in this way, or do I need to explicitly delete the raw pointer?
Instead of wrapping each pointer in a unique_ptr, I'd consider using a Boost ptr_vector.
Assuming the data is allocated so you actually can use delete to delete it, the code would look something like this:
size_t response_count = api.getResponseCount();
// Unfortunately, we have to define, then resize. It has a ctor that takes a size,
// but it treats that as an amount to reserve rather than an actual size.
boost::ptr_vector<Response> responses;
responses.resize(response_count);
api.getResponses(responses.c_array());
for(auto response : responses) {
// Do some processing with response
}
...and when responses goes out of scope, it will delete all the objects pointed to by the pointers it contains. If necessary, you can specify an Allocator class that defines how the objects are allocated and deleted.
Reference
https://www.boost.org/doc/libs/1_71_0/libs/ptr_container/doc/ptr_container.html
Though not considered good practice, you can derive from unique_ptr with a non explicit constructor.
template<typename P>
struct MakeUnique : std::unique_ptr<P> {
MakeUnique(P* p) : std::unique_ptr<P>(p) {}
};
This can then be used like this:
for ( MakeUnique<Response> resp : responses ) {
...
}
Probably the closest thing to a one-liner. See working version here.
You can create a wrapper for the vector<response*> that hands out unique pointers
struct wrapper{
struct iterator {
iterator( std::vector<response*>::iterator it ) : it_(it){}
friend bool operator!=( iterator const& lhs, iterator const &rhs ){ return lhs.it_ != rhs.it_; }
void operator++(){ ++it_;}
std::unique_ptr<response> operator*(){ return std::unique_ptr<response>(*it_); }
private:
std::vector<response*>::iterator it_;
};
wrapper( std::vector<response*>& rs ) : rs_{rs} {}
iterator begin() const { return iterator{rs_.begin()}; }
iterator end() const { return iterator{rs_.end()}; }
private:
std::vector<response*>& rs_;
};
You can then iterate over the responses like this:
for( auto resp : wrapper( responses ) ){...
}
See working version here.

constructing a std::vector using std::transform. Possibility to return unnamed result?

Let's have
class InputClass;
class OutputClass;
OutputClass const In2Out(InputClass const &in)
{
//conversion implemented
}
and finally
std::vector<OutputClass> Convert(std::vector<InputClass> const &input)
{
std::vector<OutputClass> res;
res.reserve(input.size());
//either
for (auto const &in : input)
res.emplace_back(In2Out(in));
return res;
//or something like
std::transform(input.begin(), input.end(), std::back_inserter(res), [](InputClass const &in){return In2Out(in);});
return res;
}
And now my question:
Can I rewrite the Convert function somehow avoiding the need to name the new container? I. e. is there a way to construct a vector directly using something roughly like std::transform or std::for_each?
As in (pseudocode, this unsurprisingly does not work or even build)
std::vector<OutputClass> Convert(std::vector<InputClass> const &input)
{
return std::transform(input.begin(), input.end(), std::back_inserter(std::vector<OutputClass>()), [](InputClass const &in){return In2Out(in);});
}
Searched, but did not find any elegant solution. Thanks!
Starting in C++ 20 you can use the new std::ranges::transform_view to accomplish what you want. It will call your transformation function for each element in the container that it is adapting and you can use that view to invoke std::vector's iterator range constructor which will allocate the memory for the entire vector once and then populate the elements. It still requires you to create a variable in the function but it becomes much more streamlined. That would give you something like
std::vector<OutputClass> Convert(std::vector<InputClass> const &input)
{
auto range = std::ranges::transform_view(input, In2Out);
return {range.begin(), range.end()};
}
Do note that this should optimize to the exact same code your function generates.
Yes it is possible, and quite simple when using boost:
struct A
{
};
struct B
{
};
std::vector<B> Convert(const std::vector<A> &input)
{
auto trans = [](const A&) { return B{}; };
return { boost::make_transform_iterator(input.begin(), trans), boost::make_transform_iterator(input.end(), trans) };
}
https://wandbox.org/permlink/ZSqt2SbsHeY8V0mt
But as other mentioned this is weird and doesn't provide any gain (no performance gain or readability gain)
Can I rewrite the Convert function somehow avoiding the need to name the new container?
Not using just std::transform. std::transform itself never creates a container. It only inserts elements to an output iterator. And in order to both get output iterator to a container, and return the container later, you pretty much need a name (unless you allocate the container dynamically, which would be silly and inefficient).
You can of course write a function that uses std::transform, creates the (named) vector, and returns it. Then caller of that function doesn't need to care about that name. In fact, that's pretty much what your function Convert is.

Why can't I wrap a T* in an std::vector<T>?

I have a T* addressing a buffer with len elements of type T. I need this data in the form of an std::vector<T>, for certain reasons. As far as I can tell, I cannot construct a vector which uses my buffer as its internal storage. Why is that?
Notes:
Please don't suggest I use iterators - I know that's usually the way around such issues.
I don't mind that the vector having to copy data around if it's resized later.
This question especially baffles me now that C++ has move semantics. If we can pull an object's storage from under its feet, why not be able to shove in our own?
You can.
You write about std::vector<T>, but std::vector takes two template arguments, not just one. The second template argument specifies the allocator type to use, and vector's constructors have overloads that allow passing in a custom instance of that allocator type.
So all you need to do is write an allocator that uses your own internal buffer where possible, and falls back to asking the default allocator when your own internal buffer is full.
The default allocator cannot possibly hope to handle it, since it would have no clue on which bits of memory can be freed and which cannot.
A sample stateful allocator with an internal buffer containing already-constructed elements that should not be overwritten by the vector, including a demonstration of a big gotcha:
struct my_allocator_state {
void *buf;
std::size_t len;
bool bufused;
const std::type_info *type;
};
template <typename T>
struct my_allocator {
typedef T value_type;
my_allocator(T *buf, std::size_t len)
: def(), state(std::make_shared<my_allocator_state, my_allocator_state>({ buf, len, false, &typeid(T) })) { }
template <std::size_t N>
my_allocator(T(&buf)[N])
: def(), state(std::make_shared<my_allocator_state, my_allocator_state>({ buf, N, false, &typeid(T) })) { }
template <typename U>
friend struct my_allocator;
template <typename U>
my_allocator(my_allocator<U> other)
: def(), state(other.state) { }
T *allocate(std::size_t n)
{
if (!state->bufused && n == state->len && typeid(T) == *state->type)
{
state->bufused = true;
return static_cast<T *>(state->buf);
}
else
return def.allocate(n);
}
void deallocate(T *p, std::size_t n)
{
if (p == state->buf)
state->bufused = false;
else
def.deallocate(p, n);
}
template <typename...Args>
void construct(T *c, Args... args)
{
if (!in_buffer(c))
def.construct(c, std::forward<Args>(args)...);
}
void destroy(T *c)
{
if (!in_buffer(c))
def.destroy(c);
}
friend bool operator==(const my_allocator &a, const my_allocator &b) {
return a.state == b.state;
}
friend bool operator!=(const my_allocator &a, const my_allocator &b) {
return a.state != b.state;
}
private:
std::allocator<T> def;
std::shared_ptr<my_allocator_state> state;
bool in_buffer(T *p) {
return *state->type == typeid(T)
&& points_into_buffer(p, static_cast<T *>(state->buf), state->len);
}
};
int main()
{
int buf [] = { 1, 2, 3, 4 };
std::vector<int, my_allocator<int>> v(sizeof buf / sizeof *buf, {}, buf);
v.resize(3);
v.push_back(5);
v.push_back(6);
for (auto &i : v) std::cout << i << std::endl;
}
Output:
1
2
3
4
6
The push_back of 5 fits into the old buffer, so construction is bypassed. When 6 is added, new memory is allocated, and everything starts acting as normal. You could avoid that problem by adding a method to your allocator to indicate that from that point onward, construction should not be bypassed any longer.
points_into_buffer turned out to be the hardest part to write, and I've omitted that from my answer. The intended semantics should be obvious from how I'm using it. Please see my question here for a portable implementation in my answer there, or if your implementation allows it, use one of the simpler versions in that other question.
By the way, I'm not really happy with how some implementations use rebind in such ways that there is no avoiding storing run-time type info along with the state, but if your implementation doesn't need that, you could make it a bit simpler by making the state a template class (or a nested class) too.
The short answer is that a vector can't use your buffer because it wasn't designed that way.
It makes sense, too. If a vector doesn't allocate its own memory, how does it resize the buffer when more items are added? It allocates a new buffer, but what does it do with the old one? Same applies to moving - if the vector doesn't control its own buffer, how can it give control of this buffer to another instance?
These days - you no longer need to wrap a T* in an std::vector, you can wrap it with an std::span (in C++20; before that - use gsl::span). A span offers you all the convenience of a standard library container - in fact, basically all relevant features of std::vector excluding changes to the size - with a very thin wrapper class. That's what you want to use, really.
For more on spans, read: What is a "span" and when should I use one?

How might I return a reversed adaptor wrapping a container?

I have a class which has-a deque which it uses as a stack (could be a vector, just happened to choose deque).
At any rate, I wish to allow consumers to iterate over the contents of the stack (something that std::stack cannot do). I am currently using push_back() to push items onto the stack, and hence if one iterates over the contents in forward ordering, one goes from bottom to the top of the stack.
I would prefer to expose things in reverse order, so that iterating over the stack using for (auto e: thestack) works in top-down fashion.
I found C++11 reverse range-based for-loop which shows a couple of solutions for reversing the iteration ordering for the new for-loop syntax (as well as range-based algorithms).
What is unclear to me is how to provide a simple mechanic for giving my users access to this automatically reversed deque.
e.g. without any real effort, I can simply allow const& access to the underlying deque:
const std::deque<T> & GetStack() const { return m_stack; }
and consumers could be responsible for the reversing:
for (auto e : reverse(m_toolstack.GetStack()))
Here, I am attempting to use the following solution for reverse:
template<class Fwd>
struct reverser_generic
{
Fwd &fwd;
reverser_generic(Fwd& fwd_): fwd(fwd_) {}
typedef std::reverse_iterator<typename Fwd::iterator> reverse_iterator;
reverse_iterator begin() { return reverse_iterator(std::end(fwd)); }
reverse_iterator end() { return reverse_iterator(std::begin(fwd)); }
};
template<class Fwd>
struct reverser_special
{
Fwd &fwd;
reverser_special(Fwd& fwd) : fwd(fwd) { }
auto begin() -> decltype(fwd.rbegin()) { return fwd.rbegin(); }
auto end() -> decltype(fwd.rend()) { return fwd.rend(); }
};
template<class Fwd>
auto reverse_impl(Fwd& fwd, long) -> decltype(reverser_generic<Fwd>(fwd))
{
return reverser_generic<Fwd>(fwd);
}
template<class Fwd>
auto reverse_impl(Fwd& fwd, int) -> decltype(fwd.rbegin(), reverser_special<Fwd>(fwd))
{
return reverser_special<Fwd>(fwd);
}
template<class Fwd>
auto reverse(Fwd&& fwd) -> decltype(reverse_impl(fwd,int(0)))
{
static_assert(!(std::is_rvalue_reference<Fwd&&>::value), "Cannot pass an rvalue reference to reverse()");
return reverse_impl(fwd,int(0));
}
The above all compiles and runs correctly using VS2012 (thanks to Jive Dadson et. al. for that solution).
However, in my stack facade, I really want to simply always return the reverse of the underlying container, but I am unclear on how to do so in a sensible fashion:
auto GetContents() const -> decltype(reverse(m_stack)) { return reverse(m_stack); }
The above errors, indicating that m_stack is unknown. this->m_stack is equally unknown.
How might I go about returning the "whatever it is that is the reverse of my member m_stack"?
Note, once that is answered, I also need "How do I return the const & of whatever is the decltype of reverse(m_stack)?
Is the declaration of GetContents before the declaration of m_stack?
Names in a late-specified return type must obey the same rules as names in the rest of the function signature, i.e. they cannot refer to class members that haven't been declared yet:
struct X
{
auto f() -> decltype(m_x) { return m_x; } // ERROR
int m_x;
auto g() -> decltype(m_x) { return m_x; } // OK
};
To make it work, either use std::declval to refer to the type (which means you must update the signature of GetContents if you change the type of m_stack from std::deque to std::vector) or ensure m_stack has been declared before you refer to it.

Check if container of shared_ptr contains a pointer?

Using Observer pattern. I have a class, called Monitor for example, that is monitoring a collection of objects. The class is an Observer and each object in it's collection is a Subject. Currently the collection is implemented as a std::list of shared_ptr. In the Update method of the Monitor class I want to check if the update is coming from one of the objects in it's collection.
std::list<SomeSharedPointer> items_;
...
void Monitor::Update(Subject *subject)
{
if(subject == something_)
{
DoSomething();
}
else if
??
// if subject is one of the objects in our collection then do something..
}
Subject here is a raw pointer and my collection is a list of shared_ptr. How can I effectively check if the subject coming in is any one of the objects in my collection?
(Note my compiler, msvc, supports lambdas if there is an algorithmic solution requiring one)
UPDATE
I should add that I realize I can use a for loop over the container, but I'm wondering if there's a snazzier way.
UPDATE 2
SomeSharedPointer is a typedef for std::shared_ptr<SomeType> where SomeType derives from abstract class Subject (standard Observer pattern implementation). SomeType will at some point call Notify() which will call the Update() method for each observer.
auto i = std::find_if(items_.begin(), items_.end(),
[=](const SomeSharedPointer& x) { return x.get() == subject; });
if (i != c.end())
{
// Object found, and i is an iterator pointing to it
}
A little helper method can make this more readable:
typedef std::list<SomeSharedPtr> ObserverCollection;
// You can also add a const version if needed
ObserverCollection::iterator find_observer(Subject* s)
{
return std::find_if(items_.begin(), items_.end(),
[=](const SomeSharedPointer& x) { return x.get() == s; });
}
Then, you use it like this if you need the iterator
auto i = find_observer(subject);
if (i != items_.end())
{
// Object found
}
or simply like this if you don't:
if (find_observer(subject) != items_.end())
{
...
}
If you don't have C++11 support for auto, declare the iterator the old fashioned way
for (auto iter = items_.begin(); iter != items_.end(); ++iter)
{
if (subject == iter->get())
{
.. do stuff ..
}
}
Shared pointer has a .get() function that returns the pointer.
Since you said that the observer needs to make a decision based on a state of items it is monitoring, then you should add a method to the base class (Subject in your question) which returns an enum defining the item's state. Then based on the state, add a switch in the update method:
enum State{ STATE_1, STATE_2 };
void Monitor::Update(Subject *subject)
{
switch( subject->getState() )
{
case STATE_1:
// do something 1
break;
case STATE_2:
// do something 2
break;
default:
//error
}
}
If possible, you could consider changing your container to something that gives you better search behavior. For instance, you could use a std::set. It costs more per insertion, but faster per lookup. Or, std::unordered_set. Both insertion and lookup are fast, but iteration is likely slower. To achieve proper comparison, you can create a helper class to enable converting your raw pointer into a shared pointer that has an no-op deleter.
template <typename T>
struct unshared_ptr {
std::shared_ptr<T> p_;
unshared_ptr (T *p) : p_(p, [](...){}) {}
operator const std::shared_ptr<T> & () const { return p_; }
operator T * () const { return p_.get(); }
};
If your container supported the find method, then:
typedef unshared_ptr<SomeType> unshared_some;
if (items_.end() != items_.find(unshared_some(subject))) {
DoSomething();
}
Try it online!
If you are sticking with std::list, you can abuse the remove_if method by passing in a predicate that always returns false, but performs your matching test.
bool matched = false;
auto pred = [subject, &matched](SomeSharedPtr &v) -> bool {
if (!matched && v.get() == subject) {
matched = true;
}
return false;
};
items_.remove_if(pred);
if (matched) {
DoSomething();
} //...