0

I am new to using smart pointers in C++ and my current issue is that I am converting C code to C++ (C++11/14/17) and I am having some problems understanding using shared_ptr with a pointer to pointer. I have derived a toy example which I believe illustrates the problem

Following is the header file

#include <memory>
using std::shared_ptr;

struct DataNode
{
  shared_ptr<DataNode> next;
} ;


 struct ProxyNode
 {
   shared_ptr<DataNode> pointers[5];
 } ;


 struct _test_
 {
   ProxyNode** flane_pointers;
  };

And the actual code test.cpp

#include <stdint.h>
#include "test.h"


shared_ptr<DataNode> newNode(uint64_t key);

shared_ptr<ProxyNode> newProxyNode(shared_ptr<DataNode> node);

int main(void)
{
  // Need help converting this to a C++ style calling
  ProxyNode** flane_pointers = (ProxyNode**)malloc(sizeof(ProxyNode*) * 100000);
  // Here is my attempt (incomplete)
  ProxyNode** flane_pointers = new shared_ptr<ProxyNode> ?
  shared_ptr<DataNode> node = newNode(1000);
  flane_pointers[1] = newProxyNode(node)
} 

shared_ptr<ProxyNode> newProxyNode(shared_ptr<DataNode> node) 
{

  shared_ptr<ProxyNode> proxy(new ProxyNode());
 return proxy;
}

shared_ptr<DataNode> newNode(uint64_t key) 
{
 shared_ptr<DataNode> node(new DataNode());
 return node;
}

I am getting these compiler errors -

 test.cpp: In function ‘int main()’:
 test.cpp:12:42: error: cannot convert ‘std::shared_ptr<ProxyNode>’ to  ‘ProxyNode*’ in assignment
  flane_pointers[1] = newProxyNode(node)

Compiled with

  g++ -c -g test.h test.cpp

g++ version is 7.3.0 (on Ubuntu 18)

I need help converting the C style malloc allocation with a C++ style calling for a pointer to a pointer and then how to fix the compiler errors. My apologies if it looks like I am missing something obvious.

gansub
  • 1,164
  • 3
  • 20
  • 47

1 Answers1

2

If this ProxyNode** flane_pointers; is supposed to be a matrix of ProxyNode then the most simple approach would be to create vector of vectors containing ProxyNode like this:

struct DataNode
{
    std::shared_ptr<DataNode> next;
};


struct ProxyNode
{
    // here instead of C style array you can use std::array
    std::shared_ptr<DataNode> pointers[5];
};

TEST(xxx, yyy) {
    std::vector<std::vector<ProxyNode> > matrixOfProxyNodes;
    // insert empty vector of ProxyNode
    matrixOfProxyNodes.push_back(std::vector<ProxyNode>());
    // insert ProxyNode to the first vector of ProxyNodes
    matrixOfProxyNodes[0].push_back(ProxyNode());
    // insert shared_ptr to DataNode in position 0, 0 of matrix of ProxyNodes
    matrixOfProxyNodes[0][0].pointers[0] = std::make_shared<DataNode>();
}

If you really need the pointer to pointer (but I really don't see the purpose for that) you can do:

    // create shared pointer to shared pointer to ProxyNode
    // and initialize it to nullptr
    std::shared_ptr<std::shared_ptr<ProxyNode> > ptr2ptr2ProxyNode(nullptr);
    // dynamically create new shared_ptr to ProxyNode
    ptr2ptr2ProxyNode.reset(new std::shared_ptr<ProxyNode>(nullptr));
    // dynamically create ProxyNode
    ptr2ptr2ProxyNode->reset(new ProxyNode());
    (*ptr2ptr2ProxyNode)->pointers[0] = std::make_shared<DataNode>();

Note that std::shared_ptr does not behave exactly as raw pointer, for example you shouldn't allocate the memory for shared_ptr with operator new[]. Here is explained why.

Also I see that you probably want to implement some kind of list or other chain. std::shared_ptr can suffer from cyclic dependency, so be careful and use std::weak_ptr when needed.

pptaszni
  • 5,591
  • 5
  • 27
  • 43
  • flane_pointers is not a matrix of ProxyNode(s) but a 1-D array of it yes. – gansub Apr 12 '19 at 16:28
  • I do not think I can use vectors because the legacy code needs to use indices to get elements. So an array based approach is necessary. Can you explain what ptr2ptr2ProxyNode is ? It is not clear. – gansub Apr 13 '19 at 01:05
  • For sure you can use `std::vector`. They are compatible with your legacy code using indices, offsets, addresses of elements and so on. This is explained [here](https://en.cppreference.com/w/cpp/container/vector). – pptaszni Apr 15 '19 at 07:16
  • Thanks. I will give it a try. Is that a best practice though ? To use vector and search with indices, offsets and addresses ? – gansub Apr 15 '19 at 08:08
  • 1
    The best practice is to define a class with the interface that serves exactly your purpose (but you need to understand that purpose first, which is not always obvious when you translate code from C). For the beginning it is a good practice to use `std::vector` instead of C-style `T* arr;` array. `std::vector` provides you the interface to access elements with square brackets operator `elem[i];` iterators (that have pointer syntax) or even C-style access to underlying [data](https://en.cppreference.com/w/cpp/container/vector/data) – pptaszni Apr 15 '19 at 09:17
  • Thank you. I have accepted your answer and that concludes this discussion. – gansub Apr 15 '19 at 09:43