Search Tree - Iterator construction and non-pointer error - c++

I'm trying to construct an iterator for a given tree, however I'm getting
error: base operand of '->' has non-pointer type 'SearchTree::Iterator'
I believe its due to the way i've tried to construct x but I'm not entirely sure what the problem is there.
All the headers and cpp files are from the book so there's nothing there that's incorrect. Its just given what they've given me, I can't figure out a way to construct this iterator.
Main.cpp
#include <iostream>
#include <string>
#include <vector>
#include "AVLTree.h"
#include "RandInt.h"
#include "SearchTree.h"
using std::cout;
using std::endl;
using std::string;
using std::to_string;
int main(int argc, const char* argv[]) {
// Set up random sequence generator
constexpr int maxv = 3249398;
RandInt rand {RandInt::getSeed(argc, argv), maxv};
AVLTree t{};
SearchTree st {};
AVLTree avl {};
// condition for duplicates
for (int i = 0; i < 5000 ; i++)
{
int random_value = rand();
string stringy = to_string(random_value);
if (t.find(k)==t.end())
{
t.insert (random_value, stringy);
}
else {}
}
SearchTree::Iterator x = SearchTree::Iterator(t.begin());
while (x != t.end()){
int k = x->key();
string str = x->value();
st.insert(k,str);
avl.insert(k,str);
++x;
}
return 0;
}
SearchTree.h
#ifndef __Binary_Search_Trees__SearchTree__
#define __Binary_Search_Trees__SearchTree__
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include "AVLEntry.h"
#include "BinaryTree.h"
#include "Entry.h"
#include "Exception.h"
using std::string;
using ET = AVLEntry;
class SearchTree {
public:
using K = ET::Key;
using V = ET::Value;
class Iterator;
protected:
using BinTree = BinaryTree<ET>;
using TPos = BinTree::Position;
protected:
BinTree T;
int n;
long comparisons;
public:
SearchTree();
SearchTree(const SearchTree& st) = delete;
SearchTree& operator=(const SearchTree& st) = delete;
int size() const { return n; }
bool empty() const { return T.root().left().isExternal(); }
Iterator find(const K& k);
Iterator insert(const K& k, const V& x);
void restructure(TPos& x);
void erase(const K& k);
void erase(const Iterator& p);
Iterator begin() const;
Iterator end() const;
long getComparisons() const { return comparisons; }
void clearComparisons() { comparisons = 0L; }
void debugPrintTree(std::ostream& os, const string& name) const;
protected:
TPos root() const;
std::pair<TPos, int> finder(const K& k, TPos v);
TPos inserter(const K& k, const V& x);
TPos eraser(TPos& v);
constexpr static int indent {4};
void debugPrintTree(std::ostream& os, const TPos& subRoot, int depth) const;
void printIndented(std::ostream& os, const char* st, int depth) const;
void printIndented(std::ostream& os, const ET& val, int depth) const;
public:
class Iterator {
private:
TPos v;
public:
Iterator(const TPos& vv) : v(vv) {}
const ET& operator*() const { return *v; }
ET& operator*() { return *v; }
TPos pos() const { return v; }
bool operator==(const Iterator& p) const { return v == p.v; }
bool operator!=(const Iterator& p) const { return ! (*this == p); }
Iterator& operator ++();
friend class SearchTree;
};
};
#endif /* defined(__Binary_Search_Trees__SearchTree__) */
entry.h
#ifndef Binary_Search_Trees_Entry_h
#define Binary_Search_Trees_Entry_h
#include <iostream>
template <typename K, typename V>
class Entry {
public:
using Key = K;
using Value = V;
public:
Entry(const K& k = K(), const V& v = V()) : _key(k), _value(v) {}
const K& key() const { return _key; }
const V& value() const { return _value; }
void setKey(const K& k) { _key = k; }
void setValue(const V& v) { _value = v; }
private:
K _key;
V _value;
};
template<typename K, typename V>
std::ostream& operator<<(std::ostream& os, const Entry<K, V>& e)
{
return os << '(' << e.key() << ", " << e.value() << ')';
}
#endif

Related

operator[] for getter and setter of vector templated class

I would like to write some getters and setters for a vector inside a class. But I am currently stuck.
I have the following:
#include <iostream>
#include <vector>
template<class vecT>
class A
{
private:
vecT data_;
public:
A(int size, double value): data_(size, value){}
typename vecT::value_type& operator[](int pos); // seter
const typename vecT::value_type operator[](int pos) const; // getter
};
template<class vecT>
typename vecT::value_type& A<vecT>::operator[](int pos)
{
if (pos > data_.size())
{
throw std::runtime_error("problem...");
}
return data_[pos];
}
template<class vecT>
const typename vecT::value_type A<vecT>::operator[](int pos) const
{
if (pos > data_.size())
{
throw std::runtime_error("problem...");
}
return data_[pos];
}
int main()
{
A<std::vector<double>> obj (5,3);
obj[2] = 5;
const double a = obj[1];
return 0;
}
Is the definition of getter and setter Ok? Can both be defined to return a value as a reference?
Best Regards

2d vector modify with iterator

I have a 2d matrix using vector library. And I wanted to iterate over the Matrix more conveniently, so I created an MatrixIterator class.
Matrix.cpp
#include <vector>
template <class T>
class MatrixIterator;
template <class T>
class Matrix
{
friend class MatrixIterator<T>;
private:
public:
std::vector<std::vector<T>> m;
unsigned rows_;
unsigned cols_;
Matrix<T>(unsigned rows, unsigned cols);
MatrixIterator<T> iterator() const
{
return {*this};
}
MatrixIterator<T> begin() const
{
return {*this};
}
MatrixIterator<T> end() const
{
return {*this, rows_, 0};
}
}
template <class T>
class MatrixIterator
{
private:
Matrix<T> matrix_;
unsigned row_;
unsigned col_;
public:
MatrixIterator<T>(Matrix<T> m) : matrix_(m), row_(0), col_(0) {};
MatrixIterator<T>(Matrix<T> m, unsigned row, unsigned col) : matrix_(m), row_(row), col_(col) {};
MatrixIterator<T> begin() const
{
return {matrix_};
}
MatrixIterator<T> end() const
{
return {matrix_, matrix_.rows_, 0};
}
void inc()
{
if(++col_ >= matrix_.cols_)
{
row_++;
col_ = 0;
}
}
MatrixIterator<T>& operator++()
{
inc();
return *this;
}
MatrixIterator<T> operator++(int)
{
inc();
return *this;
}
bool operator!=(const MatrixIterator<T> &rhs) const
{
return (row_ != rhs.row_) || (col_ != rhs.col_);
}
T& operator*()
{
return matrix_.m[row_][col_];
}
};
template <class T>
Matrix<T>::Matrix(unsigned rows, unsigned cols)
: rows_(rows), cols_(cols)
{
m.resize(cols);
for (unsigned i = 0; i < cols; i++)
{
m[i].resize(rows);
fill(m[i].begin(), m[i].end(), T());
}
}
In the following code, when I try to manipulate value using iterator, it does not change the value.
I tried returning the values as pointers from operator* but it did not work either. I saw no errors. What is wrong and how can I solve this?
main.cpp
#include "Matrix.cpp"
#include<iostream>
int main()
{
Matrix<int> m = Matrix<int>{3,3};
for(auto x: m.iterator())
x = 10;
for(auto x: m.iterator())
std::cout << x << " ";
// outputs 0 0 0 ~
}
Compiled with g++ main.cpp -std=c++20 -g -o main && main
You need to store a reference in the iterator class, other than hold a copy of it (iterator is just a view of the data).
template <class T>
class MatrixIterator {
private:
Matrix<T>& matrix_;
unsigned row_;
unsigned col_;
public:
MatrixIterator<T>(Matrix<T>& m) : MatrixIterator<T>(m, 0, 0) {}
MatrixIterator<T>(Matrix<T>& m, unsigned row, unsigned col)
: matrix_(m), row_(row), col_(col) {}
};
And you also need to non-const begin and end for your matrix, nonconst version iterator can be used to change the underlying value. The function iterator() can be removed here, since it's not common to have this in c++ code.
MatrixIterator<T> begin() const { return {*this}; }
MatrixIterator<T> begin() { return {*this}; }
MatrixIterator<T> end() const { return {*this, rows_, 0}; }
MatrixIterator<T> end() { return {*this, rows_, 0}; }
To change to the value with an iterator, you need a reference other than changing the copied value in your main function. It's not necessary to explicitly call the iterator here, the compiler will do it for you.
int main() {
Matrix<int> m = Matrix<int>{3, 3};
for (auto& x : m) x = 10;
for (auto x : m) std::cout << x << " ";
return 0;
}
Online demo.
You are iterating over values, not references when attempting to change the matrix values. Instead, try
for (auto& x : m.iterator())

How to implement Boost::Serialize for Boost::Nested_Container

(Followup of another question.)
Boost::Serialize often delivers an exception on oarchive, complaining that re-creating a particular object would result in duplicate objects. Some archives save and re-load successfully, but many result in the error above. I have not been able yet to determine the exact conditions under which the error occurs, but I have proven that none of the content used to populate the nested_container and the flat object list contains duplicate object IDs. I am using text archive, not binary. Here is how I have modified the code for nested_container and also for another, separate flat object list in order to do Boost::Serialize:
struct obj
{
int id;
const obj * parent = nullptr;
obj()
:id(-1)
{ }
obj(int object)
:id(object)
{ }
int getObjId() const
{
return id;
}
bool operator==(obj obj2)
{
if (this->getObjId() == obj2.getObjId())
return true;
else
return false;
}
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const obj &obj);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & id & parent;
}
#endif
};
struct subtree_obj
{
const obj & obj_;
subtree_obj(const obj & ob)
:obj_(ob)
{ }
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const subtree_obj &obj);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & obj_;
}
#endif
};
struct path
{
int id;
const path *next = nullptr;
path(int ID, const path *nex)
:id(ID), next(nex)
{ }
path(int ID)
:id(ID)
{ }
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const path &pathe);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & id & next;
}
#endif
};
struct subtree_path
{
const path & path_;
subtree_path(const path & path)
:path_(path)
{ }
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const subtree_path &pathe);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & path_;
}
#endif
};
//
// My flattened object list
//
struct HMIObj
{
int objId;
std::string objType;
HMIObj()
:objId(-1), objType("")
{ }
bool operator==(HMIObj obj2)
{
if (this->getObjId() == obj2.getObjId())
&& this->getObjType() == obj2.getObjType())
return true;
else
return false;
}
int getObjId() const
{
return objId;
}
std::string getObjType() const
{
return objType;
}
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const HMIObj &obj);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & objId & objType;
}
#endif
};
The problem you're experiencing is most likely due to, again, the particular order in which elements are traversed in index #0 (the hashed one). For instance, if we populate the container like this:
nested_container c;
c.insert({54});
auto it=c.insert({0}).first;
insert_under(c,it,{1});
Then the elements are listed in index #0 as (1, 54, 0). The crucial problem here is that 1 is a child of 0: when loading elements in the same order as they were saved, the first one is then 1, but this needs 0 to be loaded before in order to properly point to it. This is what Boost.Serialization very smartly detects and complains about. Such child-before-parent situations depend on the very unpredictable way elements are sorted in a hashed index, which is why you see the problem just sometimes.
You have two simple solutions:
Swap indices #0 and #1 in the definition of your nested container: as index #1 sorting order is the tree preorder, it is guaranteed that parents get processed before their children.
Override the serialization code of the nested container so as to go through index #1:
template<class Archive>
void serialize(Archive& ar,nested_container& c,unsigned int)
{
if constexpr(Archive::is_saving::value){
boost::serialization::stl::save_collection(ar,c.get<1>());
}
else{
boost::serialization::load_set_collection(ar,c.get<1>());
}
}
Complete demo code for solution #2 follows:
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <iterator>
struct obj
{
int id;
const obj* parent=nullptr;
};
namespace boost{
namespace serialization{
template<class Archive>
void serialize(Archive& ar,obj& x,unsigned int)
{
ar&x.id&x.parent;
}
}} /* namespace boost::serialization */
struct subtree_obj
{
const obj& obj_;
};
struct path
{
int id;
const path* next=nullptr;
};
struct subtree_path
{
const path& path_;
};
inline bool operator<(const path& x,const path& y)
{
if(x.id<y.id)return true;
else if(y.id<x.id)return false;
else if(!x.next) return y.next;
else if(!y.next) return false;
else return *(x.next)<*(y.next);
}
inline bool operator<(const subtree_path& sx,const path& y)
{
const path& x=sx.path_;
if(x.id<y.id)return true;
else if(y.id<x.id)return false;
else if(!x.next) return false;
else if(!y.next) return false;
else return subtree_path{*(x.next)}<*(y.next);
}
inline bool operator<(const path& x,const subtree_path& sy)
{
return x<sy.path_;
}
struct obj_less
{
private:
template<typename F>
static auto apply_to_path(const obj& x,F f)
{
return apply_to_path(x.parent,path{x.id},f);
}
template<typename F>
static auto apply_to_path(const obj* px,const path& x,F f)
->decltype(f(x))
{
return !px?f(x):apply_to_path(px->parent,{px->id,&x},f);
}
public:
bool operator()(const obj& x,const obj& y)const
{
return apply_to_path(x,[&](const path& x){
return apply_to_path(y,[&](const path& y){
return x<y;
});
});
}
bool operator()(const subtree_obj& x,const obj& y)const
{
return apply_to_path(x.obj_,[&](const path& x){
return apply_to_path(y,[&](const path& y){
return subtree_path{x}<y;
});
});
}
bool operator()(const obj& x,const subtree_obj& y)const
{
return apply_to_path(x,[&](const path& x){
return apply_to_path(y.obj_,[&](const path& y){
return x<subtree_path{y};
});
});
}
};
using namespace boost::multi_index;
using nested_container=multi_index_container<
obj,
indexed_by<
hashed_unique<member<obj,int,&obj::id>>,
ordered_unique<identity<obj>,obj_less>
>
>;
#if 1 /* set to 0 to trigger pointer conflict exception */
#include <boost/serialization/set.hpp>
namespace boost{
namespace serialization{
template<class Archive>
void serialize(Archive& ar,nested_container& c,unsigned int)
{
if constexpr(Archive::is_saving::value){
boost::serialization::stl::save_collection(ar,c.get<1>());
}
else{
boost::serialization::load_set_collection(ar,c.get<1>());
}
}
}} /* namespace boost::serialization */
#endif
template<typename Iterator>
inline auto insert_under(nested_container& c,Iterator it,obj x)
{
x.parent=&*it;
return c.insert(std::move(x));
}
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <sstream>
void print(const nested_container& c)
{
for(const obj& x:c){
std::cout<<"("<<x.id;
if(x.parent)std::cout<<"->"<<x.parent->id;
std::cout<<")";
}
std::cout<<"\n";
}
int main()
{
nested_container c;
c.insert({54});
auto it=c.insert({0}).first;
insert_under(c,it,{1});
print(c);
std::ostringstream oss;
boost::archive::text_oarchive oa(oss);
oa<<c;
nested_container c2;
std::istringstream iss(oss.str());
boost::archive::text_iarchive ia(iss);
ia>>c2;
print(c2);
}
By the way, why are you providing serialize functions for subtree_obj, path and subtree_path? You don't need that to serialize nested_containers.

Find elements of std::set by custom comparison with value of different type

Consider the following toy example of an std::set with a custom comparator:
#include <set>
struct A {
A() : a(cnt++) {}
const int a;
static int cnt;
};
int A::cnt = 0;
struct comp {
bool operator()(const A& left, const A& right)
{
return left.a < right.a;
}
};
int main()
{
std::set<A, comp> sa;
for (int i = 0; i < 10; ++i) sa.insert(A());
return 0;
}
Note that A cannot simply be created from an integer.
I would like to look for an A with a given value of A::a in sa, without constructing a temporary object of type A, i.e. I am searching for something like
sa.find(4)
with a custom comparator that allows for direct comparison of integers with objects of type A. Is that possible?
With C++14 you can utilize "transparent" comparator:
#include <iostream>
#include <set>
#include <type_traits>
class A
{
public: explicit A() : a{cnt++} {}
private: explicit A(int) = delete;
public: const int a;
private: static int cnt;
};
int A::cnt{};
class Comparator
{
// this member is required to let container be aware that
// comparator is capable of dealing with types other than key
public: using is_transparent = std::true_type;
public: bool operator()(const int & left, const A& right) const
{
return left < right.a;
}
public: bool operator()(const A & left, const int& right) const
{
return left.a < right;
}
public: bool operator()(const A& left, const A& right) const
{
return left.a < right.a;
}
};
int main()
{
std::set<A, Comparator> sa{};
for (int i{}; i < 10; ++i)
{
sa.emplace();
}
std::cout << sa.find(3)->a << std::endl;
return 0;
}
online compiler
Before C++14 heterogenous lookup was available in ::boost::intrusive::set:
#include <boost/intrusive/set.hpp>
#include <iostream>
namespace bi = ::boost::intrusive;
// hook contains set node data, supports various options, can be a member
class A: public bi::set_base_hook
<
bi::link_mode<bi::link_mode_type::safe_link>
>
{
public: explicit A() : a{cnt++} {}
private: explicit A(int) = delete;
public: const int a;
private: static int cnt;
};
int A::cnt{};
class Comparator
{
public: bool operator()(const int & left, const A& right) const
{
return left < right.a;
}
public: bool operator()(const A & left, const int& right) const
{
return left.a < right;
}
public: bool operator()(const A& left, const A& right) const
{
return left.a < right.a;
}
};
int main()
{
bi::set<A, bi::compare<Comparator>> sa{Comparator{}};
for (int i{0}; i < 10; ++i)
{
sa.insert(*new A{}); // typically user manages object creation
}
// comparators may vary
std::cout << sa.find(3, Comparator{})->a << std::endl;
return 0;
}
online compiler

Range based for loop index

A custom iterator on a std::vector returns an object of the following class:
class chunk {
public:
int value_;
size_t fake_index_;
};
The iterator updates both the fake_index_ and value_.
After that I create a range class with the iterator. Then in a range base for loop over the range class, I only use the value_, but not fake_index_.
Does that means the compiler will optimize out the iterator's update of fake_index_ ?
Test Code (with all the iterator stuff...)
#include <vector>
#include <iterator>
class Iterator;
class Chunk {
public:
int value_;
int base_bit_index_;
int real_index_;
std::vector<int>& data_;
Chunk (std::vector<int>& data, size_t real_index)
: data_{data}
{
base_bit_index_ = real_index*sizeof(int);
real_index_ = real_index;
value_ = data[real_index_];
}
int value() { return value_; }
private:
void increment_index() {
++real_index_;
base_bit_index_ += sizeof(int);
value_ = data_[real_index_];
}
friend class Iterator;
};
class Iterator : public std::iterator<std::input_iterator_tag, int>
{
private:
Chunk chunk_;
public:
Iterator(Chunk chunk) :chunk_(chunk) {}
Iterator(const Iterator& mit) : chunk_(mit.chunk_) {}
Iterator& operator++() {
chunk_.increment_index();
return *this;
}
Iterator operator++(int) {Iterator tmp(*this); operator++(); return tmp;}
bool operator==(const Iterator& rhs)
{
return chunk_.real_index_== rhs.chunk_.real_index_;
}
bool operator!=(const Iterator& rhs)
{
return !(operator==(rhs));
}
Chunk& operator*() {return chunk_;}
};
class Range
{
private:
std::vector<int>& data_;
public:
Range(std::vector<int>& data)
: data_{data}
{
}
Iterator begin()
{
Chunk chunk(data_, 0);
Iterator tmp(chunk);
return tmp;
}
Iterator end()
{
Chunk chunk(data_, data_.size());
Iterator tmp(chunk);
return tmp;
}
};
int main()
{
std::vector<int> v1;
v1.resize(100);
std::vector<int> v2;
Range X(v1);
for (auto chunk : X) {
v2.push_back(chunk.real_index_);
}
}
gcc godbolt
Test Code (without all the iterator stuff)
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v2;
for (size_t i = 0; i < 100; ++i) {
v2.push_back(i);
// std::cout<<i<<"\n";
}
}
gcc godbolt
My first impression: Looks like the iterator sucks horribly anyway.