0

I'm using an implementation of a boost graph with a boost::shared_ptr<Obj> as an edge attribute. I have two class Obj and Obj2 such that:

class Obj{
    public:
    Obj(){};
    virtual func(){std::cout << "Obj func " << std::endl};
};

class Obj2: public Obj{
    public:
    Obj2(){};
    virtual func(){std::cout << "Obj2 func " << std::endl};
};

I'm adding edges in the graph using a (simplified) function such as:

void addEdge(Vertex index, Vertex index2, const boost::shared_ptr<Obj>& edgeAttribute){
    out = boost::add_edge(index, index2, edgeAttribute, graph).first;
}

That I call using :

Obj2* obj = new Obj2();
boost::shared_ptr<Obj2> obj_ptr(obj);
addEdge(index, index2, obj_ptr);

However later in my code when picking up the edge attribute by doing edgeAttr = graph[edge] and then calling the function edgeAttr->func(), I call Obj's func and not Obj2's.

As far as I understand, it means that somewhere my Objects are being sliced. Is this short example I gave supposed to slice my object when using boost:shared_ptr and BGL, or is it some other implementation problem of my own like the initialisation?

Malcolm
  • 662
  • 7
  • 29
  • Don’t use const ref of smart pointers. Use a raw pointer or a reference to the underlying type. [Cpp core guidelines](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-smartptrparam) –  Dec 22 '17 at 19:17
  • 1
    Delete `Obj`'s copy and move ctor/assign and see if boost is using them. – Yakk - Adam Nevraumont Dec 22 '17 at 19:22
  • @manni66 but I want to add the shared ptr to the edge and not the raw pointer nor do I want to copy the object since I need it to be a pointer to be able to use virtual functions. – Malcolm Dec 22 '17 at 19:30
  • @Yakk sorry I'm not sure what you meant so I clarified how I am declaring my Obj2 and calling the function – Malcolm Dec 22 '17 at 19:37
  • 2
    @Malcolm Add `Obj( Obj const& )=delete; Obj& operator=(Obj const&)=delete;` to `Obj` declaration. This *delete*s the copy and move ctors and assign operations on `Obj`. It then becomes impossible to "slice" an `Obj2` into an `Obj`; if attempted, you'll get a compile-time error. So after doing this, rebuild, and see if boost is trying to slice, and where. If it is not, then you have proven you have misunderstood the problem entirely. If it is, you can see where it happens. – Yakk - Adam Nevraumont Dec 22 '17 at 19:41
  • _but I want to add the shared ptr to the edge_ then don’t use a const ref. –  Dec 22 '17 at 19:46
  • @Yakk There was no compile time error so no slicing indeed as the solution shows. Thanks, very helpful tip for next time I'll wonder if an object gets sliced. – Malcolm Dec 23 '17 at 00:35

1 Answers1

1

No the graph model does not slice any attribute (note, that "attribute" is actually called a bundled property).

In fact, it does never copy the Obj or Obj2 because it just copies the shared_ptr<> leading to shared ownership, as opposed to copying a property.

Let's Demonstrate How It Works

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <iostream>

class Obj {
  public:
    Obj(){};
    virtual void func(std::ostream& os) { os << "Obj  func\n"; };
};

class Obj2 : public Obj {
  public:
    Obj2(){};
    virtual void func(std::ostream& os) { os << "Obj2 func\n"; };
};
// I'm adding edges in the graph using a (simplified) function such as:

using Graph = boost::adjacency_list<boost::vecS, boost::vecS,
      boost::directedS, boost::no_property, boost::shared_ptr<Obj> >;
using Vertex = Graph::vertex_descriptor;

struct Program {
    Program() : _graph(10) {}

    void addEdge(Vertex index, Vertex index2, const boost::shared_ptr<Obj> &edgeAttribute) {
        auto out = boost::add_edge(index, index2, edgeAttribute, _graph).first;
    }

    void sample_edges() {
        Obj2 *obj = new Obj2();
        boost::shared_ptr<Obj2> obj_ptr(obj);

        addEdge(1, 2, boost::make_shared<Obj2>());
        addEdge(2, 3, boost::make_shared<Obj >());
        addEdge(3, 4, boost::make_shared<Obj2>());
        addEdge(4, 5, boost::make_shared<Obj >());
        addEdge(5, 6, boost::make_shared<Obj2>());
        addEdge(6, 7, boost::make_shared<Obj >());
    }

    void debug_dump() const {
        for (auto ed : boost::make_iterator_range(boost::edges(_graph))) {
            _graph[ed]->func(std::cout << "Edge " << ed << ": ");
        }
    }

  private:
    Graph _graph;
};

int main() {
    std::cout << "Demo edges:\n";
    Program demo;
    demo.sample_edges();
    demo.debug_dump();

    std::cout << "Copied edges:\n";
    // copy the whole shebang
    Program clone = demo;
    clone.debug_dump();
}

Prints:

Demo edges:
Edge (1,2): Obj2 func
Edge (2,3): Obj  func
Edge (3,4): Obj2 func
Edge (4,5): Obj  func
Edge (5,6): Obj2 func
Edge (6,7): Obj  func
Copied edges:
Edge (1,2): Obj2 func
Edge (2,3): Obj  func
Edge (3,4): Obj2 func
Edge (4,5): Obj  func
Edge (5,6): Obj2 func
Edge (6,7): Obj  func
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    This answer demonstrates the power of self-contained sample code: http://kera.name/articles/2013/10/nobody-writes-testcases-any-more/ – sehe Dec 22 '17 at 20:21