I have a graph structure where vertices can have several types of edges.
Vertex types are polymorphic and they must be able to "classify" edges depending on their type and store them accordingly, but I want to be able to retrieve all edges at "base level" without knowing how they are stored.
I'm trying to achieve this using boost::adaptors::transformed, boost::range::join and boost::any_range.
A example of this:
#include <iostream>
#include <sstream>
#include <set>
#include <memory>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/join.hpp>
#include <boost/range/any_range.hpp>
// forward declarations
class BaseVertex;
class DerivedVertex1;
class DerivedVertex2;
struct TransformCaster
{
typedef std::shared_ptr<BaseVertex> result_type;
std::shared_ptr<BaseVertex> operator()(std::shared_ptr<DerivedVertex1> d1) const { return std::static_pointer_cast<BaseVertex>(d1); }
std::shared_ptr<BaseVertex> operator()(std::shared_ptr<DerivedVertex2> d2) const { return std::static_pointer_cast<BaseVertex>(d2); }
};
class BaseVertex
{
public:
BaseVertex(size_t id): id_(id){}
virtual ~BaseVertex () {}
virtual std::stringstream name()
{std::stringstream ss; ss << "Base " << id_; return ss;}
virtual boost::any_range<std::shared_ptr<BaseVertex>,boost::forward_traversal_tag> getEdges() const = 0;
protected:
size_t id_;
};
class DerivedVertex1 : public BaseVertex
{
public:
DerivedVertex1(size_t id): BaseVertex(id){}
virtual std::stringstream name()
{std::stringstream ss; ss << "Derived1 " << id_; return ss;}
void addEdge1(const std::shared_ptr<DerivedVertex1>& rel)
{ relations_1_.insert(rel); }
void addEdge2(const std::shared_ptr<DerivedVertex2>& rel)
{ relations_2_.insert(rel); }
virtual boost::any_range<std::shared_ptr<BaseVertex>,boost::forward_traversal_tag> getEdges() const
{
// These are temporary, right?
auto range1 = relations_1_ | boost::adaptors::transformed(TransformCaster());
auto range2 = relations_2_ | boost::adaptors::transformed(TransformCaster());
auto joined_range = boost::range::join(range1, range2);
// This is wrapping temporary transformed ranges?
boost::any_range<std::shared_ptr<BaseVertex>,boost::forward_traversal_tag> poly_range(joined_range);
return poly_range;
}
private:
std::set<std::shared_ptr<DerivedVertex1>> relations_1_;
std::set<std::shared_ptr<DerivedVertex2>> relations_2_;
};
class DerivedVertex2 : public BaseVertex
{
public:
DerivedVertex2(size_t id): BaseVertex(id){}
virtual std::stringstream name()
{std::stringstream ss; ss << "Derived2 " << id_; return ss;}
void addEdge1(const std::shared_ptr<DerivedVertex1>& rel)
{ relations_1_.insert(rel); }
void addEdge2(const std::shared_ptr<DerivedVertex2>& rel)
{ relations_2_.insert(rel); }
virtual boost::any_range<std::shared_ptr<BaseVertex>,boost::forward_traversal_tag> getEdges() const
{
// These are temporary, right?
auto range1 = relations_1_ | boost::adaptors::transformed(TransformCaster());
auto range2 = relations_2_ | boost::adaptors::transformed(TransformCaster());
auto joined_range = boost::range::join(range1, range2);
// This is wrapping temporary transformed ranges?
boost::any_range<std::shared_ptr<BaseVertex>,boost::forward_traversal_tag> poly_range(joined_range);
return poly_range;
}
private:
std::set<std::shared_ptr<DerivedVertex1>> relations_1_;
std::set<std::shared_ptr<DerivedVertex2>> relations_2_;
};
int main()
{
std::shared_ptr<DerivedVertex1> derived1 = std::make_shared<DerivedVertex1>(0);
std::shared_ptr<DerivedVertex2> derived2 = std::make_shared<DerivedVertex2>(1);
derived1->addEdge1(derived1); // self pointing edge
derived1->addEdge2(derived2); // edge towards other
std::shared_ptr<BaseVertex> base = std::static_pointer_cast<BaseVertex>(derived1);
// segfault on getEdges()
for(auto& e : base->getEdges())
std::cout << e->name().str() << std::endl;
return 0;
}
This is giving me segfault at getEdges() evaluation. As I understand, any_range is keeping a reference to temporary variables boost::adaptors::tranformed. I tried to keep the transform adaptors ranges as class member variables but it didn't work.
Is there a correct way to achieve this with any_range? Could transform_iterator / any_iterator types be the answer or would I face similar issues?