Usecase for weak_ptr in C++ primer 5th Edition - c++

Hi i am reading C++ primer 5th addition and have some doubts in the section of weak_ptr. It is written that
By using a weak_ptr, we don’t affect the lifetime of the vector to which a given StrBlob points. However, we can prevent the user from attempting to access a vector that no longer exists.
Then they have given the following code as an example:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<initializer_list>
using namespace std;
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob():data(std::make_shared<std::vector<std::string>>()){
}
StrBlob(std::initializer_list<std::string> il):data(make_shared<vector<std::string>>(il)){
}
size_type size() const {
return data->size();
}
bool empty() const {
return data->empty();
}
void push_back(const std::string &t){
data->push_back(t);
}
std::string& front(){
check(0,"front on empty StrBlob");
return data->front();
}
std::string& front() const{
check(0,"front on const empty StrBlob");
return data->front();
}
std::string& back(){
check(0,"back on empty StrBlob");
return data->back();
}
std::string& back() const {
check(0,"back on const empty StrBlob");
return data->back();
}
void pop_back(){
check(0,"pop_back on empty StrBlob");
data->pop_back();
}
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string &msg) const{
if(i >= data->size()){
throw out_of_range(msg);
}
}
StrBlobPtr begin();
StrBlobPtr end();
};
class StrBlobPtr {
public:
typedef std::vector<std::string>::size_type size_type;
StrBlobPtr():curr(0){
}
StrBlobPtr(StrBlob &a, size_type sz = 0):wptr(a.data), curr(sz){
}
std::string& deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& incr(){
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
std::shared_ptr<std::vector<std::string>> check(std::size_t i, const std::string &msg) const{
auto ret = wptr.lock();
if(!ret){
throw std::runtime_error("unbound StrBlobPtr");
}
if(i>= ret->size()){
throw std::out_of_range(msg);
}
return ret;
}
private:
std::weak_ptr<std::vector<std::string>> wptr;
size_type curr;
};
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
}
int main(){
return 0;
}
My questions are as follows:
How can we prevent the user from attempting to access a vector that no longer exists? I can't come up with a use case,how can we use the above quoted statement in this example?
How does this example shows/verifies that we can prevent the user from attempting to access a vector that no longer exists? *If this example does not shows what they have written then why is this example there in the book?*Note that i have written if.

1. How can we prevent the user from attempting to access a vector that no longer exists?
We can prevent it by exchanging a weak_ptr for a shared_ptr. weak_ptr::lock() does that. It atomically checks if the pointed-to object still exists and increments the corresponding shared_ptr ref count, thus "blocking" any possible deletion from that point on.
So after this line:
auto ret = wptr.lock();
ret will be a shared_ptr that either owns the object or doesn't, and that fact will not change for as long as ret exists.
Then with a simple test you can safely check if there is an object or not:
if(!ret){
/* no object anymore */
}
At the end the function does return ret;, which returns a copy of it, thus still preventing an object from being deleted (ref count is again incremented and then decremented). So as long as you own an instance of shared_ptr, you can rest assured the object will continue to exist.
However, here we have a problem:
std::string& deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
This returns a reference to std::string inside a vector which, after p goes out of scope is held only by weak_ptr, i.e. a potentially dangling reference (which is no different from a dangling pointer).
2. How does this example shows/verifies that we can prevent the user from attempting to access a vector that no longer exists?
Apparently it doesn't. Just ignore it.

Related

C++ - Overloading of operators needed for an iterator

I'm trying to create an iterator on a library that allows reading a specific file format.
From the docs, to read the file content you need do something like this:
CKMCFile database;
if (!database.OpenForListing(path)) {
std::cerr << "ERROR: unable to open " << path << std::endl;
}
CKMCFileInfo info;
database.Info(info);
CKmerAPI kmer(info.kmer_length);
uint32 cnt;
std::vector<uint64_t> data;
std::vector<uint64> ulong_kmer;
data.reserve(info.total_kmers);
while (database.ReadNextKmer(kmer, cnt)) {
kmer.to_long(ulong_kmer);
data.push_back(ulong_kmer[0]);
}
Now, I started with this class wrapper:
class FileWrapper {
CKMCFile database;
CKMCFileInfo info;
Iterator _end;
public:
explicit FileWrapper(const std::string &path) {
if (!database.OpenForListing(path)) {
std::cout << "ERROR: unable to open " << path << std::endl;
}
database.Info(info);
}
Iterator begin() {
Iterator it;
it.database = &database;
it.total = 0;
uint32_t cnt;
std::vector<uint64_t> ulong_kmer;
CKmerAPI tmp(info.kmer_length);
database.ReadNextKmer(tmp, cnt);
tmp.to_long(ulong_kmer);
return it;
}
Iterator end() const { return _end; }
uint64_t size() { return info.total_kmers; }
};
And then, this is the Iterator class:
class Iterator {
friend class FileWrapper;
CKMCFileInfo info;
CKMCFile *database;
uint64_t kmer, total;
public:
Iterator &operator++() {
++total;
uint32_t cnt;
std::vector<uint64_t> ulong_kmer;
CKmerAPI tmp(info.kmer_length);
database->ReadNextKmer(tmp, cnt);
tmp.to_long(ulong_kmer);
return *this;
}
bool operator<(const Iterator &rhs) const { return total < rhs.total; }
uint64_t operator*() const { return kmer; }
};
But, during some test I can't use into a for loop for something like for (auto it = begin(); it != end(); ++i) { ... } or begin() + size(). How can I overload correctly this two operatos? opeartor!= and operato+
You'll have to think about 2 major things before:
Ownership. Currently, you have to make sure your FileWrapper survives at least as long as any Iterator returned from it by calling its begin() (since your Iterators store pointers to data owned by the FileWrapper object). If you cannot guarantee that, maybe think about using unique_ptrs or shared_ptrs
Iterator Category. As discussed in the comments, it appears that your database requires you to use "input iterators". They can only be incremented by one (do not provide operator+(int)) and dereferenced. Indeed, what would the iterator begin() + 10 look like? If this should advance your file-pointer, then you cannot define the end as begin() + size() as that would just skip through the file.
Representation. What should an end-iterator look like? A simple choice might be to indicate the end with database == nullptr. In this case, an operator!= might look like this:
bool is_end() const { return database == nullptr; }
bool operator==(const Iterator& other) const {
if(is_end()) return other.is_end();
if(other.is_end()) return false;
return (database == other.database) && (total == other.total);
}
bool operator!=(const Iterator& other) const { return !operator==(other); }
Now, you'll need code that ensures that all end-iterators have database == nullptr and, whenever a non-end iterator becomes and end-iterator by application of operator++(), you'll need to set database = nullptr and total = 0 (or something).
A note at the end: your Iterators may be in an inconsistent state after construction and before assignment of their database member. It is prudent to declare a proper constructor for Iterator that initializes its members.
EDIT: here's a suggestion for an integration

C++ cannot convert Student* to DYNVECTOR<Student,24>::Node* in initialization

Im trying to create an iterator for my dynamic vector class that i've implemented,
The thing is I keep getting this error when I try to initilaize some student class for testing
purposes on some assignment for the iterator, I cannot figure out why Im having this error wasted alot of hours on it and its impossible to figure out why this error occurs..
Here is the code
some edit
Here is the main function that im trying to use that takes the iterator from my class
some edit
Just in the intiliaze of the DYNVECTOR class in the main my code fails , I keep on getting
the error:
error: cannot convert 'Student*' to 'DYNVECTOR <Student, 24>::Node*' in initialization
Iter(T *N ) : _pointer(N) { }
EDIT: please guys focus on this part:
inputIterator begin() { return inputIterator(pa);}
this is what is causing the error, the functionality of the push back function and other funtions are still in progress but that is not the reason causing this error.
The Problem
inputIterator begin() { return inputIterator(pa);}
is calling inputIterator's constructor
Iter(T *N ) : _pointer(N) { }
with a pointer to a T, a T *. Iter takes T * happily, but it tries to store that T * into _pointer and _pointer is defined as
Node *_pointer;
which is NOT a T *. The assignment fails because the types don't match.
The Naive Solution
Make the types match. This means you have to pass in a Node *. Bad news: DYNARRAY doesn't have any Node *s to give it. Naive solution fails.
The Proper Solution
Throw out Node. Node is useful if you have a linked list. You don't have a linked list. Kill it. Make it dead. Clean up the mess.
class DYNVECTOR
{
// no nodes
class Iter // directly uses T pointers
{
public:
Iter(T *N) :
_pointer(N) // types match now
{
}
T& operator*() const
{
return *_pointer; // simpler without Node, no?
}
T* operator->() const
{
return _pointer; // simple
}
Iter& operator++()
{
_pointer++; // dead simple
return *this;
}
Iter operator++(int)
{
Iter tmp = *this;
_pointer++; // yawn-city
return tmp;
}
bool operator==(Iter const &rhs) const
{
return _pointer == rhs._pointer; // unchanged
}
bool operator!=(Iter const &rhs) const
{
return _pointer != rhs._pointer; // unchanged
}
private:
T *_pointer; // T *, not Node *
};
private:
size_t someCap, length; //, initCap; don't see the point of initCap
T *pa; // unchanged
public:
typedef Iter inputIterator;
DYNVECTOR():
someCap(Capacity), // Still not sure what Capacity is for, so I used
// it here instead of magic number 24
length(0),
pa(new T[someCap])
{
// used initializer list instead.
}
inputIterator begin()
{
return inputIterator(pa); // unchanged
}
inputIterator end()
{
return inputIterator(&pa[length]); // iterator to one past the end.
// just like std::vector
}
template<class Iter>
DYNVECTOR(const Iter &begin, const Iter &end): // far more versatile if const references
DYNVECTOR() // delegate basic set-up to default constructor
{
for (Iter pointer = begin; pointer != end; pointer++) // loop unchanged
{
push_back(*pointer);
}
}
// make uncopyable (for now anyway) See Rule of Three
// linked below for why
DYNVECTOR(const DYNVECTOR & ) = delete;
DYNVECTOR& operator=(const DYNVECTOR & ) = delete;
~DYNVECTOR() // for my own testing. left as example
{
delete[] pa; // clean up allocated storage
}
void push_back(const T & newb) // for my own testing. left as example
{
if (length == someCap) // need more space
{
int newCap = someCap * 2; // double the size
// you might need to do something different like
// int newCap = someCap + Capacity;
// There's no way for me to know.
// The rest should be right though.
T* newArr = new T[newCap]; // allocate bigger array
for (size_t index = 0; index < length; index++)
{ // copy old array into new
newArr[index] = pa[index];
}
delete[] pa; // discard old array
pa = newArr; // use new array
someCap = newCap; // update capacity
}
pa[length] = newb; // insert new item
length++; // update counter
}
};
Documentation on the Rule of Three and friends. You cannot write complex and efficient C++ unless you understand these rules. Learn them or consign yourself to being a hack.
First of all when you deal with objects it is bad practice to do:
DYNVECTOR<Student, 24> students;
It should be:
DYNVECTOR<Student*, 24> students;
Secondly you never created a constructor for your DYNVECTOR how do you expect the object to be created??

C++ custom lazy iterator

I have a somewhat simple text file parser. The text I parse is split into blocks denoted by { block data }.
My parser has a string read() function, which gets tokens back, such that in the example above the first token is { followed by block followed by data followed by }.
To make things less repetitive, I want to write a generator-like iterator that will allow me to write something similar to this JavaScript code:
* readBlock() {
this.read(); // {
let token = this.read();
while (token !== '}') {
yield token;
token = this.read();
}
}
which in turn allows me to use simple for-of syntax:
for (let token of parser.readBlock()) {
// block
// data
}
For C++ I would like something similar:
for (string token : reader.read_block())
{
// block
// data
}
I googled around to see if this can be done with an iterator, but I couldn't figure if I can have a lazy iterator like this which has no defined beginning or end. That is, its beginning is the current position of the reader (an integer offset into a vector of characters), and its end is when the token } is found.
I don't need to construct arbitrary iterators, or to iterate in reverse, or to see if two iterators are equal, since it's purely to make linear iteration less repetitive.
Currently every time I want to read a block, I need to re-write the following:
stream.skip(); // {
while ((token = stream.read()) != "}")
{
// block
// data
}
This becomes very messy, especially when I have blocks inside blocks. To support blocks inside blocks, the iterators would have to all reference the same reader's offset, such that an inner block will advance the offset, and the outer block will re-start iterating (after the inner is finished) from that advanced offset.
Is this possible to achieve in C++?
In order to be usable in a for-range loop, a class has to have member functions begin() and end() which return iterators.
What is an iterator? Any object fulfilling a set of requirements. There are several kind of iterators, depending on which operations allow you. I suggest to implement an input iterator, which is the simplest: https://en.cppreference.com/w/cpp/named_req/InputIterator
class Stream
{
public:
std::string read() { /**/ }
bool valid() const { /* return true while more tokens are available */ }
};
class FileParser
{
std::string current_;
Stream* stream_;
public:
class iterator
{
FileParser* obj_;
public:
using value_type = std::string;
using reference = const std::string&;
using pointer = const std::string*;
using iterator_category = std::input_iterator_tag;
iterator(FileParser* obj=nullptr): obj_ {obj} {}
reference operator*() const { return obj_->current_; }
iterator& operator++() { increment(); return *this; }
iterator operator++(int) { increment(); return *this; }
bool operator==(iterator rhs) const { return obj_ == rhs.obj_; }
bool operator!=(iterator rhs) const { return !(rhs==*this); }
protected:
void increment()
{
obj_->next();
if (!obj_->valid())
obj_ = nullptr;
}
};
FileParser(Stream& stream): stream_ {&stream} {};
iterator begin() { return iterator{this}; }
iterator end() { return iterator{}; }
void next() { current_ = stream_->read(); }
bool valid() const { return stream_->valid(); }
};
So your end-of-file iterator is represented by an iterator pointing to no object.
Then you can use it like this:
int main()
{
Stream s; // Initialize it as needed
FileParser parser {s};
for (const std::string& token: parser)
{
std::cout << token << std::endl;
}
}

std::map::emplace inserting duplicate of naked pointer

I have a code snippet that uses std::map::emplace to insert elements into a map. The key is an internal version of a unique_ptr.
The standard states:
The element may be constructed even if there already is an element with the key in the container, in which case the newly constructed element will be destroyed immediately.
In the following snippet, if the wrapper is constructed/destructed, it will correctly take destroy the naked pointer. If it is not, the object will be leaked. The snippet is just an example.
class Wrapper {
public:
Wrapper(int* param):
value(param) {}
~Wrapper() { delete value;}
bool operator<(const Wrapper& other) const { return *value < *other.value;}
private:
int * value;
};
int main()
{
std::map<Wrapper, int> map;
{
int *key = new int(100);
map.emplace(key, 5);
}
{
int *keyDuplicate = new int(100);
map.emplace(keyDuplicate, 10);
}
}
The handling seems to be implementation dependant. I could check the return value and destroy the object if it was a duplicate. But if the object was constructed and deleted, I would be deleting the pointer twice.

Preventing assignment by subscript operator by not returning references causes runtime crash

I'm doing this:
template<typename T> class var_accessor {
public:
std::set<std::shared_ptr<T>> varset;
std::map<std::string,std::shared_ptr<T>> vars_by_name;
std::map<uint32,std::shared_ptr<T>> vars_by_id;
std::shared_ptr<T> operator[](const uint32& index) { return vars_by_id[index]; }
std::shared_ptr<T> operator[](const std::string& index) { return vars_by_name[index]; }
bool is_in_set(std::shared_ptr<T> what) { auto it = varset.find(what); if (it == varset.end()) return false; return true; }
bool is_in_set(uint32 what) { auto it = vars_by_id.find(what); if (it == vars_by_id.end()) return false; return true; }
bool is_in_set(std::string& what) { auto it = vars_by_name.find(what); if (it == vars_by_name.end()) return false; return true; }
bool place(std::shared_ptr<T> what, const uint32 whatid, const std::string& whatstring) {
if (is_in_set(what)) return false;
varset.emplace(what);
vars_by_name.emplace(whatstring,what);
vars_by_id.emplace(whatid,what);
return true;
}
};
Then...
class whatever {
std::string name;
std::function<int32()> exec;
};
And:
class foo {
public:
var_accessor<whatever> stuff;
};
This works:
std::shared_ptr<whatever> thing(new whatever);
thing->name = "Anne";
thing->exec = []() { return 1; }
foo person;
person.stuff.emplace(thing, 1, thing->name);
Getting the name crashes it:
std::cout << person.stuff[1]->name;
But if I change the operator[]'s to return references, it works fine.
I don't want to be able to accidentally add new elements without adding to all 3 structures, so that's why I made
std::shared_ptr<T> operator[]
instead of
std::shared_ptr<T>& operator[]
Is there any way to prevent assignment by subscript but keep the subscript operator working?
To be clear I want to be able to keep doing
std::cout << person.stuff[4];
But NOT be able to do
std::shared_ptr<whatever> bob(new whatever);
bob->name = "bob";
person.stuff[2] = bob;
The error is a EXC_BAD_ACCESS inside the std::string class madness
Everything I read says simply "don't return references if you want to prevent assignment" but it also prevents using it for me.
Yes I know some things should be made private but I just want to get it working first.
Using Clang/LLVM in XCode 5.1
Thanks!
You should return a const reference. See this question
A const reference means the caller is not allowed to change the value, only look at it. So assignment will be a compile-time error. But using it will work (and be efficient).