1

I'm hiding the implementation details of third party C++ graph library (LEMON graph library) from my project by doing the following :

API.h file

class Node;
using NodeId = unique_ptr<Node>;
class ProgramGraph {
private:
  class ProgramGraphImpl;
  ProgramGraphImpl* pimpl;
public:
  ProgramGraph();
  NodeId add_instr_node(int opcode, const string& desc, BlockId& parent);
  ...
};

API.cpp file

class Node {
public:
  ListDigraph::Node node;
  Node(ListDigraph::Node node) : node(node) {}
  ~Node() {}
};

class ProgramGraphImpl {
  NodeId add_instr_node(int opcode, const string& desc, BlockId& parent) {
    ListDigraph::Node n = pg.addNode(); // this is from the 3rd party library
    unique_ptr<Node> uq = make_unique<Node>(Node(n));
    node_map[n] = Instr(opcode, desc, parent); // Instr is another class defined in API.cpp
    return uq;
  }
  ...
};

NodeId ProgramGraph::add_instr_node(int opcode, const string& desc, BlockId& parent) {
  return pimpl->add_instr_node(opcode, desc, parent);
}

I am using the API like this in some_other_file.cpp :

#include "API.h"
...
    const NodeId& currMI = pg.add_instr_node(opcode, opcode_desc, block_id);
...

When I compile it, I get the error below (error: invalid application of 'sizeof' to an incomplete type program_graph::Node) - any ideas ? Thank you.

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include/c++/v1/__memory/unique_ptr.h:53:19: error: invalid application of 'sizeof' to an incomplete type 'program_graph::Node' static_assert(sizeof(_Tp) > 0, ^~~~~~~~~~~ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include/c++/v1/__memory/unique_ptr.h:318:7: note: in instantiation of member function 'std::default_delete<program_graph::Node>::operator()' requested here _ptr.second()(__tmp); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include/c++/v1/__memory/unique_ptr.h:272:19: note: in instantiation of member function 'std::unique_ptr<program_graph::Node>::reset' requested here ~unique_ptr() { reset(); } ^ some_other_file.cpp:151:36: note: in instantiation of member function 'std::unique_ptr<program_graph::Node>::~unique_ptr' requested here const NodeId& currMI = pg.add_instr_node(opcode, opcode_desc, block_id);

ihi
  • 107
  • 1
  • 9
  • 2
    [Does this answer help?](https://stackoverflow.com/a/6089065/7691729) – Quimby May 20 '22 at 07:31
  • You only have `class Node;` in your headers instead of the class definition. Move it from your .cpp to your .h. Also in your cpp file change `class ProgramGraph { NodeId add_instr_node` to `NodeId ProgramGraph::add_instr_node` and include api.h from api.cpp – Sebastian May 20 '22 at 08:29
  • @Sebastian OP's point is *not* to have `class Node` in the header. But since callers of the public `add_instr_node` must be able to delete a `unique_ptr`, that goal is not achievable. There's no way to make this work. – j6t May 20 '22 at 08:32
  • 1
    @j6t perhaps with unique_ptr and a custom deleter? – Sebastian May 20 '22 at 08:35
  • with `std::default_delete`? – Sebastian May 20 '22 at 08:45
  • Red flags! currMI is a reference to a pointer to a node that **isn't** stored in the graph! Is that on purpose? – user253751 May 20 '22 at 09:01
  • @Sebastian Good point. Using a deleter that is not `std::default_delete` might actually work. – j6t May 20 '22 at 09:22
  • @j6t yes, you are right, one would have to encapsulate it. In any case some public interface to invoke deletion directly or indirectly is necessary. – Sebastian May 20 '22 at 09:29
  • Something like the type-erased deleter in a section on this blog entry: https://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/ – Sebastian May 20 '22 at 09:33
  • Is your intention that upon deletion of the NodeID the node should be removed from the graph? Then a custom deleter could do that. But why not just return a plain Node with move (no-copy) semantic? No need to encapsulate `ListDigraph::Node` twice. – Goswin von Brederlow May 20 '22 at 16:13

2 Answers2

0

You was redefinition class ProgramGraph in "API.cpp" file.
Try removing it, and recompiling and provide a more detailed version of your code.
Just simple:

class Node {
public:
    Node()  {}
    ~Node() {}
};
NodeId ProgramGraph::add_instr_node(int opcode, const string& desc/*, BlockId& parent*/) {
    /*return pimpl->add_instr_node(opcode, desc, parent);*/
    return nullptr;
}
zpc
  • 47
  • 3
  • My bad : `API.cpp` contains the definition of `ProgramGraphImpl`, not `ProgramGraph`. Edited my question accordingly. Thank you. – ihi May 20 '22 at 17:00
0

Need to use a custom deleter something like:

using NodePtr = std::unique_ptr<Node,void(*)(Node*)>;

and then providing the deleter in the .cpp file:

void delete_node(Node* node) { delete node; }
...
NodePtr uq{new Node(n), delete_node};

since the default deleter does not work on incomplete types.

Zitrax
  • 19,036
  • 20
  • 88
  • 110