2

this is my situation:

I am writing a data structure in C++ which consists of Nodes and Edges. Nodes are connected to other nodes by means of edges.

I have different types of nodes, such as text nodes, or int nodes. Text nodes are connected to text nodes and int nodes are connected to int nodes. I am using inheritance to implement the different types of nodes because it makes sense: fundamentally, all nodes are connected to other nodes so Node is the base class, and TxtNode, IntNode are inherited classes which share the fundamental property of a node.

However, this gives me problems when I try to fetch a connected inherited node from an inherited node, because the fetching function (function which retrieves a specific node connected to the calling node) is defined in the base class Node. For example, calling this fetch function from TextNode returns me the base version of the TextNode, which lacks extra information.

I am not sure what would be the sensible coding practice to do here. I chose to code it this way because it didn't seem to make sense that I must define a different connection fetch function for all different types of nodes.

Please let me know if there is any more information I can disclose.

Here is my code, I am trying to print an inherited node's partner but it will result in printing the base node.

Node.cpp

#include "Node.h"
#include "Edge.h"

#include "common.h"

#include <vector>
#include <cassert>
#include <cstddef>
#include <stdint.h>
#include <iostream>

Node::Node() {
    id = (intptr_t)this;
    edges.clear();
}

int Node::connect(Node* dst) {
    // establish link between calling node
    // and dst node, first linkage will
    // edge between each other, links after
    // increments strength. returns number of
    // links

    // fetch edge connecting this node and 
    // dst node
    Edge* edge = findDstEdge(dst);

    // if the branch isn't established yet,
    // then make connection
    if (edge==NULL) {
        establishConnection(dst, 1);
        return 0;
    } else {
        edge->strengthenConnection();
        return 1;
    }

}

Edge* Node::findDstEdge(Node* dst) {
    // fetches edge corresponding
    // to destination node

    // walk through vector of edges to find
    // edge connecting to dst

    vector<Edge*>::iterator iter = edges.begin();

while(iter!=edges.end()) {
    Edge* e = *iter;
    Node* partner = e->getPartner(this);
    if (partner->getID() == dst->getID())
        return e;
    iter++;
    }
    // not found
    return NULL;
}

void Node::establishConnection(Node* dst, int str) {
    // low level node addition

    // establish a new edge between
    // nodes which don't know each other
    Edge* e = new Edge(this, dst, str);
    this->manuallyConnect(e);
    dst->manuallyConnect(e);
}

void Node::manuallyConnect(Edge* e) {
    edges.push_back(e);
}

ostream& operator<<(ostream& stream,Node n) {

    vector<Edge*>::iterator iter = n.edges.begin();

    while(iter!=n.edges.end()) {
        Edge* e = *iter;
        stream << *e << endl;
        iter++;
    }

    return stream;
}

Node.h

#ifndef _NODE_H_
#define _NODE_H_

#include "common.h"

#include <vector>
#include <string>
#include <stdint.h>

class Edge;

using namespace std;

class Node {  

  protected:
    vector<Edge*> edges;
    intptr_t id;

  public:
    Node();

    // manipulation
    void establishConnection(Node* dst,
           int str);
    int connect(Node* dst);
    Edge* findDstEdge(Node* dst);

    // fetchers
    intptr_t getID() {return id;}
    vector<Edge*> getEdges() {return edges;}

    void manuallyConnect(Edge* e);

    friend ostream& operator<<(ostream& stream, Node n);
};

#endif

TxtNode.cpp

#include "TxtNode.h"
#include "Edge.h"

#include "common.h"

#include <iostream>

TxtNode::TxtNode(char c): Node()  {
    _c = c;
}

ostream& operator<<(ostream& stream, TxtNode tn) {
    // print out character of this TxtNode
    stream << "char: " << tn._c << endl;
    stream << "id: " << tn.id << endl;

    // print out all connections and its
    // strength
    vector<Edge*>::iterator iter = tn.edges.begin();

    while(iter!=tn.edges.end()) {
        Edge* e = *iter;
        stream << *e << endl;
        iter++;
    }

    return stream;
}

Edge.cpp

#include "Edge.h"
#include "Node.h"

#include "common.h"

#include <cassert>
#include <iostream>

using namespace std;

Edge::Edge(Node* a, Node* b, int str) {
    node_a = a;
    node_b = b;
    strength = str;
}

void Edge::strengthenConnection() {
    strength++;
}

Node* Edge::getPartner(Node* src) {
    uint src_ID = src->getID();
    if (node_a->getID() == src_ID)
        return node_b;
    else if (node_b->getID() == src_ID)
        return node_a;
    assert(false);
}

ostream& operator<<(ostream& stream, Edge e) {
    stream << "strength: "
           << e.strength
           << endl;
    stream << "node_a: "
           << e.node_a->getID()
           << endl;
    stream << "node_b: "
           << e.node_b->getID()
           << endl;
    return stream;
}

Currently I just have the code to print the ID which is an intptr_t, because I found out that I can't access inherited class's member from base class.

I am inclined to access the inherited class's member from base class because the edge class deals with base node class.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user3064869
  • 530
  • 1
  • 6
  • 19
  • 1
    Use virtual member functions, it will automatically go to the child class. – Barmar Aug 08 '14 at 23:26
  • 1
    Related: http://stackoverflow.com/q/2391679/873025 – Felix Glas Aug 08 '14 at 23:27
  • So basically, if virtual member functions is the standard way to go, there is no way other than having have to write different fetch-connected-node function for all inherited nodes? – user3064869 Aug 08 '14 at 23:37
  • 1
    It sounds like you need to support [double dispatch](http://en.wikipedia.org/wiki/Double_dispatch) in your program. Classical solution for it is [Visitor pattern](http://en.wikipedia.org/wiki/Visitor_pattern). See also [this answer](http://stackoverflow.com/questions/14056609/is-there-a-way-to-infer-the-type-of-an-object/14057145#14057145). – Ilya Aug 08 '14 at 23:42
  • 1
    If nodes are only going to be connected to other nodes of the same type, then templates seem more fitting than inheritance to me. – eerorika Aug 08 '14 at 23:59
  • Currently that is the case, but in the future, I may have to connect nodes of differing types. I will look into this double dispatch thing. Thanks everyone!! – user3064869 Aug 09 '14 at 00:08
  • Also have a look at covariant return types ... en.m.wikipedia.org/wiki/Covariant_return_type – Martin Ba Aug 09 '14 at 08:20

2 Answers2

1

You write

Text nodes are connected to text nodes and int nodes are connected to int nodes.

Then you can very simply define Node_ as a class template:

template< class Value >
struct Node_
{
    vector<Node_*>  connected_nodes;
    Value           value;
};

In other words, use compile time polymorphism, not dynamic polymorphism, in order to have the relevant type information available at compile time.

That said, do have a look at Boost.Graph and see if it doesn't suit your needs.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
1

The classes code would've been a nice to have.

From what you are telling you don't declare the fetch function virtual.

Here's a very quick example on how virtual / non virtual functions work

class baseNode {
 public:
    int fetch() { printf "fetched from base";};
}

class intNode : public baseNode {
 public:
    int fetch() { printf "fetched from derived int";};
}

class txtNode : public baseNode {
 public:
    int fetch() { printf "fetched from derived txt";};
}

The following code

baseNode * ptr = new intNode();
ptr->fetch();

Will print "fetched from base"

But if you declare the fetch function virtual :

class baseNode {
 public:
     virtual int fetch() { printf " fetched from base";};
}

Then the same code will print "fetched from derived int".

MichaelCMS
  • 4,703
  • 2
  • 23
  • 29
  • The problem is, nodes don't have direct access to the nodes it is connected to. It searches through its edges and returns its partner. I added the code to my first post. – user3064869 Aug 09 '14 at 08:15
  • Where is your first post ? – MichaelCMS Aug 09 '14 at 08:26
  • @user3064869: Since MichaelCMS raised a concern about the declaration of the Node class, it would be helpful to provide Node.h. – Chris Culter Aug 09 '14 at 08:49
  • Well, the code certainly cleared things up. No real need of inheritance here. As a side note, you can use pointer equality if you want to skip the getID part . For the future when you want to connect nodes of different types, you will probably have more need of virtual functions. – MichaelCMS Aug 11 '14 at 06:42