As part of an assignment for a data structures class, I am trying to get this over a decade-old code to actually work. The code is found here: http://www.brpreiss.com/books/opus4/
(And to all of the users here who are horrified at such bad design, take heart - this is a homework assignment where the goal is ostensibly to get someone else's code to work. I am not advocating its use.)
Here, the author defined the class Stack and its associated Iterator:
#ifndef STACK_H
#define STACK_H
#include "linkList.h"
#include "container.h"
class Stack : public virtual Container
{
public:
virtual Object& Top () const = 0;
virtual void Push (Object&) = 0;
virtual Object& Pop () = 0;
};
class StackAsLinkedList : public Stack
{
LinkedList<Object*> list;
class Iter;
public:
StackAsLinkedList () : list() {}
~StackAsLinkedList() { Purge(); }
//
// Push, Pop and Top
//
void Push(Object& object);
Object& Pop() override;
Object& Top() const override;
int CompareTo(Object const& obj) const;
//
// purge elements from, and accept elements onto, the list
//
void Purge();
void Accept (Visitor&) const;
friend class Iter;
};
class StackAsLinkedList::Iter : public Iterator
{
StackAsLinkedList const& stack;
ListElement<Object*> const* position;
public:
Iter (StackAsLinkedList const& _stack) : stack(_stack) { Reset(); }
//
// determine whether iterator is pointing at null
//
bool IsDone() const { return position == 0; }
//
// overloaded dereference and increment operator
//
Object& operator*() const;
void operator++();
void Reset() { position = stack.list.Head(); }
};
#endif
I am not sure what the objective is here, because trying to instantiate a StackAsLinkedList::Iter will predictably give an error because it is private. Furthermore, the author doesn't use the iterator he just implemented for stack in the below example, which instead uses the iterator defined in the parent class of Stack called Container to traverse the stack and print the values:
StackAsLinkedList stack;
Iter& i = stack.NewIterator();
stack.Push(*new Int(1) ); //type "Int" is a Wrapper for primitive "int"
stack.Push(*new Int(2) );
...
while ( ! outIter.IsDone() )
{
cout << *outIter << endl;
++outIter;
}
...
But when he creates stack.NewIterator(), a look at the method call in Container shows:
virtual Iterator& NewIterator () const { return *new NullIterator (); }
So the conditional in the while statement will always fail and thus the body will never execute.
This leads me to believe that I should be implementing another NewIterator method for Stack, but I am not sure what the return value should be ( *new StackAsLinkedList::Iter(_stack) ?).
Any ideas?
Adding the following method in StackAsLinkedList seemed to clear up the problem:
Iterator& StackAsLinkedList::NewIterator() const
{
return *new Iter(*this);
}
Also, the order of assignment in main() was also an issue. This seemed to correct it:
StackAsLinkedList stack;
stack.Push(*new Int(1) ); //type "Int" is a Wrapper for primitive "int"
stack.Push(*new Int(2) );
...
Iter& i = stack.NewIterator();
while ( ! outIter.IsDone() )
{
cout << *outIter << endl;
++outIter;
}
I realize that this solution is not ideal - ideally I should refactor or better yet just start over (or just use STL). But as I said above, the goal was to just get this stuff to compile and work within a limited time-frame. So to echo what others have said: please don't use this code!
Related
I am using a Visitor pattern to traverse and print the children of the tree I am operating on. To get indented printing, I specify the indentation level in a style like:
printCurrent();
indentLevel(); // increases static variable
Visitor::visit(elem); // which then prints the children's node data
unindentLevel(); // decreases static variable
In order to make this nicer, I want to implement a function that takes the Visitor::visit with the argument elem and automatically handles the pre-action (indentLevel()) and post-action (unindentLevel()).
Before implementing that function, I need to define a function pointer that will be used as argument for the function. However, I am failing at specifying the argument to the pointer. As an example, let's look into the PrintVisitor which is derived from the Visitor:
void PrintVisitor::visit(BinaryExpr &elem) {
std::cout << formatOutputStr({elem.getNodeName()});
this->incrementLevel();
Visitor::visit(elem); // <-- this is where I want to create a function pointer to
this->decrementLevel();
}
The idea basically is that the PrintVisitor does everything related to printing and all other logic (e.g., traversal logic) is implemented in the base class Visitor. Hence the PrintVisitor::visit needs to execute its specific action (e.g., printing via formatOutputStr) and then execute the Visitor::visit method:
void PrintVisitor::visit(BinaryExpr &elem) {
std::cout << formatOutputStr({elem.getNodeName()});
void (Visitor::*myPt)(BinaryExpr&) = &Visitor::visit; // declare function pointer
executeIndented(myPt, elem); // pass function pointer myPt
}
// ...
void executeIndented("Function f", "FunctionArgs elem") {
// pre-action
this->incrementLevel();
// main action: call function pointer
(Visitor().*f)(elem); // call function pointer with arg
//post-action
this->decrementLevel();
}
My goal is to somehow achieve that both pre- and post-action always are called in each PrintVisitor::visit method. For that I was thinking it would make sense to encapsulate these pre- and post-actions into another function executeIndented which ensures that.
The syntax of (Visitor().*myPt)(elem); looks a little odd to me, is this really the correct way to call the (base) function Visitor::visit with the argument elem using my function pointer myPt?
// EDIT
Using (Visitor(*this).*myPt)(elem); instead also works. What's the difference between those two ways and is either one of those to be preferred?
// EDIT2
Hope that the description of what I am trying to achieve is more clear now.
As I understand, you should have something like:
struct TraversalVisitor : IVisitor
{
void visit(BinaryExpr &elem) final
{
pre_traversal_action(elem);
visit(elem.lhs);
action(elem);
visit(elem.rhs);
post_traversal_action(elem);
}
virtual void pre_traversal_action(BinaryExpr &elem) { /*Nothing */ }
virtual void action(BinaryExpr &elem) { /*Nothing */ }
virtual void post_traversal_action(BinaryExpr &elem) { /*Nothing */ }
void visit(UnaryExpr &elem) final;
// ...
};
struct PrintVisitor : TraversalVisitor
{
void pre_traversal_action(BinaryExpr &elem) override {
std::cout << formatOutputStr({elem.getNodeName()});
incrementLevel();
}
//void action(BinaryExpr &elem) override { /*Nothing */ }
void post_traversal_action(BinaryExpr &elem) override { decrementLevel(); }
// ...
private:
void formatOutputStr(const std::string&);
void incrementLevel();
void decrementLevel();
// ...
};
Whereas you try to implement something like:
struct Visitor : IVisitor
{
virtual visit(BinaryExpr &elem)
{
visit(elem.lhs);
visit(elem.rhs);
}
// ...
};
struct PrintVisitor : Visitor
{
private:
void formatOutputStr(const std::string&);
void incrementLevel();
void decrementLevel();
void executeIndented(Expr& elem) {
incrementLevel(); // pre-action
// Traversal
Visitor::visit(elem);
decrementLevel(); // post-action
}
void visit(BinaryExpr &elem) override {
std::cout << formatOutputStr({elem.getNodeName()});
executeIndented(elem);
}
// ...
};
Your attempt, IMO, just factorize PrintVisitor, without enforcing some traversal strategy.
So i got a class WayPoint (inside the namespace HHN).
And i got a class WayPointContainer.
The container got a private vector variable to store objects from the type "HHN::WayPoint"
What i want to do now is i want to overload the operator[] so i can
easy access the objects inside the vector like so:
WayPoint p1("name",1.5,2.0);
WayPointContainer c1;
c1[0] = p1 // This would add the WayPoint p1 to the vector of the container on index 0
WayPoint p2 = c1[0] // This would get the WayPoint from the vector at index 0 and copy it to p2
...
I found different implementations but they were for other types then vector or did not use a complex type inside the vector.
Here is my WayPointContainer.h
#include <vector>
#include <iostream>
#include "WayPoint.h"
#ifndef SRC_WAYPOINTCONTAINER_H_
#define SRC_WAYPOINTCONTAINER_H_
class WayPointContainer {
private:
std::vector<HHN::WayPoint>* pContainer{ nullptr };
public:
WayPointContainer();
WayPointContainer(const WayPointContainer& orig);
virtual ~WayPointContainer();
WayPointContainer& operator=(const WayPointContainer& rhs);
HHN::WayPoint& operator[](int idx) const;
void Add(const HHN::WayPoint& arg);
int Size() const;
void Print() const;
};
#endif /* SRC_WAYPOINTCONTAINER_H_ */
Here is my WayPointContainer.cpp
#include <vector>
#include "WayPointContainer.h"
#include <iostream>
using namespace std;
//Default Konstruktor
WayPointContainer::WayPointContainer() {
//Heap bereich ... new ... pContainer
pContainer = new std::vector<HHN::WayPoint>;
}
//Destruktor
WayPointContainer::~WayPointContainer() {} //TODO
//Copy Konstruktor
WayPointContainer::WayPointContainer(const WayPointContainer& orig) {
pContainer = orig.pContainer;
}
WayPointContainer& WayPointContainer::operator=(const WayPointContainer& rhs) {
if(&rhs == this) {
return *this;
}
if ( pContainer != rhs.pContainer) {
pContainer = rhs.pContainer;
}
return *this;
}
HHN::WayPoint& WayPointContainer::operator[](int idx) const {*
//invalid initialization of reference of type 'HHN::WayPoint&' from expression of type 'std::vector<HHN::WayPoint>'
return pContainer[idx];
}
void WayPointContainer::Add(const HHN::WayPoint& arg) {
pContainer->insert(pContainer->begin(), arg);
}
int WayPointContainer::Size() const {
int i = pContainer->size();
return i;
}
void WayPointContainer::Print() const {
for (auto waypoint = pContainer->begin(); waypoint != pContainer->end(); ++waypoint) {
cout << waypoint->Name();
}
}
The method i am struggling with:
HHN::WayPoint& WayPointContainer::operator[](int idx) const {*
//invalid initialization of reference of type 'HHN::WayPoint&' from expression of type 'std::vector<HHN::WayPoint>'
return pContainer[idx];
}
The Code i implemented there got the invalid initialization error described above.
So i expect to use the []-operator as described at the top but right now its not implemented or implemented with an error.
(I am also missing the Destructor for the vector "pContainer" inside the destructor of the WayPointContainer. So if thats something you know feel free to add it but thats not my question just a bonus.)
If you want i can also provide the code i got for the WayPoint class and my main.cpp i use to test it.
The error message is quite clear about the immediate problem in your operator implementation
invalid initialization of reference of type 'HHN::WayPoint&' from expression of type 'std::vector<HHN::WayPoint>'
pContainer[idx] dereferences pContainer with an offset of idx, thus the result is of type std::vector<HHN::WayPoint>.
There are two ways to solve the problem:
You either dereference the pointer and apply idx on it:
return (*pContainer)[idx];
You don't use a pointer to hold your std::vector<HHN::WayPoint> class member at all (recommended solution):
class WayPointContainer {
private:
std::vector<HHN::WayPoint> container;
// ...
};
In that case you won't need to deal with memory de-/allocation for the pointer, and can simply write your operator overload as
HHN::WayPoint& WayPointContainer::operator[](int idx) const {
return container[idx];
}
I have a class idx_aware that goes into a container container, which wraps around a std::vector. When the class is added to container, container sets a pointer to itself in idx_aware, as well as the index of idx_aware in its internal memory storage.
The index is not going to change until the container is destroyed or idx_aware is removed; idx_aware needs to know about its container and its index, because it has some methods that require both to work.
Now this introduces the following problem: when I get a non-const reference to an idx_aware class contained in container, I could assign to it another idx_aware class, which could have a different index. The intention would be assigning all the fields and keeping the index as it is.
#include <vector>
#include <limits>
#include <iostream>
class container;
// Stores a std::size_t field, which can be set only by subclasses.
class with_idx {
std::size_t _i;
public:
with_idx() : _i(std::numeric_limits<std::size_t>::max()) {}
operator std::size_t() const { return _i; }
protected:
void set_idx(std::size_t i) { _i = i; }
};
// Knows its index and its container
class idx_aware : public with_idx {
container const *_container;
int _some_field1;
float _some_field2;
public:
void foo() {
// Do stuff using _container and _i
}
private:
friend class container;
};
// Wraps around a std::vector
class container {
std::vector<idx_aware> _data;
public:
idx_aware &operator[](std::size_t idx) {
// Need non-const access to call foo
return _data[idx];
}
idx_aware const &operator[](std::size_t idx) const {
return _data[idx];
}
std::size_t add(idx_aware const &item) {
// Here it could potentially reuse a freed position
std::size_t free_slot = _data.size();
// Ensure _data is big enough to contain free_slot
if (_data.size() <= free_slot) {
_data.resize(free_slot + 1);
}
// Assign
_data[free_slot] = item;
_data[free_slot].set_idx(free_slot);
_data[free_slot]._container = this;
return free_slot;
}
};
int main() {
container c;
idx_aware an_item;
std::size_t i = c.add(an_item);
std::cout << c[i] << std::endl; // Prints 0
idx_aware another_item; // Created from somewhere else
// I want to set all the data in idx_aware, but the
// index should stay the same!
c[i] = another_item;
std::cout << c[i] << std::endl; // Prints numeric_limits<size_t>::max()
// Now container[i] is broken because it doesn't know anymore its index.
return 0;
}
One possible workaround would be to change with_idx in such a way that when set_idx is called, a flag is set that prevents assignment and copy operator to overwrite the _i property, like this:
class with_idx {
std::size_t _i;
bool _readonly;
public:
with_idx() : _i(std::numeric_limits<std::size_t>::max()), _readonly(false) {}
with_idx(with_idx const &other) : _i(other._i), _readonly(false) {}
with_idx &operator=(with_idx const &other) {
if (!_readonly) {
_i = other._i;
}
return *this;
}
operator std::size_t() const { return _i; }
protected:
void set_idx(std::size_t i) {
_i = i;
if (i != std::numeric_limits<std::size_t>::max()) {
// This has been set by someone with the right to do so,
// prevent overwriting
_readonly = true;
} else {
// Removed from the container, allow overwriting
_readonly = false;
}
}
};
This would have the consequence of returning, after assignment, a reference to an idx_aware class with unchanged index.
idx_aware ¬_in_container1 = /* ... */;
idx_aware ¬_in_container2 = /* ... */;
idx_aware &in_container = /* ... */;
not_in_container1 = in_container = not_in_container2;
// std::size_t(not_in_container_1) != std::size_t(not_in_container_2)
Is there a design pattern that can model this situation in a better way? My searches were not successful.
Are there other unwanted consequences of overriding the assignment operator in this way? The limitation I pointed out in the previous example does not look too "bad".
Is there an easier solution? I thought about writing some proxy object to replace the idx_aware & return type of operator[].
Experience tells that when C++ does not do what you intend, you are likely to be misusing OOP...
Robert's comment suggested me this solution.
Why would the contained object know about its container? To be able to perform actions such as foo and provide shorthand methods that otherwise would require to have access to the container.
Let's take this functionality away from the contained object; the contained object is just data payload. Instead, let's make operator[] return not the contained object, but some sort of iterator, a wrapper around the contained object, which knows the container and the index, and once dereferenced returns the actual contained object.
class was_idx_aware {
int _some_field1;
float _some_field2;
};
class container {
std::vector<idx_aware> _data;
public:
class idx_aware_wrapper {
container const *_container;
std::size_t _idx;
public:
idx_aware_wrapper(container const &c, std::size_t i)
: _container(&c)
, _idx(i)
{}
was_idx_aware const &operator*() const {
return _container->_data[_idx];
}
was_idx_aware &operator*() {
return _container->_data[_idx];
}
void foo() {
// Do stuff using _container and _idx.
}
};
idx_aware_wrapper operator[](std::size_t i) {
return idx_aware_wrapper(*this, i);
}
/* .... */
};
This allows quick access to any data in was_idx_aware, and the wrapper class can be augmented with all the methods that require interaction with the container. No need to store and keep indices up to date or override assignment operators.
I have this class:
class CComputer {
public:
// constructor
CComputer(string name) {
this->name = name;
};
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component const & component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
string name;
string address;
list<Component> listOfComponents;
};
and then these classes:
// ancestor for other classes...It's really dummy yet, but I dunno what to add there
class Component {
public:
Component() {};
~Component() {};
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) {
this->mem = mem;
};
int mem;
};
Now I feed my CComputer class with some values:
CComputer c("test.com");
c . AddAddress("123.45.678.910") .
AddComponent(CCPU(8, 2400)) .
AddComponent(CCPU(8, 1200)).
AddComponent(CMemory(2000)).
AddComponent(CMemory(2000)));
And now I would like to print it out with all the info I've put in there (CCPU & CMemory details including)
but how to implement it, to be able to iterate through CComputer::listOfComponents and don't care if I acctually access CCPU or CMemory ? I can add it to that list, but I have really no idea, how to make it, to be able to access the variables of those components.
So the output should look like:
##### STARTING #####
CComputer:
name:test.com
address:123.45.678.910
CCPU:
cores:8,freq:2400
CCPU:
cores:8, freq:1200
CMemory:
mem:2000
CMemory:
mem:2000
###### FINISHED! #####
As others have mentioned, you need to implement a virtual function (e.g. virtual std::string ToString() const = 0;) in the base class that is inherited and overridden by each child class.
However, that isn’t enough. Your code exhibits slicing which happens when you copy your child class instances into the list: the list contains objects of type Component, not of the relevant child class.
What you need to do is store polymorphic instances. Values themselves are never polymorphic, you need to use (smart) pointers or references for this. References are out, however, since you cannot store them in a standard container (such as std::list). Using raw pointers is considered bad style nowadays, but judging from the naming conventions of your classes you don’t learn modern C++ in your class (sorry!).
Therefore, raw pointers is probably the way to go. Change your code accordingly:
Store a list of pointers:
list<Component*> listOfComponents;
Make the argument type of AddComponent a pointer instead of const&.
Call the function by passing a newed object, e.g.:
AddComponent(new CCPU(8, 2400))
Now your code leaks memory left, right and center. You need to implement a destructor to free the memory:
~CComputer() {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i)
delete *i;
}
But now your code violates the Rule of Three (read this article! It’s important, and it may be the most useful thing about C++ you’re going to learn in this programming class) and consequently you also need to implement the copy constructor and copy assignment operator. However, we can’t. Sorry. In order to implement copying for your class, you would have to implement another virtual function in your Component class, namely one that clones an object (virtual Component* Clone() const = 0;). Only then can we proceed.
Here’s a sample implementation in CCPU:
Component* Clone() const {
return new CCPU(cores, freq);
}
… this needs to be done in all classes deriving from Component, otherwise we cannot correctly copy an object of a type that derives from Component and is hidden behind a pointer.
And now we can implement copying in the CComputer class:
CComputer(CComputer const& other)
: name(name)
, address(addess) {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
}
CComputer& operator =(CComputer const& other) {
if (this == &other)
return *this;
name = other.name;
address = other.address;
listOfComponents.clear();
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
return *this;
}
This code is brittle, not thread-safe and error-prone and no competent C++ programmer would ever write this1. Real code would for instance use smart pointers instead – but as mentioned before I’m pretty sure that this would be beyond the scope of the class.
1 What does this make me now, I wonder?
Just add a virtual method to Class Component called e.g. toString(), which returns a string describing the component. Then you can iterate through all components and call toString() without worrying about exactly what each component is. If you do that, then for each computer you would be able to print out the values of all the components.
However, as pointed out in one of the comments, the example output you give in the question outputs the CCPU for all computers, then all the memory for all computers. To order the output like that, you'll need to add another virtual method to Component called e.g. getType() which returns an enum or integer that represents the type of the information. You can then have two for-next loops, one nested inside the other, where the outer loop iterates through all the types and the inner loop iterating through all the computers calling the toString() on all components which match the type specified in the outer for loop.
Here's something that implements this idea.
#include <iostream>
#include <string>
#include <list>
using namespace std;
int const TYPE_CCPU = 1;
int const TYPE_MEMORY = 2;
class Component {
public:
virtual int GetType() { return -1; }
virtual std::string ToString() const {
return "OOPS! Default `ToString` called";
}
};
class CComputer {
public:
typedef std::list<Component*>::iterator iter_t;
// constructor
CComputer(string name) {
this->name = name;
};
~CComputer() {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
delete *i;
}
}
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component *component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
void PrintType(int type) {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
if ((*i)->GetType() == type)
std::cout << (*i)->ToString() << '\n';
}
}
string name;
string address;
list<Component*> listOfComponents;
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int GetType() { return TYPE_CCPU; }
std::string ToString() const {
return "CCPU::ToString()";
}
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) { this->mem = mem; };
int GetType() { return TYPE_MEMORY; }
std::string ToString() const {
return "CMemory::ToString()";
}
int mem;
};
typedef std::list<CComputer*>::iterator iter_c;
int main() {
list<CComputer*> computerlist;
CComputer *c1 = new CComputer("test.com"), *c2 = new CComputer("test2.com");
c1->AddAddress("123.45.678.910").
AddComponent(new CCPU(8, 1200)).
AddComponent(new CMemory(2000));
computerlist.push_back(c1);
c2->AddAddress("987.65.432.10").
AddComponent(new CCPU(8, 2400)).
AddComponent(new CMemory(4000));
computerlist.push_back(c2);
for(int t=TYPE_CCPU; t<=TYPE_MEMORY; t++)
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
(*i)->PrintType(t);
}
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
delete (*i);
}
}
Implement ToString() in each of your classes. In .NET this is a standard even the "object" type implements.
So, I have got the following classes and methods:
Property: Has a single member of type int (named mTag)
TypedProperty: Inherits from the Property class and adds a member called mValue of type T to it.
PropertyList: A class which Maintains a std::set of Property and has an Add and Print method.
CheckSubset: A method which checks if a std::set is included in another set.
I don't know how I should implement the CheckSubset method. Because I do not know how to iterate through a set<Property> and access to the template member (mValue). I also tried to use the includes method, which did not work (even if it worked, I would have no idea how it did!). The same problem exists in the PropertyList::Print method, where I do not know what cast should be used.
Any advice on the implementation of CheckSubset and Print methods would be appreciated!
Updated source code (using pointer)
#include <string>
#include <iostream>
#include <set>
#include <algorithm>
#include <tr1/memory>
using namespace std;
/////////////////// Property Class //////////////////////
class Property
{
public:
Property(){};
Property(const int tag)
: mTag(tag) {}
virtual ~Property() {}
int mTag;
bool operator<(const Property &property) const
{
return mTag < property.mTag;
}
};
/////////////////// TypedProperty Class /////////////////
template< typename T >
class TypedProperty : public Property
{
public:
TypedProperty (const int tag, const T& value)
: Property(tag), mValue(value){}
T mValue;
};
/////////////////////////////////////////////////////////
typedef std::tr1::shared_ptr<Property> PropertyPtr;
/////////////////// PropertyList Class /////////////////
class PropertyList
{
public:
PropertyList(){};
virtual ~PropertyList(){};
template <class T>
void Add(int tag, T value)
{
PropertyPtr ptr(new TypedProperty<T>(tag, value));
mProperties.insert(ptr);
}
void Print()
{
for(set<PropertyPtr>::iterator itr = mProperties.begin(); itr != mProperties.end(); itr++)
{
cout << ((PropertyPtr)*itr)->mTag << endl;
// What should I do to print mValue? I do not know its type
// what should *itr be cast to?
}
}
set<PropertyPtr> mProperties;
};
//////////////////// Check Subset ///////////////////////
/*
* Checks if subset is included in superset
*/
bool CheckSubset(set<PropertyPtr> &superset, set<PropertyPtr> &subset)
{
// How can I iterate over superset and subset values while I do not know
// the type of mValue inside each Property?
// I also tried the following method which does not seem to work correctly
return includes(superset.begin(), superset.end(),
subset.begin(), subset.end());
}
int main()
{
PropertyList properties1;
properties1.Add(1, "hello");
properties1.Add(2, 12);
properties1.Add(3, 34);
properties1.Add(4, "bye");
properties1.Print();
PropertyList properties2;
properties2.Add(1, "hello");
properties2.Add(3, 34);
if(CheckSubset(properties1.mProperties, properties2.mProperties)) // should be true
cout << "properties2 is subset!" << endl;
PropertyList properties3;
properties3.Add(1, "hello");
properties3.Add(4, 1234);
if(CheckSubset(properties1.mProperties, properties3.mProperties)) // should be false
cout << "properties3 is subset!" << endl;
}
What you want, cannot be done with the current design.
Your approach fails with std::set<Property>.
std::set<Property> will slice. That means that it will only copy the Property part and forget to copy the additional TypedProperty<T> members.
As a result, inside PropertyList::print(), there is no way to access the mValue.
If you want to store TypedProperty<T>s inside a std::set, you must use some sort of pointer. I.e. either std::set<Property*>, or a smart pointer version.
For solving the problem in Print method of PropertyList, you could write a Print method for TypedProperty class, which prints its tag and value.
But about the problem in accessing mValue which you want to do some operations on, I can't think of a way using normal types and templates to get the mValue without engaging your parent class Property with template type of TypedProperty (which seems undesirable). But you could get the address of mValue and cast it to void* to eliminate the type problem. This way you will face another problem, that you can not point to value of a void* pointer, so you can not work with your pointer in parent level. Therefore, you should write a method (implemented by TypedProperty) that takes a void* pointer and casts it to the type defined in child and perform the desired operation.
For example in the following code, I assumed you want to check equality of a value in a TypedProperty with another one of the same type (IsEqual method).
Now you can implement simply CheckSubset using IsEqual (checking two elements would be like: superItr->IsEqual(subItr->GetValue())).
class Property
{
public:
Property(){};
Property(const int tag)
: mTag(tag) {}
virtual ~Property() {}
virtual void* GetValue() = 0;
virtual bool IsEqual(void* value) = 0;
virtual void Print() = 0;
int mTag;
bool operator<(const Property &property) const
{
return mTag < property.mTag;
}
};
template< typename T >
class TypedProperty : public Property
{
public:
TypedProperty (const int tag, const T& value)
: Property(tag), mValue(value){}
void* GetValue()
{
return &mValue;
}
bool IsEqual(void* value)
{
return *((T*)value) == mValue;
}
void Print()
{
cout << "Tag: " << mTag << ", Value: " << mValue << endl;
}
T mValue;
};
typedef std::tr1::shared_ptr<Property> PropertyPtr;
class PropertyList
{
public:
PropertyList(){};
virtual ~PropertyList(){};
template <class T>
void Add(int tag, T value)
{
PropertyPtr ptr(new TypedProperty<T>(tag, value));
mProperties.insert(ptr);
}
void Print()
{
cout << "-----------" << endl;
for(set<PropertyPtr>::iterator itr = mProperties.begin(); itr != mProperties.end(); itr++)
{
(*itr)->Print();
}
}
set<PropertyPtr> mProperties;
};