How to do deep copy of graph property wrapped in std::shared_ptr<> in boost graph?

46 Views Asked by At

I have a boost graph with custom properties.
I want to make a copy of it. I tried it by following way.

using BGType = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
                                     // Vertex Properties...
                                     vertexProps,
                                     // Edge Propereties...
                                     edgeProps,
                                     // Graph Properties
                                     graphProps>;

vertexProps.h

 class vertexProps {
       public:
        explicit vertexProps(const std::string *moduleName = nullptr, const std::string *name = nullptr,
                                long refPtr = 0 )
         : _refPtr(refPtr),
        {
            _moduleName = moduleName ? *moduleName : "";
            _name = name ? *name : "";
        };
       std::string _moduleName;
       std::string _name;
       BGType *_subGraph = nullptr;
       BGType *_graph = nullptr;
}

struct CustomVertexCopy {
    BGType const &g1;
    BGType &g2;
    void operator()(BGType::vertex_descriptor v1, BGType::vertex_descriptor v2) const
    {
        schVertexProps const &p1 = g1[v1];
        schVertexProps &p2 = g2[v2];
        p2._subGraph = p1._subGraph;
        p2._graph = p1._graph;
        p2._moduleName = p1._moduleName;
        p2._name = p1._name;
    }
};

edgeProps.h

class edgeProps {
   public:
    explicit edgeProps(std::string name = "")
        : _name(name){};
    std::string _name;
};

struct CustomEdgeCopy {
    BGType const &g1;
    BGType &g2;

    void operator()(BGType::edge_descriptor e1, BGType::edge_descriptor e2) const { g2[e2]._name = g1[e1]._name; }
};

schGraphProps.h

class schGraphProps {
   public:
    explicit schGraphProps(std::string *name = nullptr) { _name = name ? *name : ""; };

    std::string _name;
    std::map<std::string, std::vector<std::string>> _altNames;
    std::map<std::string, schSymbol> _modSymbol;
    std::shared_ptr<vertexProps> _vertexPtr = nullptr; // ****want to deep copy this property*******
}

someFunction.cpp

OnClick(BGType* bgNew)
{
   // some code
  BGType* oldBg = new BGType;
  boost::copy_graph(
                  *bgNew, *oldBg,
                  boost::vertex_copy(CustomVertexCopy{*bgNew, *oldBg}).edge_copy(CustomEdgeCopy{*bgNew, *oldBg}));
  boost::get_property(*oldBg) = boost::get_property(*bgNew);
  // Copying graph properties
  DeepCopyOfBG(bgNew, oldBg);
}


    DeepCopyOfBG(BGType *bGraph, BGType *oldBg)
    {
        boost::copy_graph(
          *bGraph, *oldBg,
          boost::vertex_copy(CustomVertexCopy{*bGraph, *oldBg}).edge_copy(CustomEdgeCopy{*bGraph, *oldBg}));
    
        boost::get_property(*oldBg) = boost::get_property(*bGraph);   

        // Deep copy _altNames map
        for (auto entry : (*bGraph)[boost::graph_bundle]._altNames) {
            std::vector<std::string> deepCopyValues(entry.second.begin(), entry.second.end());
            (*oldBg)[boost::graph_bundle]._altNames[entry.first] = deepCopyValues;

      // Deep copy _modSymbol map
        for (auto entry : (*bGraph)[boost::graph_bundle]._modSymbol) {
            (*oldBg)[boost::graph_bundle]._modSymbol[entry.first] = entry.second;
        }
      
      // Want to copy std::shared_ptr<schVertexProps> _vertexPtr = nullptr

}

I want to deep copy of following property but could not do it.

std::shared_ptr<vertexProps> _vertexPtr = nullptr

I am stuck because of shared_ptr concept.

How to do deep copy of shared_ptr concept ?

1

There are 1 best solutions below

0
sehe On

My first response is: don't use shared_ptr when you want value semantics.

That's precisely why I showed you value_ptr in my previous answer. That way, Rule-Of-Zero applies and the language automatically behaves exactly like you want it to.

If you must have shared pointers and still have them deep-cloning, you can copy that value_ptr and make it contain a shared_ptr instead of unique_ptr.

If you don't want that, or have some other "force-of-nature" reason why you can't (???) you can always ... just copy the object manually. In general, with any unique-pointer behaves conceptually like a pointer.

{
    // clone a pointer
    T* p = new T();
    T* clone = new T(*p);
}
{
    // clone a unique_ptr
    unique_ptr<T> p = std::make_unique<T>();
    unique_ptr<T> clone = std::make_unique<T>(*p);
}
{
    // clone a shared_ptr
    shared_ptr<T> p = std::make_shared<T>();
    shared_ptr<T> clone = std::make_shared<T>(*p);
}

I hope you spot the pattern? A deep-copy is basically just invoking the copy-constructor of *p: whatever p points to. It doesn't even matter what kind of (smart) pointer you have.

{
    // clone a whatever_ptr
    whatever_ptr<T> p = std::make_whatever<T>();
    whatever_ptr<T> clone = std::make_whatever<T>(*p);
}

The Specific Question Code

Regarding the code, you have more problems, because you're running copy_graph multiple times on the same objects. You also run the following assignment twice:

    boost::get_property(*oldBg) = boost::get_property(*bGraph);   

That assignment ALREADY copies all the graph properties (the schGraphProps object).

Now, C++ allows you to customize the behavior of such a copy, by supplying a user-defined copy constructor. In your case you can e.g. make it:

 schGraphProps(schGraphProps const& other)
     : _name(other._name)
     , _altNames(other._altNames)
     , _modSymbol(other._modSymbol)
     , _vertexPtr(other._vertexPtr ? std::make_shared<vertexProps>(*other._vertexPtr) : nullptr) {}

Remembering to fix the pointer misuse in the constructor as I've shown you several times before, you get the following class:

class schGraphProps {
   public:
     explicit schGraphProps(std::string name = {}) : _name(std::move(name)) {}
     schGraphProps(schGraphProps const& other)
         : _name(other._name)
         , _altNames(other._altNames)
         , _modSymbol(other._modSymbol)
         , _vertexPtr(other._vertexPtr ? std::make_shared<vertexProps>(*other._vertexPtr) : nullptr) {}

     std::string                                     _name;
     std::map<std::string, std::vector<std::string>> _altNames;
     std::map<std::string, schSymbol>                _modSymbol;
     std::shared_ptr<vertexProps>                    _vertexPtr = nullptr;
};

Live Demo

I'm not in the mood to create a full demo again, because I keep fixing the bugs and unncessary complexity that you keep re-introducing. So I'll focus on just the graph properties.

ASIDE In fact, this might help you understand the behaviour of the C++ type system better. Even as part of complicated data structures like BGL's adjacency_list all the parts are still "just C++ types". Understanding the C++ type system is a must if you're writing code in C++.

Live On Coliru

#include <cassert>
#include <map>
#include <memory>
#include <string>
#include <vector>

struct vertexProps{};
struct schSymbol{};

class schGraphProps {
   public:
     explicit schGraphProps(std::string name = {}) : _name(std::move(name)) {}
     schGraphProps(schGraphProps const& other)
         : _name(other._name)
         , _altNames(other._altNames)
         , _modSymbol(other._modSymbol)
         , _vertexPtr(other._vertexPtr ? std::make_shared<vertexProps>(*other._vertexPtr) : nullptr) {}

     std::string                                     _name;
     std::map<std::string, std::vector<std::string>> _altNames;
     std::map<std::string, schSymbol>                _modSymbol;
     std::shared_ptr<vertexProps>                    _vertexPtr = nullptr;
};

#include <iostream>
int main() {
    auto v = std::make_shared<vertexProps>();

    schGraphProps graphProps1{"hello"};
    graphProps1._vertexPtr = v;

    // clone it!
    auto graphProps2 = graphProps1; // uses copy constructor

    std::cout << "Original v:\t" << v.get() << "\n";
    std::cout << "graphProps1._vertexPtr:\t" << graphProps1._vertexPtr.get() << "\n";
    std::cout << "graphProps2._vertexPtr:\t" << graphProps2._vertexPtr.get() << "\n";
    assert(graphProps1._vertexPtr == v);
    assert(graphProps2._vertexPtr != v); // deep copied
}

Prints e.g.

Original v: 0xbce030
graphProps1._vertexPtr: 0xbce030
graphProps2._vertexPtr: 0xbce050

and all the asserts pass, proving the deep-copy.