Hello, I have an exercise from my text that defines a StrBlob class, then a StrBlobPtr class to hold weak pointers to the StrBlobs. This is from C++ Primer (5th Edition) and coincidentally, the entire chapter is available on-line at here.
My problem is that the begin and end functions of StrBlob can't be defined until the entire StrBlobPtr class is defined. Forward declarations don't cut it, since begin and end need more than pointers.
The solution (if you also look at the errata for the book) seems to be to define StrBlob, leave begin and end undefined, then full define StrBlobPtr, and following that, finally define StrBlob::begin() and StrBlob::end().
Anyhow, the above works, as I show in the included code below - but it seems like a hack and messy. What would be the proper way to do this? My text may be obfuscating the issue in the pursuit of pedagogy.
Additionally, how would one separate StrBlob and StrBlobPtr into there own headers? I'd think it impossible, since the StrBlob would have to nestle an "#include "StrBlobPtr.hpp" in the center of it's own definition...?
Code:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
class StrBlobPtr; // Forward declaration
class StrBlob {
public:
friend class StrBlobPtr;
typedef std::vector<std::string>::size_type size_type;
StrBlob();
explicit StrBlob(std::initializer_list<std::string> il);
// StrBlob(std::initializer_list<std::string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
// add and remove elements
void push_back(const std::string& s) { data->push_back(s); }
void pop_back();
// element access
std::string& front();
std::string& back();
std::string& front() const;
std::string& back() const;
StrBlobPtr begin();
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string>> data;
// throw -msg- if data[i] isn't valid
void check(size_type i, const std::string& msg) const;
};
StrBlob::StrBlob()
: data(std::make_shared<std::vector<std::string>>()) {}
StrBlob::StrBlob(std::initializer_list<std::string> il)
: data(std::make_shared<std::vector<std::string>>(il)) {}
void StrBlob::check(size_type i, const std::string& msg) const {
if (i >= data->size())
throw std::out_of_range(msg);
}
std::string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
std::string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
std::string& StrBlob::front() const {
check(0, "front on empty StrBlob");
return data->front();
}
std::string& StrBlob::back() const {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
// StrBlobPtr throw an exception on attempts to access a nonexistent element
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) {}
StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) {}
std::string& deref() const;
StrBlobPtr& incr(); // prefix version
private:
// check returns a shared_ptr to the vector if the check succeeds
std::shared_ptr<std::vector<std::string>>
check(std::size_t, const std::string&) const;
// store a weak_ptr, which means the underlying vector may be destroyed
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr; // current position within the array
};
std::string& StrBlobPtr::deref() const {
auto p = check(curr, "dereference past end of StrBlobPtr");
return (*p) [curr]; // (*p) is the vector to which this object points
}
StrBlobPtr& StrBlobPtr::incr() {
// if curr already points to the end of the container, we can't increment it
check(curr, "increment past end of StrBlobPtr");
++curr; // advance the current state
return *this;
}
std::shared_ptr<std::vector<std::string>> StrBlobPtr::
check(std::size_t i, const std::string& msg) const {
auto ret = wptr.lock(); // is the vector still around?
if(!ret) { // if not, throw
throw std::runtime_error("Unbound StrBlobPtr");
}
if (i >= ret->size()) { // if it is, is i in range?
throw std::out_of_range(msg); // if not throw
}
return ret; // return shared_ptr to the vector
}
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
int main() {
return 0;
}
As I said, the above works (compiles, haven't -tested- it extensively) but it seems messy. Any comments, suggestions or concerns are welcome.