Following the question in Heterogenous vectors of pointers. How to call functions.
I would like to know how to identify null points inside the vector of boost::variant.
Example code:
#include <boost/variant.hpp>
#include <vector>
template< typename T>
class A
{
public:
A(){}
~A(){}
void write();
private:
T data;
};
template< typename T>
void A<T>::write()
{
std::cout << data << std::endl;
}
class myVisitor
: public boost::static_visitor<>
{
public:
template< typename T>
void operator() (A<T>* a) const
{
a->write();
}
};
int main()
{
A<int> one;
A<double> two;
typedef boost::variant<A<int>*, A<double>* > registry;
std::vector<registry> v;
v.push_back(&one);
v.push_back(&two);
A<int>* tst = new A<int>;
for(auto x: v)
{
boost::apply_visitor(myVisitor(), x);
try {delete tst; tst = nullptr;}
catch (...){}
}
}
Since I am deleting the pointer I would hope that the last one will give me an error or something. How can I check if the entry in the entry is pointing to nullptr?
Note: this partly ignores the X/Y of this question, based on the tandom question (Heterogenous vectors of pointers. How to call functions)
What you seem to be after is polymorphic collections, but not with a virtual type hierarchy.
This is known as type erasure, and Boost Type Erasure is conveniently wrapped for exactly this use case with Boost PolyCollection.
The type erased variation would probably look like any_collection:
Live On Coliru
#include <boost/variant.hpp>
#include <cmath>
#include <iostream>
#include <vector>
#include <boost/poly_collection/any_collection.hpp>
#include <boost/type_erasure/member.hpp>
namespace pc = boost::poly_collection;
BOOST_TYPE_ERASURE_MEMBER(has_write, write)
using writable = has_write<void()>;
template <typename T> class A {
public:
A(T value = 0) : data(value) {}
// A() = default; // rule of zero
//~A() = default;
void write() const { std::cout << data << std::endl; }
private:
T data/* = 0*/;
};
int main()
{
pc::any_collection<writable> registry;
A<int> one(314);
A<double> two(M_PI);
registry.insert(one);
registry.insert(two);
for (auto& w : registry) {
w.write();
}
}
Prints
3.14159
314
Note that the insertion order is preserved, but iteration is done type-by-type. This is also what makes PolyCollection much more efficient than "regular" containers that do not optimize allocation sizes or use pointers.
BONUS: Natural printing operator<<
Using classical dynamic polymorphism, this would not work without adding virtual methods, but with Boost TypeErasure ostreamable is a ready-made concept:
Live On Coliru
#include <boost/variant.hpp>
#include <cmath>
#include <iostream>
#include <vector>
#include <boost/poly_collection/any_collection.hpp>
#include <boost/type_erasure/operators.hpp>
namespace pc = boost::poly_collection;
using writable = boost::type_erasure::ostreamable<>;
template <typename T> class A {
public:
A(T value = 0) : data(value) {}
// A() = default; // rule of zero
//~A() = default;
private:
friend std::ostream& operator<<(std::ostream& os, A const& a) {
return os << a.data;
}
T data/* = 0*/;
};
int main()
{
pc::any_collection<writable> registry;
A<int> one(314);
A<double> two(M_PI);
registry.insert(one);
registry.insert(two);
for (auto& w : registry) {
std::cout << w << "\n";
}
}
Printing the same as before.
UPDATE
To the comment:
I want to create n A<someType> variables (these are big objects). All of these variables have a write function to write something to a file.
My idea is to collect all the pointers of these variables and at the end loop through the vector to call each write function. Now, it might happen that I want to allocate memory and delete a A<someType> variable. If this happens it should not execute the write function.
This sounds like one of the rare occasions where shared_ptr makes sense, because it allows you to observe the object's lifetime using weak_ptr.
Object Graph Imagined...
Let's invent a node type that can participate in a pretty large object graph, such that you would keep an "index" of pointers to some of its nodes. For this demonstration, I'll make it a tree-structured graph, and we're going to keep References to the leaf nodes:
using Object = std::shared_ptr<struct INode>;
using Reference = std::weak_ptr<struct INode>;
Now, lets add identification to the Node base so we have an arbitrary way to identify nodes to delete (e.g. all nodes with odd ids). In addition, any node can have child nodes, so let's put that in the base node as well:
struct INode {
virtual void write(std::ostream& os) const = 0;
std::vector<Object> children;
size_t id() const { return _id; }
private:
size_t _id = s_idgen++;
};
Now we need some concrete derived node types:
template <typename> struct Node : INode {
void write(std::ostream& os) const override;
};
using Root = Node<struct root_tag>;
using Banana = Node<struct banana_tag>;
using Pear = Node<struct pear_tag>;
using Bicycle = Node<struct bicycle_tag>;
// etc
Yeah. Imagination is not my strong suit ¯\(ツ)/¯
Generate Random Data
// generating demo data
#include <random>
#include <functional>
#include <array>
static std::mt19937 s_prng{std::random_device{}()};
static std::uniform_int_distribution<size_t> s_num_children(0, 3);
Object generate_object_graph(Object node, unsigned max_depth = 10) {
std::array<std::function<Object()>, 3> factories = {
[] { return std::make_shared<Banana>(); },
[] { return std::make_shared<Pear>(); },
[] { return std::make_shared<Bicycle>(); },
};
for(auto n = s_num_children(s_prng); max_depth && n--;) {
auto pick = factories.at(s_prng() % factories.size());
node->children.push_back(generate_object_graph(pick(), max_depth - 1));
}
return node;
}
Nothing fancy. Just a randomly generated tree with a max_depth and random distribution of node types.
write to Pretty-Print
Let's add some logic to display any object graph with indentation:
// for demo output
#include <boost/core/demangle.hpp>
template <typename Tag> void Node<Tag>::write(std::ostream& os) const {
os << boost::core::demangle(typeid(Tag*).name()) << "(id:" << id() << ") {";
if (not children.empty()) {
for (auto& ch : children) {
ch->write(os << linebreak << "- " << indent);
os << unindent;
}
os << linebreak;
}
os << "}";
}
To keep track of the indentation level I'll define these indent/unindent
manipulators modifying some custom state inside the stream object:
static auto s_indent = std::ios::xalloc();
std::ostream& indent(std::ostream& os) { return os.iword(s_indent) += 3, os; }
std::ostream& unindent(std::ostream& os) { return os.iword(s_indent) -= 3, os; }
std::ostream& linebreak(std::ostream& os) {
return os << "\n" << std::setw(os.iword(s_indent)) << "";
}
That should do.
Getting Leaf Nodes
Leaf nodes are the nodes without any children.
This is a depth-first tree visitor taking any output iterator:
template <typename Out>
Out get_leaf_nodes(Object const& tree, Out out) {
if (tree) {
if (tree->children.empty()) {
*out++ = tree; // that's a leaf node!
} else {
for (auto& ch : tree->children) {
get_leaf_nodes(ch, out);
}
}
}
return out;
}
Removing some nodes:
Yet another depht-first visitor:
template <typename Pred>
size_t remove_nodes_if(Object tree, Pred predicate)
{
size_t n = 0;
if (!tree)
return n;
auto& c = tree->children;
// depth first
for (auto& child : c)
n += remove_nodes_if(child, predicate);
auto e = std::remove_if(begin(c), end(c), predicate);
n += std::distance(e, end(c));
c.erase(e, end(c));
return n;
}
DEMO TIME
Tieing it all together, we can print a randomly generated graph:
int main()
{
auto root = generate_object_graph(std::make_shared<Root>());
root->write(std::cout);
This puts all its leaf node References in a container:
std::list<Reference> leafs;
get_leaf_nodes(root, back_inserter(leafs));
Which we can print using their write() methods:
std::cout << "\nLeafs: " << leafs.size();
for (Reference& ref : leafs)
if (Object alive = ref.lock())
alive->write(std::cout << " ");
Of course all the leafs are still alive. But we can change that! We will remove one in 5 nodes by id:
auto _2mod5 = [](Object const& node) { return (2 == node->id() % 5); };
std::cout << "\nRemoved " << remove_nodes_if(root, _2mod5) << " 2mod5 nodes from graph\n";
std::cout << "\n(Stale?) Leafs: " << leafs.size();
The reported number of leafs nodes would still seem the same. That's... not
what you wanted. Here's where your question comes in: how do we detect the
nodes that were deleted?
leafs.remove_if(std::mem_fn(&Reference::expired));
std::cout << "\nLive leafs: " << leafs.size();
Now the count will accurately reflect the number of leaf nodes remaining.
Live On Coliru
#include <memory>
#include <vector>
#include <ostream>
using Object = std::shared_ptr<struct INode>;
using Reference = std::weak_ptr<struct INode>;
static size_t s_idgen = 0;
struct INode {
virtual void write(std::ostream& os) const = 0;
std::vector<Object> children;
size_t id() const { return _id; }
private:
size_t _id = s_idgen++;
};
template <typename> struct Node : INode {
void write(std::ostream& os) const override;
};
using Root = Node<struct root_tag>;
using Banana = Node<struct banana_tag>;
using Pear = Node<struct pear_tag>;
using Bicycle = Node<struct bicycle_tag>;
// etc
// for demo output
#include <boost/core/demangle.hpp>
#include <iostream>
#include <iomanip>
static auto s_indent = std::ios::xalloc();
std::ostream& indent(std::ostream& os) { return os.iword(s_indent) += 3, os; }
std::ostream& unindent(std::ostream& os) { return os.iword(s_indent) -= 3, os; }
std::ostream& linebreak(std::ostream& os) {
return os << "\n" << std::setw(os.iword(s_indent)) << "";
}
template <typename Tag> void Node<Tag>::write(std::ostream& os) const {
os << boost::core::demangle(typeid(Tag*).name()) << "(id:" << id() << ") {";
if (not children.empty()) {
for (auto& ch : children) {
ch->write(os << linebreak << "- " << indent);
os << unindent;
}
os << linebreak;
}
os << "}";
}
// generating demo data
#include <random>
#include <functional>
#include <array>
static std::mt19937 s_prng{std::random_device{}()};
static std::uniform_int_distribution<size_t> s_num_children(0, 3);
Object generate_object_graph(Object node, unsigned max_depth = 10) {
std::array<std::function<Object()>, 3> factories = {
[] { return std::make_shared<Banana>(); },
[] { return std::make_shared<Pear>(); },
[] { return std::make_shared<Bicycle>(); },
};
for(auto n = s_num_children(s_prng); max_depth && n--;) {
auto pick = factories.at(s_prng() % factories.size());
node->children.push_back(generate_object_graph(pick(), max_depth - 1));
}
return node;
}
template <typename Out>
Out get_leaf_nodes(Object const& tree, Out out) {
if (tree) {
if (tree->children.empty()) {
*out++ = tree;
} else {
for (auto& ch : tree->children) {
get_leaf_nodes(ch, out);
}
}
}
return out;
}
template <typename Pred>
size_t remove_nodes_if(Object tree, Pred predicate)
{
size_t n = 0;
if (!tree)
return n;
auto& c = tree->children;
// depth first
for (auto& child : c)
n += remove_nodes_if(child, predicate);
auto e = std::remove_if(begin(c), end(c), predicate);
n += std::distance(e, end(c));
c.erase(e, end(c));
return n;
}
#include <list>
int main()
{
auto root = generate_object_graph(std::make_shared<Root>());
root->write(std::cout);
std::list<Reference> leafs;
get_leaf_nodes(root, back_inserter(leafs));
std::cout << "\n------------"
<< "\nLeafs: " << leafs.size();
for (Reference& ref : leafs)
if (Object alive = ref.lock())
alive->write(std::cout << " ");
auto _2mod5 = [](Object const& node) { return (2 == node->id() % 5); };
std::cout << "\nRemoved " << remove_nodes_if(root, _2mod5) << " 2mod5 nodes from graph\n";
std::cout << "\n(Stale?) Leafs: " << leafs.size();
// some of them are not alive, see which are gone ("detecing the null pointers")
leafs.remove_if(std::mem_fn(&Reference::expired));
std::cout << "\nLive leafs: " << leafs.size();
}
Prints e.g.
root_tag*(id:0) {
- bicycle_tag*(id:1) {}
- bicycle_tag*(id:2) {
- pear_tag*(id:3) {}
}
- bicycle_tag*(id:4) {
- bicycle_tag*(id:5) {}
- bicycle_tag*(id:6) {}
}
}
------------
Leafs: 4 bicycle_tag*(id:1) {} pear_tag*(id:3) {} bicycle_tag*(id:5) {} bicycle_tag*(id:6) {}
Removed 1 2mod5 nodes from graph
(Stale?) Leafs: 4
Live leafs: 3
Or see the COLIRU link for a much larger sample.
I have a school assignment to implement a hash_set and a hash_map, I am given a main.cpp that has code to test these classes (inserts, deletes, searches, prints, etc). I am also given a base hash_table class that is a templated class, and just told to "make the hash_set and hash_map work based on the testing code with new classes of their own".
Without more specific details, I assume I am supposed to implement the set and map based on the hash_table, which the teacher said the project could be completed in 40 lines of code (wat??) between the two new classes...but I can't even get the hash_set class to inherit the hash_table since it's a template. I can get it compiling ok without making the hash_set class a template, until I instantiate it, since the testing code is passing it a template type in the declaration. But if I try to make the class a template type itself to compensate for this, everything breaks and I can't track down the source of the error. I'm about 8 hours into this project which should supposedly be a 1 hour project, so I'm at wits end here.
Here is the original main.cpp with the testing code (hash_map is commented out because I haven't even started that yet :/ )
//
// main.cpp
// CS3100Project05_HashCollections
#include "WSUHashTable.h"
//#include "WSUHashMap.h" // Uncomment to test WSUHashMap
#include "WSUHashSet.h" // Uncomment to test WSUHashSet
#include <iostream>
int main(int argc, const char * argv[])
{
///////////////////////////////////////////////////////////////////
/// Part 1 :TEST HASH TABLE ///
///////////////////////////////////////////////////////////////////
{
WSUHashTable<int> example = {1, 2, 3, 4, 1};
std::cout << "Part 1a: Expected output is in any order " <<
"\"1 2 3 4\"\n";
for(int n : example)
{
std::cout << n << ' ';
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << "Part 1b: Expected output is \"Found 2\"\n";
{
auto search = example.find(2);
if(search != example.end())
{
std::cout << "Found " << (*search) << '\n';
}
else
{
std::cout << "Not found\n";
}
}
std::cout << std::endl;
std::cout << "Part 1c: Expected output is \"Not found\"\n";
example.erase(2);
{
auto search = example.find(2);
if(search != example.end()) {
std::cout << "Found " << (*search) << '\n';
}
else {
std::cout << "Not found\n";
}
}
std::cout << std::endl;
}
///////////////////////////////////////////////////////////////////
/// Part 2: TEST HASH SET ///
///////////////////////////////////////////////////////////////////
/***** Uncomment to test WSUHashSet */
{
WSUHashSet<std::string> stringSet = {"1", "2", "3", "4"};
std::cout << "Part 2a: Expected output is \"Found \"2\"\"\n";
{ // Test find() that succeeds
auto search = stringSet.find("2");
if(search != stringSet.end()) {
std::cout << "Found \"" << (*search) << "\"\n";
}
else {
std::cout << "Not found\n";
}
}
std::cout << std::endl;
std::cout << "Part 2b: Expected output is \"Not found\"\n";
stringSet.erase("2");
{ // Test find() that fails
auto search = stringSet.find("2");
if(search != stringSet.end())
{
std::cout << "Found \"" << (*search) << "\"\n";
}
else
{
std::cout << "Not found\n";
}
}
std::cout << std::endl;
WSUHashSet<double> doubleSet = {
1.1, 2.2, 3.2, 4.4, 5.5, 6.1, 7.2, 8.4, 9.9
};
std::cout << "Part 2c: Expected output is in any order " <<
"\"5.5 7.2 8.4 9.9 1.1 2.2 3.2 4.4 6.1\"\n";
for(auto n : doubleSet )
{ // Test using implicit iterators
std::cout << n << ' ';
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << "Part 2d: Expected output is in any order " <<
"\"5.5 7.2 8.4 9.9 4.4 6.1\"\n";
// Part 7: Using explicit iterators while mutating set
for(auto it = doubleSet.begin(); it != doubleSet.end(); )
{ // erase every number less than 4.0
if(*it < 4.0)
{
it = doubleSet.erase(it);
}
else
{
++it;
}
}
for(auto n : doubleSet)
{
std::cout << n << ' ';
}
std::cout << std::endl;
std::cout << std::endl;
}
///////////////////////////////////////////////////////////////////
/// Part 3: TEST HASH MAP ///
///////////////////////////////////////////////////////////////////
/***** Uncomment to test WSUHashMap *
{
WSUHashMap<int, std::string> dict = {{1, "one"}, {2, "two"}};
dict.insert({3, "three"});
dict.insert(std::make_pair(4, "four"));
dict.insert({{4, "another four"}, {5, "five"}});
std::cout << "Part 3a: Expected output is " <<
"\"inserting 1 -> \"another one\" failed\"\n";
bool ok = dict.insert({1, "another one"}).second;
std::cout << "inserting 1 -> \"another one\" "
<< (ok ? "succeeded" : "failed") << '\n';
std::cout << std::endl;
std::cout << "Part 3b: Expected output is " <<
"\"contents: " <<
"1 => one " <<
"2 => two " <<
"3 => three " <<
"4 => four " <<
"5 => five\"\n";
std::cout << "contents: ";
for(auto& p : dict)
{
std::cout << " " << p.first << " => " << p.second << ' ';
}
std::cout << std::endl;
}
*****/
return 0;
}
And this is the original hash_table class file:
//
// WSUHashTable.h
// CS3100Project05_HashCollections
#ifndef __CS3100Project05_HashCollections__WSUHashTable_H
#define __CS3100Project05_HashCollections__WSUHashTable_H
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include <cassert>
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
template <
typename elementT,
typename Hash = std::hash<elementT>
>
class WSUHashTable
{
private:
///////////////////////////////////////////////////////////////////
typedef elementT bucket_t;
///////////////////////////////////////////////////////////////////
static const std::size_t initialCapacity = (1 << 2);
constexpr static float maxLoadFactor = 0.73f;
///////////////////////////////////////////////////////////////////
std::size_t mSize; // Number of elements in table
std::vector<bucket_t> mStorage; // Storage for elements
std::vector<bool> mIsValidFlags;
public:
///////////////////////////////////////////////////////////////////
class iterator : public std::iterator<
std::input_iterator_tag, elementT>
{
friend class WSUHashTable<elementT, Hash>;
const WSUHashTable<elementT, Hash> *mHashTablePtr;
std::size_t mIndex;
////////////////////////////////////////////////////////////////
iterator(
const WSUHashTable<elementT, Hash> &hashTable,
std::size_t index = 0
) :
mHashTablePtr(&hashTable),
mIndex(index)
{
}
////////////////////////////////////////////////////////////////
std::size_t getIndex() const
{
return mIndex;
}
public:
////////////////////////////////////////////////////////////////
iterator(const iterator &other) :
mHashTablePtr(other.mHashTablePtr),
mIndex(other.mIndex)
{
}
////////////////////////////////////////////////////////////////
iterator& operator++()
{
++mIndex;
while(mIndex < mHashTablePtr->mIsValidFlags.size() &&
!mHashTablePtr->mIsValidFlags[mIndex])
{ // Skip over empty buckets
++mIndex;
}
return *this;
}
////////////////////////////////////////////////////////////////
iterator operator++(int)
{
const_iterator tmp(*this);
operator++();
return tmp;
}
////////////////////////////////////////////////////////////////
bool operator==(const iterator& rhs)
{
return mIndex == rhs.mIndex;
}
////////////////////////////////////////////////////////////////
bool operator!=(const iterator& rhs)
{
return mIndex != rhs.mIndex;
}
////////////////////////////////////////////////////////////////
const elementT &operator*()
{
return mHashTablePtr->mStorage[mIndex];
}
};
///////////////////////////////////////////////////////////////////
typedef const iterator const_iterator;
private:
typedef std::pair<const_iterator, bool> _findResult;
///////////////////////////////////////////////////////////////////
std::size_t _calculatedIndex(const elementT &element) const
{
return (Hash()(element) % mStorage.size());
}
///////////////////////////////////////////////////////////////////
// Returns a pair containing iterator to bucket where element
// should be and flag indicating whether it is there.
_findResult _find( const elementT &element ) const
{
std::size_t index = _calculatedIndex(element);
while(mIsValidFlags[index] &&
((mStorage[index] < element) || (element < mStorage[index])))
{ // Loop until element is found or an empty bucket is found
++index;
index %= mStorage.size();
}
return _findResult(
const_iterator(*this, index), mIsValidFlags[index]);
}
///////////////////////////////////////////////////////////////////
void _doubleCapacityAndRehash()
{
const std::size_t oldSize = mIsValidFlags.size();
// Save off mStorage by moving contents instead of copying
std::vector<bucket_t> oldStorage = std::move(mStorage);
std::vector<bool> oldIsValidFlags = std::move(mIsValidFlags);
// Replace mStorage and mIsValidFlags with empty storage with
// twice the size/capacity.
mStorage = std::move(std::vector<bucket_t>(oldSize * 2));
mIsValidFlags = std::move(std::vector<bool>(oldSize * 2));
// We are going to re-insert everything, so strat with size 0
mSize = 0;
for(std::size_t i = 0; i < oldSize; ++i)
{ // Insert values from all valid buckets in old storage
if(oldIsValidFlags[i])
{
insert(oldStorage[i]);
}
}
}
public:
///////////////////////////////////////////////////////////////////
WSUHashTable() :
mSize(0),
mStorage(initialCapacity),
mIsValidFlags(initialCapacity)
{
}
///////////////////////////////////////////////////////////////////
WSUHashTable(const WSUHashTable &other) :
mSize(other.mSize),
mStorage(other.mStorage),
mIsValidFlags(other.mIsValidFlags)
{
}
///////////////////////////////////////////////////////////////////
template< class InputIt >
WSUHashTable(InputIt first, InputIt last) :
mSize(0),
mStorage(initialCapacity),
mIsValidFlags(initialCapacity)
{
while(first != last)
{
insert(*first);
++first;
}
}
///////////////////////////////////////////////////////////////////
WSUHashTable(std::initializer_list<elementT> init) :
mSize(0),
mStorage(initialCapacity),
mIsValidFlags(initialCapacity)
{
insert(init);
}
///////////////////////////////////////////////////////////////////
std::size_t size() const
{
return mSize;
}
///////////////////////////////////////////////////////////////////
/// Inserts element(s) into the container, if the container doesn't
/// already contain an an equivalent element.
/// Returns a pair consisting of an iterator to the inserted
/// element (or to the element that prevented the insertion) and a
/// bool denoting whether the insertion took place.
std::pair<const_iterator, bool> insert( const elementT &element )
{
if(mSize > (maxLoadFactor * mStorage.size()))
{ // resize to make room for insertion
_doubleCapacityAndRehash();
}
_findResult result = _find(element);
if(result.second)
{ // element is present
return std::pair<const_iterator, bool>(result.first, false);
}
const std::size_t index = result.first.getIndex();
mStorage[index] = element;
mIsValidFlags[index] = true;
++mSize;
return std::pair<const_iterator, bool>(result.first, true);
}
///////////////////////////////////////////////////////////////////
/// Inserts element(s) into the container, if the container doesn't
/// already contain an an equivalent element.
/// Returns a pair consisting of an iterator to the inserted
/// element (or to the element that prevented the insertion) and a
/// bool denoting whether the insertion took place.
///
/// An && argumnet signals an "emplace" operation in C++11. The
/// value will be moved via std::move() instead of copied.
std::pair<const_iterator, bool> insert( elementT &&element )
{
if(mSize > (maxLoadFactor * mStorage.size()))
{ // resize to make room for insertion
_doubleCapacityAndRehash();
}
_findResult result = _find(element);
if(result.second)
{ // element is present
return std::pair<const_iterator, bool>(
result.first, false);
}
const std::size_t index = result.first.getIndex();
mStorage[index] = std::move(element);
mIsValidFlags[index] = true;
++mSize;
return std::pair<const_iterator, bool>(result.first, true);
}
///////////////////////////////////////////////////////////////////
void insert( std::initializer_list<elementT> ilist )
{
for(const elementT &element : ilist)
{
insert(element);
}
}
///////////////////////////////////////////////////////////////////
/// Returns iterator following the last removed element.
const_iterator erase( const_iterator pos )
{
const std::size_t index = pos.getIndex();
if(mIsValidFlags[index])
{ // element is present
mIsValidFlags[index] = false;
--mSize;
}
return ++iterator(pos);
}
///////////////////////////////////////////////////////////////////
// Returns the number of elements erased (it will be zero or one).
std::size_t erase(const elementT &element)
{
_findResult result = _find(element);
if(result.second)
{ // element is present
mIsValidFlags[result.first.getIndex()] = false;
--mSize;
return 1;
}
return 0;
}
///////////////////////////////////////////////////////////////////
const_iterator find( const elementT &element ) const
{
_findResult result = _find(element);
if(result.second)
{ // element was found
return result.first;
}
return end();
}
///////////////////////////////////////////////////////////////////
const_iterator begin() const
{
std::size_t index = 0;
while(index < mIsValidFlags.size() && !mIsValidFlags[index])
{ // Skip over empty buckets
++index;
}
return const_iterator(*this, index);
}
///////////////////////////////////////////////////////////////////
const_iterator end() const
{
return const_iterator(*this, mStorage.size());
}
};
#endif /* defined(__CS3100Project05_HashCollections__WSUHashTable_H) */
I know the hash_table file is long, and I have a decent idea on how to proceed, since a hash_set really only needs to implement a search function within the insert to make sure the list is unique, but doesn't really mess with hash order or anything (I'm not really sure how to implement the hash itself, but I assume the inheritance will sort that out nicely), and the hash_map will allow a null key and any null values....but first something has to compile.
This is what I have currently as my hash_set class, just trying to get a simple constructor that uses the parent class's constructor with the correct type (std::string):
#ifndef __WSUHashSet__
#define __WSUHashSet__
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include "WSUHashTable.h"
/*template<
typename elementT,
typename Hash = std::hash<elementT>
>*/
class WSUHashSet : public WSUHashTable<std::string> {
private:
public:
WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
};
#endif //__WSUHashSet__
Currently I get the same error: with this exact code it tells me that "WSUHashSet is not a template", which is good because my class is fine, but bad because I can't just edit the main.cpp to not treat it as a template.
When I try to make it a template, like this:
#ifndef __WSUHashSet__
#define __WSUHashSet__
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include "WSUHashTable.h"
template<
typename elementT,
typename Hash = std::hash<elementT>
>
class WSUHashSet<elementT> : public WSUHashTable<std::string> {
private:
public:
WSUHashSet<elementT>::WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
};
#endif //__WSUHashSet__
I really need some direction here. I can't afford to spend a lot more time on this as it's not my only class, and I feel like this should be fairly simple. The theory makes sense, but the implementation makes me feel like I'm blindly wandering in circles.
Thanks
EDIT: the actual compliler error is
WSUHashSet.h line 19: error: WSUHashSet is not a template
Apparently the pasting into the code block here caused confusion. Here's the actual line (19 in codeblocks) that is breaking the compilation:
WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
If you look at the test code, you can see the the types you create must be templates, as they're instantiated for specific element types:
WSUHashTable<int> example = {1, 2, 3, 4, 1};
WSUHashSet<double> doubleSet = { ...
You show your attempt to make a template:
template<
typename elementT,
typename Hash = std::hash<elementT>
>
class WSUHashSet<elementT> : public WSUHashTable<std::string> {
public:
WSUHashSet<elementT>::WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
...
That's not so far off... try:
template <typename elementT>
class WSUHashSet<elementT> : public WSUHashTable<elementT>
{
public:
WSUHashSet(std::initializer_list<std::string> init)
: WSUHashTable<elementT>(init)
{ }
I have an heterogeneous n-ary tree made by different kind of nodes, for example:
class Node { }
class Subnode1 : public Node
{
}
class Subnode2 : public Node
{
private:
unique_ptr<Subnode1> child;
public:
Subnode1* getChild() { return child.get(); }
}
class Subnode4 : public Subnode2 { }
class Subnode3 : public Node
{
private:
list<unique_ptr<Subnode2>> children;
public:
// getter
}
// etc
The structure could contain any amount of types of nodes and it will be extended in the future.
I implemented a way to visit the tree which allows me to customize what to do when each node is visited which is basically implemented in this way:
void genericVisit(Node* node) {
if (dynamic_cast<Subnode2>(node))
visit(static_cast<Subnode2*>(node));
// etc
}
virtual void visit(Subnode2* node) {
if (node->getChild())
genericVisit(node->getChild());
}
// etc
It works nicely to traverse the tree but now I have the requirement to be able to replace subtrees with other subtrees so I'm thinking about the best approach to follow without altering the structure too much.
The best solution would have been to let getter return directly unique_ptr<Subnode2>& so that I do the visit on the unique pointers and I'm able to change the content of the smart pointer while visiting it. This would work but unique_ptr is not polymorphic, and there is no way to dispatch the call to the most specialized method, eg:
void genericVisit(unique_ptr<Node>& node) { // NON WORKING CODE
if (dynamic_cast<unique_ptr<Subnode2>&>(node))
visit(static_cast<unique_ptr<Subnode2>&>(node));
// etc
}
So I was wondering which could be the best solution to the problem, minding that while traversing the tree, as long as I change the content of the current subtree without changing the reference to that node in the parent, (which would be allowed by using an unique_ptr) I don't see any problem.
Because I thought a comment was not going to be enough to convey all that information:
#Jack you can (Sean Parent: should) use shared_pointer<const Node> there, and copy as required for changes. Immutability for the win! That said, perhaps you can do without the inheritance altogether? Make things simpler and potentially more efficient – sehe 6 hours ago
I thought I'd demonstrate both aspects a little, in reverse order:
On using static polymorphism for the trees: let's define a tree where leaf nodes can be strings or userdefined structs:
struct Data {
double x,y,z;
};
using Node = make_recursive_variant<std::string, Data, std::vector<recursive_variant_> >::type;
using Nodes = std::vector<Node>;
The sample tree used in all the tests below is now defined as:
Node tree = Nodes {
"hello",
Data { 1,2,3 },
Nodes {
"more nested",
Nodes {
Data { 2,3,4 },
Data { 3,4,5 },
Data { 4,5,6 },
},
"nodes"
}
};
Boost Variant lends itself very naturally for transformation via visitation. For example, let's write a visitor that reverses all nodes in the tree, so including the strings and Data{x,y,z} -> Data{z,y,x}:
struct reverser : static_visitor<Node> {
Node operator()(Node const& tree) const { return apply_visitor(*this, tree); }
Node operator()(std::string const& s) const { return std::string(s.rbegin(), s.rend()); }
Node operator()(Data const& d) const { return Data {d.z, d.y, d.x}; }
Node operator()(Nodes const& children) const {
Nodes revchildren;
std::transform(children.rbegin(), children.rend(), back_inserter(revchildren), *this);
return revchildren;
}
};
Node reverse(Node const& tree) {
return reverser()(tree);
}
See the simplified demo at this stage Live On Coliru
I added a print visitor so you can see what the result is
It even roundtrip tests the tree (tree == reverse(reverse(tree)))
Now you'll notice that the trees under 1. have value semantics, meaning that the reverse operation results in a completely separate copy of the tree.
I've went on and added a similar SharedTree:
using SNode = boost::shared_ptr<
boost::make_recursive_variant<
std::string, Data, std::vector<boost::shared_ptr<boost::recursive_variant_>
> >::type>;
using Node = SNode::element_type;
using Nodes = std::vector<SNode>;
When you manipulate copies of these trees, both copies change because they share nodes (the demo program below adds a node to one SharedTree and observes that the other also changes)
Now comes the Sean Parent approach: using shared_ptr<T const>
namespace CowTree {
using SNode = boost::shared_ptr<
boost::make_recursive_variant<
std::string, Data, std::vector<boost::shared_ptr<boost::recursive_variant_ const>
> >::type const>;
using Node = SNode::element_type;
using Nodes = std::vector<SNode>;
}
The beauty of this is that it's cheap to make copies, but you cannot possibly modify a node: you always have to deep-copy a subtree to modify any aspect of it's value. To demonstrate this, I've made a transformation that just uppercases string leaf nodes, leaving everything else unchanged:
template <typename SNode, typename Nodes = std::vector<SNode> >
struct upper_caser : boost::static_visitor<SNode> {
SNode operator()(SNode const& tree) const {
return apply_visitor(boost::bind(*this, boost::ref(tree), _1), *tree);
}
// dispatch
SNode operator()(SNode const&, std::string const& value) const {
std::string xformed;
std::transform(value.begin(), value.end(), back_inserter(xformed), [](uint8_t c) { return std::toupper(c); });
return boost::make_shared<typename SNode::element_type>(xformed);
}
SNode operator()(SNode const& node, Nodes const& children) const {
Nodes xformed; // TODO optimize
std::transform(children.begin(), children.end(), back_inserter(xformed), *this);
return (equal(children, xformed))
? node
: boost::make_shared<typename SNode::element_type>(xformed);
}
template <typename V> SNode operator()(SNode const& node, V const&) const {
return node;
}
};
template <typename SNode>
SNode ucase(SNode const& tree) {
return upper_caser<SNode>()(tree);
}
As you can see the transformation is completely generic and will work with the SharedTree just as well as the CowTree - because no value is ever mutated. Obviously, using the CowTree is much safer because it guarantees immutability.
The test program actively checks that
the original cow tree doesn't change when we get it's ucased transform.
it also checks that the subtrees that haven't changed, are indeed shared (haven't been cloned)
Full Program
Live On Coliru
#include <boost/variant.hpp>
namespace Tree {
struct Data {
double x,y,z;
};
using Node = boost::make_recursive_variant<std::string, Data, std::vector<boost::recursive_variant_> >::type;
using Nodes = std::vector<Node>;
namespace Operations {
struct reverser : boost::static_visitor<Node> {
Node operator()(Node const& tree) const { return apply_visitor(*this, tree); }
Node operator()(Data const& d) const { return Data {d.z, d.y, d.x}; }
Node operator()(std::string const& s) const { return std::string(s.rbegin(), s.rend()); }
Node operator()(Nodes const& children) const {
Nodes revchildren;
std::transform(children.rbegin(), children.rend(), back_inserter(revchildren), *this);
return revchildren;
}
};
Node reverse(Node const& tree) {
return reverser()(tree);
}
}
using Operations::reverse;
}
// For our demo, let's implement `operator <<` with a manipulator
#include <iostream>
namespace IO {
namespace detail {
using namespace boost;
// this pretty prints the tree as C++ initializer code
struct print_visitor : static_visitor<void> {
print_visitor(std::ostream& os, std::string const& indent = "\n") : _os(os), _indent(indent) {}
// concrete types
void operator()(std::string const& s) const { _os << '"' << s << '"'; }
void operator()(Tree::Data const& d) const { _os << "Data {" << d.x << "," << d.y << "," << d.z << '}'; }
// generics to cater for both direct and shared tree nodes
template <typename T> void operator()(shared_ptr<T> const& sp) const { (*this)(*sp); }
template <typename T> void operator()(shared_ptr<const T> const& sp) const { (*this)(*sp); }
template <typename... Ts> void operator()(variant<Ts...> const& tree) const { apply_visitor(*this, tree); }
template <typename Node>
void operator()(std::vector<Node> const& children) const {
_os << "Nodes {";
print_visitor subnode(_os, _indent + " ");
for(auto& n : children) {
_os << subnode._indent;
subnode(n);
_os << ",";
}
_os << _indent << '}';
}
private:
std::ostream& _os;
mutable std::string _indent;
};
template <typename NodeType>
struct print_manip {
print_manip(NodeType const& n) : _node(n) {}
friend std::ostream& operator<<(std::ostream& os, print_manip const& m) {
return print_visitor(os)(m._node), os << ";";
}
private:
NodeType const& _node;
};
}
template <typename NodeType>
detail::print_manip<NodeType> print(NodeType const& node) {
return node;
}
}
#include <boost/make_shared.hpp>
namespace Support {
namespace detail {
template <typename SNode, typename Nodes = std::vector<SNode> >
struct share_visitor : boost::static_visitor<SNode> {
SNode operator()(Tree::Node const& tree) const { return apply_visitor(*this, tree); }
SNode operator()(Tree::Nodes const& children) const {
Nodes shared;
std::transform(children.begin(), children.end(), back_inserter(shared), *this);
return boost::make_shared<typename SNode::element_type>(shared);
}
template <typename LeafNode>
SNode operator()(LeafNode const& v) const { return boost::make_shared<typename SNode::element_type>(v); }
};
}
}
#include <boost/bind.hpp>
namespace SharedTree {
using Tree::Data;
using SNode = boost::shared_ptr<
boost::make_recursive_variant<
std::string, Data, std::vector<boost::shared_ptr<boost::recursive_variant_>
> >::type>;
using Node = SNode::element_type;
using Nodes = std::vector<SNode>;
namespace Operations {
template <typename SNode, typename Nodes = std::vector<SNode> >
struct upper_caser : boost::static_visitor<SNode> {
SNode operator()(SNode const& tree) const {
return apply_visitor(boost::bind(*this, boost::ref(tree), _1), *tree);
}
// dispatch
SNode operator()(SNode const&, std::string const& value) const {
std::string xformed;
std::transform(value.begin(), value.end(), back_inserter(xformed), [](uint8_t c) { return std::toupper(c); });
return boost::make_shared<typename SNode::element_type>(xformed);
}
SNode operator()(SNode const& node, Nodes const& children) const {
Nodes xformed; // TODO optimize
std::transform(children.begin(), children.end(), back_inserter(xformed), *this);
return (equal(children, xformed))
? node
: boost::make_shared<typename SNode::element_type>(xformed);
}
template <typename V> SNode operator()(SNode const& node, V const&) const {
return node;
}
};
template <typename SNode>
SNode ucase(SNode const& tree) {
return upper_caser<SNode>()(tree);
}
}
using Operations::ucase;
SNode share(Tree::Node const& tree) {
return Support::detail::share_visitor<SNode>()(tree);
}
}
namespace CowTree {
using Tree::Data;
using SNode = boost::shared_ptr<
boost::make_recursive_variant<
std::string, Data, std::vector<boost::shared_ptr<boost::recursive_variant_ const>
> >::type const>;
using Node = SNode::element_type;
using Nodes = std::vector<SNode>;
SNode share(Tree::Node const& tree) {
return Support::detail::share_visitor<SNode>()(tree);
}
}
#include <boost/lexical_cast.hpp> // for the roundtrip test
namespace Support {
template <typename NodeType1, typename NodeType2>
bool tree_equal(NodeType1 const& a, NodeType2 const& b) {
using IO::print;
return boost::lexical_cast<std::string>(print(a)) ==
boost::lexical_cast<std::string>(print(b));
}
}
int main() {
using namespace Tree;
using IO::print;
using Support::tree_equal;
Node tree = Nodes {
"hello",
Data { 1,2,3 },
Nodes {
"more nested",
Nodes {
Data { 2,3,4 },
Data { 3,4,5 },
Data { 4,5,6 },
},
"nodes"
}
};
std::cout << "Before transformation: \n" << print(tree) << "\n";
std::cout << "After transformation: \n" << print(reverse(tree)) << "\n";
Node roundtrip = reverse(reverse(tree));
std::cout << "Roundtrip tree_equal: " << std::boolalpha << tree_equal(tree, roundtrip) << "\n";
std::cout << "//////////////////////////////////////////////////\n";
std::cout << "// manipulate SharedTree \"copies\"\n";
auto shared = SharedTree::share(tree);
std::cout << "Shared: " << print(shared) << "\n";
std::cout << "Equal to source: " << tree_equal(tree, shared) << "\n";
auto shared2 = shared;
using boost::get;
using boost::make_shared;
get<SharedTree::Nodes>(*shared).push_back(make_shared<SharedTree::Node>("added to a shared tree"));
std::cout << "Shared2 after changing shared: " << print(shared2) << "\n";
std::cout << "Shared trees equal: " << tree_equal(shared, shared2) << "\n";
std::cout << "Not equal to source: " << tree_equal(tree, shared) << "\n";
std::cout << "//////////////////////////////////////////////////\n";
std::cout << "// now let's see about CowTree\n";
auto cow = CowTree::share(tree);
std::cout << "Cow: " << print(cow) << "\n";
std::cout << "Equal to source: " << tree_equal(tree, cow) << "\n";
auto ucased = SharedTree::ucase(cow);
std::cout << "Ucased: " << print(ucased) << "\n";
std::cout << "Equal to cow source: " << tree_equal(ucased, cow) << "\n";
/*
* The
*
* Nodes {
* Data { 2,3,4 },
* Data { 3,4,5 },
* Data { 4,5,6 },
* },
*
* subtree should still be shared, because it wasn't touched:
*/
std::cout << "Subtree from ucased: " << print(get<CowTree::Nodes>(*get<CowTree::Nodes>(*ucased)[2])[1]) << "\n";
std::cout << "Subtree from cow: " << print(get<CowTree::Nodes>(*get<CowTree::Nodes>(*cow )[2])[1]) << "\n";
std::cout << "Subtrees match: " << tree_equal(
get<CowTree::Nodes>(*get<CowTree::Nodes>(*ucased)[2])[1],
get<CowTree::Nodes>(*get<CowTree::Nodes>(*cow) [2])[1])
<< "\n";
// unchanged nodes should be shared:
std::cout << "Subtrees shared: " << (
get<CowTree::Nodes>(*get<CowTree::Nodes>(*ucased)[2])[1].get() ==
get<CowTree::Nodes>(*get<CowTree::Nodes>(*cow) [2])[1].get())
<< "\n";
// changed nodes aren't shared:
std::cout << "Siblings unshared: " << (
get<CowTree::Nodes>(*get<CowTree::Nodes>(*ucased)[2])[2].get() !=
get<CowTree::Nodes>(*get<CowTree::Nodes>(*cow) [2])[2].get())
<< "\n";
std::cout << "Parents unshared: " << (
get<CowTree::Nodes>(*ucased)[2].get() !=
get<CowTree::Nodes>(*cow) [2].get())
<< "\n";
std::cout << "Roots unshared: " << ( ucased.get() != cow.get())
<< "\n";
}
Output:
Before transformation:
Nodes {
"hello",
Data {1,2,3},
Nodes {
"more nested",
Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
},
"nodes",
},
};
After transformation:
Nodes {
Nodes {
"sedon",
Nodes {
Data {6,5,4},
Data {5,4,3},
Data {4,3,2},
},
"detsen erom",
},
Data {3,2,1},
"olleh",
};
Roundtrip tree_equal: true
//////////////////////////////////////////////////
// manipulate SharedTree "copies"
Shared: Nodes {
"hello",
Data {1,2,3},
Nodes {
"more nested",
Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
},
"nodes",
},
};
Equal to source: true
Shared2 after changing shared: Nodes {
"hello",
Data {1,2,3},
Nodes {
"more nested",
Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
},
"nodes",
},
"added to a shared tree",
};
Shared trees equal: true
Not equal to source: false
//////////////////////////////////////////////////
// now let's see about CowTree
Cow: Nodes {
"hello",
Data {1,2,3},
Nodes {
"more nested",
Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
},
"nodes",
},
};
Equal to source: true
Ucased: Nodes {
"HELLO",
Data {1,2,3},
Nodes {
"MORE NESTED",
Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
},
"NODES",
},
};
Equal to cow source: false
Subtree from ucased: Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
};
Subtree from cow: Nodes {
Data {2,3,4},
Data {3,4,5},
Data {4,5,6},
};
Subtrees match: true
Subtrees shared: true
Siblings unshared: true
Parents unshared: true
Roots unshared: true
One way to solve the problem on having to dispatch on unique_ptr<T> is to go back to passing around pointers the way you do in your visitor, but changing the return type to Node*, like this:
// Top-level function stays the same
Node* genericVisit(Node* node) {
if (dynamic_cast<Subnode2>(node)) {
return visit(static_cast<Subnode2*>(node));
}
// etc
}
// Specialized overloads can return replacements as they go
virtual Node* visit(Subnode2* node) {
if (mustReplace()) {
Subnode1 *myReplacement = ...
return myReplacement;
}
if (node->getChild()) {
Node *replacement = genericVisit(node->getChild());
// nullptr means no replacement; non-null means we replace the child
if (replacement) {
node->setChild(replacement);
}
}
return nullptr;
}
This approach requires implementer to pay attention to what's returned (i.e. nullptr or not) before performing the replacement. On the other hand, it keeps the final decision on performing the replacement in the hands of the function that makes the visitor call, which ultimately translates to more control over the internals, i.e. better encapsulation.
I'm writing a program to read text from a file and I need to print out the position that each of the numbers appear in the file. For example, the .txt file looks like this:
one
two one two
three three two one
and my output should look like:
one: 0, 2, 7
two: 1, 3, 6
three: 4, 5
Everything works fine until I try to display the map of type string, list(int), then I get the whole ""no match for 'operator<<'" error.
Here is my code, any help would be greatly appreciated, thanks!
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <list>
#include <vector>
using namespace std;
int main()
{
ifstream is("input.txt");
if (is.fail())
{
cout << "File I/O error!" << endl;
exit(1);
}
map<string, list<int> > m;
string word;
vector<string> v;
list<int> l, x, y;
while (is >> word)
{
v.push_back(word);
}
for (unsigned int i = 0; i < v.size(); ++i)
{
if (v[i] == "one")
l.push_back(i);
else if (v[i] == "two")
x.push_back(i);
else if (v[i] == "three")
y.push_back(i);
}
m["One"] = l;
m["Two"] = x;
m["Three"] = y;
for (map<string, list<int> >::iterator i = m.begin(); i != m.end(); ++i)
cout << (*i).first << ", " << (*i).second << endl;
return 0;
}
The problem is when you try to output (*i).second. As i is of type map<string, list<int> >::iterator (*i).second is a list<int> and c++ does not know how to output it.
You have two options - either overload ostream& operator<<(ostream& out, const list<int>& l) or use an inner cycle to output the elements one by one. I personally recommend the second option as overloading the operator for such "popular" type may be dangerous.
The simplest solution:
// !!! DO NOT DO THIS AT HOME OR AT ALL !!!
namespace std {
template <typename T, typename A>
ostream& operator<<(ostream& out, list<T, A> const& l) {
if (l.empty()) { return out << "[]"; }
out << '[';
bool first = true;
for (auto const& t: l) {
if (first) { first = false; } else { out << ", "; }
out << t;
}
return out << ']';
} // operator<<
} // namespace std
Unfortunately... it is strictly forbidden to do so (you are only allowed to specialize existing templates in the std namespace, not add any overload).
Therefore, the best standard compliant solution is to:
Declare a new stream of your own (which forwards everything to a std::ostream&)
Overload this operator for your new stream (in its namespace)
Only ever use this new stream afterward
Feel free to bitch at the clumsiness...
Example of custom stream to get you started:
namespace project {
class OStream {
public:
explicit OStream(std::ostream& out): _out(out) {}
template <typename T>
OStream& operator<<(T const& t) { print(*this, t); return *this; }
template <typename T>
void push(T const& t) { _out << t; }
private:
std::ostream& _out;
}; // class OStream
// Generic Operator (directly forwards to `std::ostream`)
template <typename T>
void print(OStream& out, T const& t) { out.push(t); }
// STL Containers
template <typename It>
void print_range(OStream& out, It begin, It end) {
if (begin == end) { out << "[]"; return; }
out << '[' << *begin;
for (++begin; begin != end; ++begin) { out << ", " << *begin; }
out << ']';
} // push_range
template <typename T, typename A>
void print(OStream& out, std::list<T, A> const& l) {
print_range(out, l.begin(), l.end());
} // operator<<
} // namespace project
// usage
int main() {
std::list<int> example = { 1, 2, 3, 4 };
project::OStream(std::cout) << example << "\n";
}
Note: there are other solutions than creating a custom stream, such as copy pasting std::copy(example.begin(), example.end(), std::ostream_iterator<int>(std::cout, ", ")); everywhere you need to print a list, but I am yet to meet another handy one.