given the following classes:
#include <vector>
using std::vector;
enum TypeSeg {
OPEN, CLOSE, CLOSE_LEFT, CLOSE_RIGHT
};
template<class T>
class Segment {
T left;
T right;
TypeSeg typeS;
public:
Segment(T left, T right, TypeSeg typeS) :
left(left), right(right), typeS(typeS) {
}
//.....
};
template<class T>
class SegCollection {
vector<Segment<T>> segments;
public:
//.....
};
So that Segment describes segment ?left, right? while:
if typeS==OPEN so : (left,right).
if typeS==CLOSE so : [left,right].
if typeS==CLOSE_LEFT so : [left,right).
if typeS==CLOSE_RIGHTso : (left,right].
and SegCollection describes collection of segments so that:
The SegCollection no contains two same segments , and no contains two segments with intersect (it will contain the union of them instead) and even no contains two segments like that: [1,4) and [4,5) (for example) , but, it will contain [1,5).
How can I implement operator-() for SegCollection that delete segment from SegCollection so that: this segment not must be in SegCollection, but, all the points (from all the segments in SegCollection) that exists in SegCollection and in the deleted segment will be removed from the segments that exists in SegCollection.
For example: given: [1,7] , [9,12] , if we will remove (2,5) , so we will get: [1,2] , [5,7] , [9,12].
I don't know (I thought about it some hours..) how can I treat in the case that I need to split segment following remove of segment (like [1,7] in the example , that changed to [1,2] , [5,7])?
Note: Segment is a template-class because that it's can be from (int, int) , (float,float) e.g.
This tip may help.
template<typename T>
class SegCollection {
vector<Segment<T>> segments;
public:
void push_back(Segment<T> seg)
{
segments.push_back(seg);
}
const SegCollection& operator-(const Segment<T> seg)
{
// implement some rules that recognize field
// TypeSeg and depending on it, changing segments' content
// ...
return *this;
}
}
Below find the very short application of your assumptions. I have changed class to struct due to the access reasons (you could leave class but you need to provide with some getters in order to have access to the private members)
#include <vector>
#include <algorithm>
using std::vector;
enum TypeSeg {
OPEN, CLOSE, CLOSE_LEFT, CLOSE_RIGHT
};
template<typename T>
struct Segment {
T left;
T right;
TypeSeg typeS;
Segment(T left, T right, TypeSeg typeS) :
left(left), right(right), typeS(typeS) {
};
};
template<typename T>
struct SegCollection {
vector<Segment<T>> segments;
void push_back(Segment<T> seg)
{
segments.push_back(seg);
}
const SegCollection& operator-(const Segment<T> seg)
{
//Find segment that could be divided
auto it = std::find_if(segments.begin(), segments.end(),
[&seg](auto i){
if (i.left <= seg.left)
if (seg.right <= i.right)
return true;
return false;
});
//If not found, return *this
if (it == segments.end())
{
std::cout << "Cannot do this, Bro." << std::endl;
return *this;
}
//Set boundaries for new segments
int new_right = it->left + seg.left - it->left;
int new_left = seg.right;
// Here you have to apply other conditions
// I have used only one case - as you have mentioned in your post
if (seg.typeS == OPEN)
{
Segment<T> seg_first(it->left, new_right, CLOSE);
Segment<T> seg_second(new_left, it->right, CLOSE);
*it = seg_second;
this->segments.insert(it, seg_first);
}
return *this;
}
};
Related
I have a class, Table, which contains a member height. The value of height should be either an int or point to an object which has additional data.
The purpose here is that the user can either input a straight value, or choose a predefined value (identified by its ID) which can be shared by multiple Table objects.
This is a way I think I could achieve this functionality.
1) Create a base class Height.
2) Have two subclasses StraightHeight and PredefinedHeight
StraightHeight would simply contain an integer with the height (with getter and setter), and PredefinedHeight would contain the additional data:
class PredefinedHeight : public Height
{
public:
void setHeight(int height);
void setId(int id);
void setName(std::string name);
int getHeight();
int getId();
std::string getName();
private:
int height;
int id;
std::string name;
};
Inside Table, the height member would be of type Height.
I think this would work in practice, however I have another issue. When it comes to displaying this data, if height is a straight value (of type StraightHeight), it should just display the int height. If it is a PredefinedHeight, it should display the id and name as well. I could have a virtual method std::string getData() in both classes which returns the necessary data but it feels messy relying on the classes to format the string, etc.
Alternatively I could use a dynamic cast, but worry that it may be bad practise in this case.
Or for simplicity sake, not bother with inheritance and just have one class for Height. If it's a straight value, just leave the additional members empty.
Does anyone have a better suggestion?
Note: This is an updated, rewritten answer. The prior version is available in the edit history.
Let's look at it from data modeling perspective: Table has a property height. That property can either be owned by the Table, or can come from another source. The simplest way one could think of would be to use a pointer to a const object. The pointer should be to a const, since the height may be a preset, and those shouldn't be changeable via the Table object.
class Table {
int m_myHeight;
const int *m_height = &m_myHeight;
public:
int height() const { return *m_height; }
void setHeight(int newHeight) {
m_height = &m_myHeight;
m_myHeight = newHeight;
}
void setPresetHeight(const int &preset)
{
m_height = &preset;
/* this line is optional */ m_myHeight = *m_height;
}
};
class PresetHeights {
std::vector<int> m_data;
public:
const int &getPreset(int index);
};
This will work just fine, but you may wish to have some additional properties assigned to the preset - properties that the "embedded" height of the Table object doesn't have. For example, the preset can have a name, etc.
This can be done by holding a reference to either "just" a height, or to a height preset. Since the identifier of a height preset is used to index the identifier in a presets table, it probably makes sense to make the id value private, and only accessible via the presets table. This gives the power over ids to the presets table, and gives some freedom in how the table is implemented.
The example that follows is written in C++17, and can be tried out on godbolt.
#include <string>
#include <variant>
struct Height {
int value;
Height(int value) : value(value) {}
operator int() const { return value; }
};
struct HeightPreset : Height {
using Id = int;
private:
Id id; // the unique identifier of this preset
friend class HeightPresets;
friend int main(); // test harness
public:
std::string name; // name of this preset
template <typename Name>
HeightPreset(Id id, int value, Name &&name) :
Height(value), id(id), name(std::forward<Name>(name)) {}
};
The Table uses std::variant to hold either no height value (std::monostate), or a Height, or a HeightPreset:
#include <functional>
class Table {
using preset_t = std::reference_wrapper<const HeightPreset>;
std::variant<std::monostate, Height, preset_t> m_height;
public:
std::optional<Height> height() const {
if (auto *customHeight = std::get_if<Height>(&m_height))
return *customHeight;
else if (auto *presetHeight = std::get_if<preset_t>(&m_height))
return std::get<preset_t>(m_height).get();
else
return {};
}
void setHeight(Height newHeight)
{ m_height = newHeight; }
void setHeightPreset(const HeightPreset &preset)
{ m_height = std::cref(preset); }
bool hasPresetHeight() const { return m_height.index() == 2; }
const HeightPreset &presetHeight() const
{ return std::get<preset_t>(m_height).get(); }
};
Presets Iterable By Value of Type HeightPreset
The presets are a map from HeightPreset::Id to HeightPreset. But first, we need an iterator adapter to let us iterate the preset values - hiding the implementation detail that we use a map whose iterated values are std::pair, not HeightPreset.
#include <map>
template <class K, class V, class C, class A>
class map_cvalue_iterator
{
typename std::map<K, V, C, A>::const_iterator it;
public:
map_cvalue_iterator(typename std::map<K,V>::const_iterator it) : it(it) {}
map_cvalue_iterator(const map_cvalue_iterator &o) : it(o.it) {}
auto &operator=(const map_cvalue_iterator &o) { it = o.it; return *this; }
auto operator++(int) { auto val = *this; ++it; return val; }
auto &operator++() { ++it; return *this; }
auto operator--(int) { auto val = *this; --it; return val; }
auto &operator--() { --it; return *this; }
const V& operator*() const { return it->second; }
const V* operator->() const { return it->second; }
bool operator==(map_cvalue_iterator o) const { return it == o.it; }
bool operator!=(map_cvalue_iterator o) const { return it != o.it; }
};
template <class M>
using map_cvalue_iterator_type
= map_cvalue_iterator<typename M::key_type, typename M::mapped_type,
typename M::key_compare, typename M::allocator_type>;
The presets are a thin wrapper around std::map:
class HeightPresets {
public:
using Id = HeightPreset::Id;
HeightPresets(std::initializer_list<HeightPreset> presets)
{
for (auto &preset : presets)
m_presets.insert({preset.id, preset});
}
auto &get(Id id) const { return m_presets.at(id); }
Id getIdFor(const HeightPreset &preset) const
{ return preset.id; }
auto begin() const { return map_cvalue_iterator_type<map_t>(m_presets.cbegin()); }
auto end() const { return map_cvalue_iterator_type<map_t>(m_presets.cend()); }
private:
using map_t = std::map<Id, HeightPreset>;
map_t m_presets;
};
The simple test harness that demonstrates the use of those types:
#include <cassert>
int main() {
const HeightPresets presets{
{1, 5, "A Fiver"},
{2, 10, "A Tenner"}
};
Table aTable;
assert(!aTable.height());
// The table has no height by default
aTable.setHeight(10);
assert(!aTable.hasPresetHeight());
// The height was not a preset
assert(aTable.height() == 10);
// The height was retained
for (auto &preset : presets)
{
aTable.setHeightPreset(preset);
assert(aTable.hasPresetHeight());
// The height was preset
assert(aTable.height() == preset);
// The height has the expected preset's value
assert(presets.getIdFor(aTable.presetHeight()) == preset.id);
// The height has the expected preset's identifier
assert(aTable.presetHeight().name == preset.name);
}
}
Presets Iterable by std::pair<HeightPreset::Id, HeightPreset>
If you wish not to use the iterator adapter, it can be removed. See below, and also try it out on godbolt.
#include <map>
class HeightPresets {
public:
using Id = HeightPreset::Id;
HeightPresets(std::initializer_list<HeightPreset> presets)
{
for (auto &preset : presets)
m_presets.insert({preset.id, preset});
}
auto &get(Id id) const { return m_presets.at(id); }
Id getIdFor(const HeightPreset &preset) const
{ return preset.id; }
auto begin() const { return m_presets.cbegin(); }
auto end() const { return m_presets.cend(); }
private:
using map_t = std::map<Id, HeightPreset>;
map_t m_presets;
};
And the test harness:
#include <cassert>
int main() {
const HeightPresets presets{
{1, 5, "A Fiver"},
{2, 10, "A Tenner"}
};
Table aTable;
assert(!aTable.height());
// The table has no height by default
aTable.setHeight(10);
assert(!aTable.hasPresetHeight());
// The height was not a preset
assert(aTable.height() == 10);
// The height was retained
for (auto &presetPair : presets)
{
auto &preset = presetPair.second;
aTable.setHeightPreset(preset);
assert(aTable.hasPresetHeight());
// The height was preset
assert(aTable.height() == preset);
// The height has the expected preset's value
assert(presets.getIdFor(aTable.presetHeight()) == preset.id);
// The height has the expected preset's identifier
assert(aTable.presetHeight().name == preset.name);
}
}
The value of height should be either an int or point to a struct which has additional data
This is called a sum type a.k.a. a tagged union.
C++ has a standard template for that: std::variant from the standard <variant> header. You probably want to use some smart pointer inside.
If you cannot use that header, re-implement it using some union inside your class (with another member discriminating that union, inspired by this), but don't forget to follow the rule of five.
I recommend reading more about C++. First, Programming -- Principles and Practice Using C++ and later the C++11 standard n3337.
I put the whole code on github: https://github.com/marianatuma/CG
I have a struct called point, declared in line.h, and a class line that has two points, start and end. EDIT: I didn't add it before, but Line inherits from GraphObj. graphObj.h:
class GraphObj {
private:
type t;
std::string name;
public:
GraphObj(type t, std::string name);
type getType();
std::string getName();
};
line.h:
#ifndef LINE_H
#define LINE_H
struct point {
double x;
double y;
};
class Line {
private:
point start;
point end;
public:
Line(type t, std::string name) : GraphObj(t, name) {};
void setStart(double x, double y);
void setEnd(double x, double y);
point getStart();
point getEnd();
};
#endif
line.cpp:
#include "line.h"
void Line::setStart(double x, double y) {
this->start.x = x;
this->start.y = y;
}
void Line::setEnd(double x, double y) {
this->end.x = x;
this->end.y = y;
}
point Line::getStart() {
return start;
}
point Line::getEnd() {
return end;
}
I always get a segmentation fault when I try accessing any of these points. I tried making them public, didn't work. I also tried using getters and it also didn't work. Here's how I'm initializing them:
The line is in a list of lines, called a display file, which will be used with cairo to draw them.
displayFile.h:
#ifndef DISPLAYFILE_H
#define DISPLAYFILE_H
#include <list>
#include "graphObj.h"
class DisplayFile {
private:
std::list<GraphObj*>* objectList;
std::list<GraphObj*>::iterator it;
int size;
public:
DisplayFile();
void add(GraphObj* g);
GraphObj* getNextObject();
void resetIterator();
int getSize();
};
#endif
displayFile.cpp:
#include "displayFile.h"
DisplayFile::DisplayFile() {
this->objectList = new std::list<GraphObj*>();
this->it = objectList->begin();
this->size = 0;
}
void DisplayFile::add(GraphObj* g) {
std::list<GraphObj*>::iterator tempIt;
tempIt = objectList->begin();
this->objectList->insert(tempIt, g);
this->size++;
}
GraphObj* DisplayFile::getNextObject() {
return *++it;
}
void DisplayFile::resetIterator() {
it = objectList->begin();
}
int DisplayFile::getSize() {
return size;
}
DisplayFile returns a GraphObj instead of the objectList, so it has to iterate through objectList by itself, hence the resetIterator (so when the main code is done traversing the list it will reset the iterator to the start of the list, but I'm not calling this method anywhere so far). The code in main.cpp where an instance of Line is used is below:
static void do_drawing(cairo_t *cr)
{
/* not using these right now
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 0.5);
*/
int size = df->getSize(); //df is the list
for(int i = 0; i < size; i++) {
Line* g = df->getNextObject();
point start = g->getStart();
}
}
The problem only starts when I try accessing the points, I can access other attributes from that line instance without problems. What am I doing wrong?
edit: I hope I've provided enough information, the main code is long and doesn't really have much to do with the line class, so I don't think it's relevant.
The problem lies with your list class.
class DisplayFile {
private:
std::list<GraphObj*>* objectList; // Why not just a list<GraphObj*>
std::list<GraphObj*>::iterator it; // Why use this?
int size; // WHY manually keep track of a STL container size?
public:
DisplayFile();
void add(GraphObj* g);
void resetIterator();
int getSize();
};
In your implementation file:
GraphObj* DisplayFile::getNextObject() {
return *++it;
}
As I already commented: this is the winner... Think about it, what if it already happens to be the last element on the list, and you ask for getNextObject()? Boom
I don't see why you couldn't just replace the entire DisplayFile class with a nice and plain std::list:
std::list<GraphObj*> objectList;
// I would also advice to change to smart pointers here
// for example: std::list<std::shared_ptr<GraphObj> > objectList;
// or std::list<std::unique_ptr<GraphObj> > objectList;
Then you would just use the STL methods to work with the list:
Add an item to the front: (for the sake of simplicity lets imagine that GraphObj has a default constructor)
GraphObj* g = new GraphObj();
objectList.push_front(g);
// If you change to smart pointers:
// objectList.push_front(std::make_shared<GraphObj>());
// or
// objectList.push_front(std::make_unique<GraphObj>());
Get the list size:
objectList.size();
Traverse the list:
for (std::list<GraphObj*>::const_iterator it = objectList.begin();
it != objectList.end();
++it)
{
point start = (*it)->getStart();
// or whatever you need to do here
}
Or with the much nicer range for:
for (const auto & graphObj : objectList)
{
point start = graphObj->getStart();
// or whatever you need to do here
}
Because you are not initilaizing the structs .
Change your constructor a bit
From this
Line(type t, std::string name) : GraphObj(t, name) {};
to
Line(type t, std::string name) : GraphObj(t, name) , start(),end() {};
This may help.
The problem might be coming from your getStart and getEnd because they return a point, which will create a copy(I think) of your start or end point, ie not using the point from line but copies. This usually isn't a big deal but if you want to change an x or y value and have the new value stick you'll need a reference to the original points x and y values.
Try this, change
point getStart();
point getEnd();
into
point *getStart() { return &start; }
point *getEnd() { return &end; }
and in your do_drawing(cairo_t *cr) change
point start = g->getStart();
to
point *start = g->getStart();
start->x = value; // or
double value = start->x; // or however you want to use start
I simplified my problem with a simple example : immagine I manage a collection of elements std::vector<Element>, each element having several members :
struct Element
{
public:
double foo;
double bar;
};
Then, I want to define an abstract class BarEvaluator, for algorithms computing the values of b from the values of a. My first idea is the following :
class BarEvaluator
{
public:
virtual void evaluate(std::vector<Element>& elements) const = 0;
};
From that, I can implement several algorithms, for example, an algorithme computing the bar values as the square of the foo values :
class SqrBarEvaluator
{
public:
virtual void evaluate(std::vector<Element>& elements) const
{
for(unsigned long int i = 0; i < elements.size(); ++i)
elements[i].bar = elements[i].foo * elements[i].foo;
}
};
This is working well. But I think it's not a really good architecture, because my algorithm is also able to modify the foo values. I don't want that.
Then I would like to be able to give my collection to the algorithm with a kind of "filter" allowing to modify only the bar variable and not the foo variable in each element. Is it possible with C++98 ? I have no idea how to do that.
Remark 1 : I don't want to do that with public or private in Element. You can immagine I also want to create algorithms FooEvaluator computing foo values from bar values, with writing access to foo and not to bar.
Remark 2 : The algorithm can require all the collection to compute each value.
Maybe you should pull the loop out of the interface.
class BarEvaluator
{
public:
virtual double evaluate(const Element& element) const = 0;
};
class SqrBarEvaluator
{
public:
virtual double evaluate(const Element& element) const
{
return element.foo * element.foo;
}
};
Then you call it like this:
std::vector<Element> elements;
...
for (std::vector<Element>::iterator it = elements.begin(); it != elements.end(); ++it) {
it->bar = barEvaluator.evaluate(*it);
}
You can use a wrapper:
class BarElementWrapper
{
public:
BarElementWrapper(Element& e) : elem(e) { }
double getFoo() { return elem.foo; }
void setBar(double b) { elem.bar = b; }
private:
Element& elem;
}
And then your algorithm receives a collection of BarElementWrapper.
I have a little conceptual problem. I have different classes representing the geometric data of an edge depending what type of edge it is. For Example the class for a straight line and a circle:
class Line{
private:
double[3] startPoint;
double[3] endPoint;
public:
//getter and setter and some other functions such as equals
}
class Circle{
private:
double[3] center;
double[3] planeNormal;
double radius;
public:
//getter and setter and some other functions such as equals
}
Now I need a class Edge which stores the type of the edge and the fitting geometric data.
In the end the Edge has to be stored in a std::vector<Edge> edges; The Problem is that I do not know the type before runtime, because I am analysing the boundary representation of CAD parts which can have various types of edges.
class Edge{
private:
EdgeType type;
GeometricData data;
public:
//...
}
So how should I design my class Edge and espacially GeometricData which has to store either a Line-object, a Circle-object or another geometric object, so that I can go back from GeometricData to Line, Circle or whatever geometric class it may be.
I tried polymorphism with GeometricData as base class, but the derived
classes are too different, since things like B-Splines are also
included.
I also tried GeometricData as void* and a template-approach
for the set- and get-methode, but with that I have problems
storing the data and not only the pointer, because of the lifetime
of the objects (I have to analyse the BRep recursivly).
I would also appreciate suggestions that may change the whole concept of the geometric representations, as long as I can access the type-fitting data such as startPoint of a straight line or radius of a circle using the edges-vector.
EDIT:
Thanks for the fast responses. I decided to use suszterpatt suggestion including some of my templates and changing my std::vector<Edge> to std::vector<shared_ptr<Edge>> as TAS mentioned. Now it looks like this:
#include "stdafx.h"
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
using namespace std;
enum EdgeType{
LINE = 100,
CIRCLE
};
//Basis
class GeometricData {
private:
public:
virtual string toXMLString() = 0;
};
class Line : public GeometricData{
//less code just for illustration
private:
double d1;
public:
double getD1() { return d1; }
void setD1(double d1) { this->d1 = d1;}
virtual string toXMLString() {
stringstream s;
s << "d1=\"" << d1 <<"\"";
return s.str();
}
};
class Circle : public GeometricData{
private:
double d2;
public:
double getD2() { return d2; }
void setD2(double d2) { this->d2 = d2;}
virtual string toXMLString() {
stringstream s;
s << "d2=\"" << d2<<"\"";
return s.str();
}
};
class Edge{
private:
EdgeType t;
GeometricData* d;
public:
Edge () { d = 0;}
~Edge () {if (d) {delete d; d=0;}}
template <typename T> int setGeomData (T data) {
static_assert(
is_same<T,Line*>::value ||
is_same<T,Circle*>::value,
"EdgeGeometryType is not supported");
GeometricData* buffer = data;
//set type corresponding to thethis->data given= data
if(is_same<T,Line*>::value){
this->t = LINE;
Line* lb = dynamic_cast<Line*>(buffer);
Line* l = new Line(*lb);
this->d = l;
}else if (is_same<T,Circle*>::value){
this->t = CIRCLE;
Circle* cb = dynamic_cast<Circle*>(buffer);
Circle* c = new Circle(*cb);
this->d = c;
}else{// this case should not occure because of the static_assert
return -1;
}
return 0;
};
template <typename T> T getGeomData () {
static_assert(
is_same<T,Line*>::value ||
is_same<T,Circle*>::value,
"EdgeGeometryType is not supported");
if ((this->t == LINE && is_same<T,Line*>::value) ||
(this->t == CIRCLE && is_same<T,Circle*>::value))
{
return dynamic_cast<T>(this->d);
}else{
return NULL;
}
};
EdgeType getType(){ return t; }
//void setType(EdgeType t) { this->t = t; } not needed
GeometricData* getData(){return d;}
};
class Model {
private:
vector <shared_ptr<Edge>> edges;
public:
Model(){}
vector <shared_ptr<Edge>> getEdges(){ return edges; }
void addEdge (Edge* e) {edges.push_back(shared_ptr<Edge>(e));}
shared_ptr<Edge> getEdge(int i ){ return edges.at(i); }
};
// Functions
void foo2 (Edge* e){
Line* l = new Line;
l->setD1(0.1);
e->setGeomData<Line*>(l);
//e->setType(LINE); not needed
delete l;
}
void foo1 (Edge* e){
Circle c;
c.setD2(0.2);
e->setGeomData<Circle*>(&c);
//e->setType(CIRCLE); not needed
}
void foo (Model* mdl){
Edge* e1 = new Edge;
Edge* e2 = new Edge;
foo1(e1);
foo2(e2);
mdl->addEdge(e1);
mdl->addEdge(e2);
}
int _tmain(int argc, _TCHAR* argv[])
{
Model mdl;
int i;
foo(&mdl);
cout << "Edge 1: " << mdl.getEdge(0)->getData()->toXMLString() << endl;
cout << "Edge 2: " << mdl.getEdge(1)->getData()->toXMLString() << endl;
for (i = 0; i<2; i++){
switch (mdl.getEdge(i)->getType()){
case LINE: {
Line* ld = (mdl.getEdge(i)->getGeomData<Line*>());
cout << "Line (templated get): " << ld->getD1() << endl;
}break;
case CIRCLE:{
Circle* cr = (mdl.getEdge(i)->getGeomData<Circle*>());
cout << "Circle (templated get): "<< cr->getD2() << endl;
}break;
}
}
return 0;
}
There's a number of solutions. The one that seems to fit best is Boost.Variant; define your Line and Circle classes as you showed, then make GeometricData a typedef of variant<Line, Circle>, and you'll be able to store an instance of either one in there. When you want to go back from a GeometricData to the actual object stored, you can perform a cast, or you can write a so-called visitor. A visitor is just a class specifying an action for each possible type, and then boost::apply_visitor can be used to select the right action based on what is stored.
Example (using vectors for simpler notation):
struct Line {
Vector3d startPoint, endPoint;
};
struct Circle {
Vector3d center;
float radius;
};
using GeometricData = boost::variant<Line, Circle>;
struct MidpointVisitor : boost::static_visitor<Vector3d> const {
Vector3d operator()(Line const& line) {
return (line.startPoint + line.endPoint)/2;
}
Vector3d operator()(Circle const& circle) const {
return circle.center;
}
};
void foo() {
GeometricData data;
// ...
auto midpoint = boost::apply_visitor(MidpointVisitor{}, data);
// ...
}
A less type-strict solution is Boost.Any, but I don't see any advantages for this case. Even if you did need another option, you'd probably want to specify that explicitly.
I suspect your solution using void* (or using a common base class and RTTI) could be made to work using smart pointers. However, the only advantages I can see are faster compilation and less awful compiler error messages, while you end up having to bother with dynamic allocation and can't have visitors.
You could also roll your own union for this, effectively implementing something along the lines of Variant. That would involve making sure you get construction, destruction and alignment all correct, and don't trigger some obscure case of undefined behaviour. If that's not a problem for you and you really don't want to use a library, it's an option, but it is very much reinventing the wheel.
I would say polymorphism where perhaps the shared interface looks something like this:
class Edge
{
enum EdgeType
{
CIRCLE,
LINE
};
EdgeType GetType();
}
Then in a switch statement somewhere you could do something like:
switch (myEdge.GetType())
{
case Edge::EdgeType::CIRCLE:
auto myCircle = (Circle)myEdge;
// do things specific to circle
break;
case Edge::EdgeType::LINE:
auto myLine = (Line)myEdge;
// do things specific to line
break;
}
That being said, I would try to use polymorphism as much as possible over the switch statement, but the above interface gives you the option of having a function using edges contain the logic for doing different things based on type.
I'm not sure I fully understand the problem you're trying solve but from reading and understanding the question, I'd say look into serialization
You could maybe create a global array type variable, store the objects you need, serialize it an deserialize it when you need to use it.
I have a code that looks something like:
struct Data { int value; };
class A {
public:
typedef std::deque<boost::shared_ptr<Data> > TList;
std::back_insert_iterator<TList> GetInserter()
{
return std::back_inserter(m_List);
}
private:
TList m_List;
};
class AA {
boost::scoped_ptr<A> m_a;
public:
AA() : m_a(new A()) {}
std::back_insert_iterator<A::TList> GetDataInserter()
{
return m_a->GetInserter();
}
};
class B {
template<class OutIt>
CopyInterestingDataTo(OutIt outIt)
{
// loop and check conditions for interesting data
// for every `it` in a Container<Data*>
// create a copy and store it
for( ... it = ..; .. ; ..) if (...) {
*outIt = OutIt::container_type::value_type(new Data(**it));
outIt++; // dummy
}
}
void func()
{
AA aa;
CopyInterestingDataTo(aa.GetDataInserter());
// aa.m_a->m_List is empty!
}
};
The problem is that A::m_List is always empty even after CopyInterestingDataTo() is called. However, if I debug and step into CopyInterestingDataTo(), the iterator does store the supposedly inserted data!
update:
I found the culprit. I actually have something like:
class AA {
boost::scoped_ptr<A> m_a;
std::back_insert_iterator<A::TList> GetDataInserter()
{
//return m_a->GetInserter(); // wrong
return m_A->GetInserter(); // this is the one I actually want
}
// ..... somewhere at the end of the file
boost::scoped_ptr<A> m_A;
};
Now, which answer should I mark as answer?
Really sorry for those not chosen, but you guys definitely got some up-votes : )
The short answer is yes, back_insert_iterator is safe to pass by value. The long answer: From standard 24.4.2/3:
Insert iterators satisfy the
requirements of output iterators.
And 24.1.2/1
A class or a built-in type X satisfies
the requirements of an output iterator
if X is an Assignable type (23.1) ...
And finally from Table 64 in 23.1:
expression t = u
return-type T&
post-condition t is equivalent to u
EDIT: At a glance your code looks OK to me, are you 100% certain that elements are actually being inserted? If you are I would single step through the code and check the address of the aa.m_a->m_List object and compare it to the one stored in outIt in CopyInterestingDataTo, if they're not the same something's fishy.
The following code, which compiles, prints "1", indicating one item added to the list:
#include <iostream>
#include <deque>
#include "boost/shared_ptr.hpp"
#include "boost/scoped_ptr.hpp"
struct Data {
int value;
Data( int n ) : value(n) {}
};
struct A {
typedef std::deque<boost::shared_ptr<Data> > TList;
std::back_insert_iterator<TList> GetInserter()
{
return std::back_inserter(m_List);
}
TList m_List;
};
struct AA {
boost::scoped_ptr<A> m_a;
AA() : m_a(new A()) {}
std::back_insert_iterator<A::TList> GetDataInserter()
{
return m_a->GetInserter();
}
};
struct B {
template<class OutIt>
void CopyInterestingDataTo(OutIt outIt)
{
*outIt = typename OutIt::container_type::value_type(new Data(0));
outIt++; // dummy
}
int func()
{
AA aa;
CopyInterestingDataTo(aa.GetDataInserter());
return aa.m_a->m_List.size();
}
};
int main() {
B b;
int n = b.func();
std::cout << n << std::endl;
}