Reference Question

  • Thread starter news.tkdsoftware.com
  • Start date
N

news.tkdsoftware.com

I have the following test code. What I have a question about is in
Func2Ref. In the pointer example, the last called method is actually the
one whom allocated memory for the object and returns the pointer to the
calling parent for it to be printed. In the reference example, the parent
actually creates the object but in Func2Ref, I need that object to be a
derived type. This is like how AST traversal occurs **hint**.

Can someone offer some input on how I should do this if I choose to use
&object reference instead of *object pointers??

class CNode {
public:
CNode() { }
virtual ~CNode() { }
virtual void print(void) = 0;
};

class CChildNode : public CNode {
public:
CChildNode() { }
virtual ~CChildNode() { }
};

class CBabyNode : public CChildNode {
public:
CBabyNode(int nVal) m_nVal(nVal) { }
virtual ~CBabyNode() { }
void print(void) { cout << m_nVal; }
private:
int m_nVal;
};

void Func2Ptr(CNode *node) {
node = new CBabyNode(5);
}
void Func1Ptr(CNode *node) {
Func2Ptr(node);
}
void Func2Ref(CNode &node) {
// here I need to be able to change
// CNode to a CBabyNode and be able
// to have it print its value when
// the main program calls print.
}
void Func1Ref(CNode &node) {
Func2Ref(node);
}

int main(int argc, char* argv[]) {
{
// Do pointer test
CNode *pNode = NULL;
Func1Ptr(pNode);
if(pNode != NULL) {
pNode->print();
delete pNode;
}

// Do Ref test
CNode node;
Func1Ref(node);
node.print();

return 0;
}

Thanks!
--
 
D

David Hilsee

news.tkdsoftware.com said:
I have the following test code. What I have a question about is in
Func2Ref. In the pointer example, the last called method is actually the
one whom allocated memory for the object and returns the pointer to the
calling parent for it to be printed. In the reference example, the parent
actually creates the object but in Func2Ref, I need that object to be a
derived type. This is like how AST traversal occurs **hint**.

Can someone offer some input on how I should do this if I choose to use
&object reference instead of *object pointers??

class CNode {
public:
CNode() { }
virtual ~CNode() { }
virtual void print(void) = 0;
};

class CChildNode : public CNode {
public:
CChildNode() { }
virtual ~CChildNode() { }
};

class CBabyNode : public CChildNode {
public:
CBabyNode(int nVal) m_nVal(nVal) { }
virtual ~CBabyNode() { }
void print(void) { cout << m_nVal; }
private:
int m_nVal;
};

void Func2Ptr(CNode *node) {
node = new CBabyNode(5);
}

That's going to leak memory. Perhaps you meant for Func2Ptr to take a
reference to a pointer?
void Func1Ptr(CNode *node) {
Func2Ptr(node);
}
void Func2Ref(CNode &node) {
// here I need to be able to change
// CNode to a CBabyNode and be able
// to have it print its value when
// the main program calls print.
}
<snip>

Could you elaborate on this? References may not be reseated. This is
explained in the FAQ (http://www.parashift.com/c++-faq-lite) section 8
(References), question 5 (How can you reseat a reference to make it refer to
a different object?). It sounds like you are trying to do something that is
impossible. What are you really trying to do?
 
J

John Harrison

news.tkdsoftware.com said:
I have the following test code. What I have a question about is in
Func2Ref.

Actually you have a few misunderstandings. Chiefly that you don't understand
how to return a value from a function.
In the pointer example, the last called method is actually the
one whom allocated memory for the object and returns the pointer to the
calling parent for it to be printed. In the reference example, the parent
actually creates the object but in Func2Ref, I need that object to be a
derived type. This is like how AST traversal occurs **hint**.

Can someone offer some input on how I should do this if I choose to use
&object reference instead of *object pointers??

class CNode {
public:
CNode() { }
virtual ~CNode() { }
virtual void print(void) = 0;
};

class CChildNode : public CNode {
public:
CChildNode() { }
virtual ~CChildNode() { }
};

class CBabyNode : public CChildNode {
public:
CBabyNode(int nVal) m_nVal(nVal) { }
virtual ~CBabyNode() { }
void print(void) { cout << m_nVal; }
private:
int m_nVal;
};

void Func2Ptr(CNode *node) {
node = new CBabyNode(5);
}

You mean

CNode *Func2Ptr() {
return new CBabyNode(5);
}
void Func1Ptr(CNode *node) {
Func2Ptr(node);
}

You mean

CNode *Func1Ptr() {
return Func2Ptr();
}

But this does exactly the same as the function above, so it hard to know
what you intended.
void Func2Ref(CNode &node) {
// here I need to be able to change
// CNode to a CBabyNode and be able
// to have it print its value when
// the main program calls print.
}

CNode cannot change to CBabyNode, it is impossible.
void Func1Ref(CNode &node) {
Func2Ref(node);
}

int main(int argc, char* argv[]) {
{
// Do pointer test
CNode *pNode = NULL;
Func1Ptr(pNode);
if(pNode != NULL) {

This will always be false, because the way you wrote Func1Ptr it did not
return any value. Whith my correction it will at least return a value and
pNode will be non NULL.
pNode->print();
delete pNode;
}

// Do Ref test
CNode node;
Func1Ref(node);
node.print();

return 0;
}

You are trying to do tree traversal, but you aren't coding anything like
that. I think you have some serious misunderstanding about what you are
doing. Unfortunately this makes it impossible to help because, whatever it
is that you are trying to accomplish, you do not need to turn a CNode into a
CBabyNode, so even if there was a way to do that it wouldn't help you. You
are just asking the wrong questions.

I would ask again but instead of asking about specific language questions,
explain what it is that you are trying to do, and if you can, what it is
that you don't understand.

john
 
N

news.tkdsoftware.com

John -
You are trying to do tree traversal, but you aren't coding anything like
that. so even if there was a way to do that it wouldn't help you. You
are just asking the wrong questions.

Yes, I am intending to generate/populate a tree. For example:

PRINT 2+3*5

would yield an AST tree so that I can traverse it using a visitor pattern
to get the following output:


5
3
*
2
+
PRINT

The goal of my prior post was to understand how I should construct my
classes for the tree and how my functions could be written using reference
variables as arguments instead of pointers to accomplish this.

If someone could illustrate an example set of code, that would be great
to help clear my misunderstandings.

Thanks
Chris
 
J

John Harrison

news.tkdsoftware.com said:
John -


Yes, I am intending to generate/populate a tree. For example:

PRINT 2+3*5

would yield an AST tree so that I can traverse it using a visitor pattern
to get the following output:


5
3
*
2
+
PRINT

The goal of my prior post was to understand how I should construct my
classes for the tree and how my functions could be written using reference
variables as arguments instead of pointers to accomplish this.

If someone could illustrate an example set of code, that would be great
to help clear my misunderstandings.

Thanks
Chris

Do you actually understand the visitor pattern and it's motivation? Before
you worry about using references not pointers I would write some code that
actually implements the visitor pattern.

I'm not a pattern expert but here my implementation using pointers. I hope
this helps.

#include <iostream>
#include <string>

class Node;

class Visitor
{
public:
virtual ~Visitor() {}
virtual void visit_number(Node* node) = 0;
virtual void visit_unary_func(Node* node) = 0;
virtual void visit_binary_func(Node* node) = 0;
};

class Node
{
public:
virtual ~Node() {}
virtual void accept(Visitor* v) = 0;
virtual void print(std::eek:stream& out) = 0;
};

class Number : public Node
{
public:
Number(int n) : num(n) {}
void accept(Visitor* v)
{
v->visit_number(this);
}
void print(std::eek:stream& out)
{
out << num << '\n';
}
private:
int num;
};

class UnaryFunc : public Node
{
public:
UnaryFunc(const std::string& n, Node* a) : name(n), arg(a) {}
void accept(Visitor* v)
{
arg->accept(v);
v->visit_unary_func(this);
}
void print(std::eek:stream& out)
{
out << name << '\n';
}
private:
std::string name;
Node* arg;
};

class BinaryFunc : public Node
{
public:
BinaryFunc(const std::string& n, Node* a1, Node* a2) : name(n), arg1(a1) ,
arg2(a2) {}
void accept(Visitor* v)
{
arg1->accept(v);
arg2->accept(v);
v->visit_binary_func(this);
}
void print(std::eek:stream& out)
{
out << name << '\n';
}
private:
std::string name;
Node* arg1;
Node* arg2;
};

class PrintVisitor : public Visitor
{
public:
PrintVisitor(std::eek:stream& o) : out(o) {}
virtual void visit_number(Node* node)
{
node->print(out);
}
virtual void visit_unary_func(Node* node)
{
node->print(out);
}
virtual void visit_binary_func(Node* node)
{
node->print(out);
}
private:
std::eek:stream& out;
};

int main()
{
Number two(2);
Number three(3);
Number five(5);
BinaryFunc times("*", &five, &three);
BinaryFunc plus("+", &times, &two);
UnaryFunc print("PRINT", &plus);
PrintVisitor pv(std::cout);
print.accept(&pv);
}
 
N

news.tkdsoftware.com

The visitor patten you posted makes sense and did drive me in the right
direction I do believe, see below.
Number two(2);
Number three(3);
Number five(5);
BinaryFunc times("*", &five, &three);
BinaryFunc plus("+", &times, &two);
UnaryFunc print("PRINT", &plus);
PrintVisitor pv(std::cout);
print.accept(&pv);
}

I guess what I'm trying to understand is the best way for the parser to
create the AST tree itself as it traverses the tokens from the lexer. See
my code below. Comments are certainly welcome.

As you can see, I pass the parent downward for tree population and have
the function return the resultant node. I added logic in the classes
you originally provided to take care of memory cleanup of the pointers
that I am passing to those objects. Does what i have provided make
logical sense?

What would you suggest I do next?

void CParser::parseProgram(void) {
CProgram program; // class derived from CNode
while(token.type != TOKEN_EOF) {
CNode *statement = ParseStatement(program);
// here I guess I need a way to
// insert the returned statement
// into my program class ??
PrintVisitor pv(std::eek:stream);
statement->accept(&pv);
delete statement; }
return program;
}

CNode* CParser::parseStatement(CNode &parent) {
CNode *p = NULL;
switch(token.type) {
case OP_PRINT:
p = new UnaryFunc("PRINT",ParseExpression(parent));
break;
default:
throw CParserException(STATEMENT_EXPECTED);
break;
}
return p;
}

CNode* CParser::parseExpression(CNode &parent) {
CNode *p = ParseTerm(parent);
while(token.type == OP_ADD || token.type == OP_SUB) {
int op = token.type;
GetNextToken();
p = new BinaryFunc(op,p,ParseTerm(parent));
}
return p;
}

CNode* CParser::parseTerm(CExpr &parent) {
CNode *p = ParseFactor(parent);
while(token.type == OP_TIMES || token.type == OP_DIV) {
int op = token.type
GetNextToken();
p = new BinaryFunc(op,p,ParseFactor(parent));
}
return p;
}

CNode* CParser::parseFactor(CExpr &parent) {
CNode *p = NULL;
switch(token.type) {
case LPAREN:
GetNextToken();
p = ParseExpression(parent);
if(token != RPAREN)
throw CParserException(RPAREN_EXPECTED);
break;
case NUMBER:
p = new Number(token);
GetNextToken();
break;
default:
throw CParserException(FACTOR_EXPECTED);
break;
}
return p;
}

thanks
chris
---
 
J

John Harrison

news.tkdsoftware.com said:
The visitor patten you posted makes sense and did drive me in the right
direction I do believe, see below.


I guess what I'm trying to understand is the best way for the parser to
create the AST tree itself as it traverses the tokens from the lexer. See
my code below. Comments are certainly welcome.

Well that's a completely different problem.
As you can see, I pass the parent downward for tree population and have
the function return the resultant node. I added logic in the classes
you originally provided to take care of memory cleanup of the pointers
that I am passing to those objects. Does what i have provided make
logical sense?

What would you suggest I do next?

void CParser::parseProgram(void) {
CProgram program; // class derived from CNode
while(token.type != TOKEN_EOF) {
CNode *statement = ParseStatement(program);
// here I guess I need a way to
// insert the returned statement
// into my program class ??

Yes I think so.
PrintVisitor pv(std::eek:stream);
statement->accept(&pv);
delete statement; }
return program;
}

CNode* CParser::parseStatement(CNode &parent) {
CNode *p = NULL;
switch(token.type) {
case OP_PRINT:
p = new UnaryFunc("PRINT",ParseExpression(parent));
break;
default:
throw CParserException(STATEMENT_EXPECTED);
break;
}
return p;
}

CNode* CParser::parseExpression(CNode &parent) {
CNode *p = ParseTerm(parent);
while(token.type == OP_ADD || token.type == OP_SUB) {
int op = token.type;
GetNextToken();
p = new BinaryFunc(op,p,ParseTerm(parent));
}
return p;
}

CNode* CParser::parseTerm(CExpr &parent) {
CNode *p = ParseFactor(parent);
while(token.type == OP_TIMES || token.type == OP_DIV) {
int op = token.type
GetNextToken();
p = new BinaryFunc(op,p,ParseFactor(parent));
}
return p;
}

CNode* CParser::parseFactor(CExpr &parent) {
CNode *p = NULL;
switch(token.type) {
case LPAREN:
GetNextToken();
p = ParseExpression(parent);
if(token != RPAREN)
throw CParserException(RPAREN_EXPECTED);
break;
case NUMBER:
p = new Number(token);
GetNextToken();
break;
default:
throw CParserException(FACTOR_EXPECTED);
break;
}
return p;
}

It looks a lot better than the previous code. Looks like you are developing
a recursive descent parser. I don't see why you are passing the parent down
however, you never seem to do anything with it other than pass it along.

john
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,157
Latest member
MercedesE4
Top