Recursive templates

J

Jon Slaughter

#pragma once
#include <vector>
class empty_class
{
};


template <int _I, int _J, class _element, class _property>
class RDES_T
{
private:
std::vector< RDES_T<_I - 1, _J - 1, _element *, _property *> * >
Container;
public:
RDES_T<0, _J-1, empty_class, _property * > Properties;

RDES_T(void) { };
int Count() { return (int)Container.size(); };
virtual ~RDES_T(void) { };
};



template <class _element, class _property>
class RDES_T<0, 0, _element, _property> { };




Ok, I was having some major problems getting this to work but it was because
it was some syntax problem with

std::vector< RDES_T<_I - 1, _J - 1, _element *, _property *> * >
Container;

(had to put spaces in the right place for the pointer symbols and crap...
was strange)


The above code does not generate any errors and seems to let me generate
everything I need completely through recursion. Its AWESOME actually!!
Thanks Tim and Ben. (I was actually trying to get it to work with my old
code but because I couldn't figure out the errors I just started from
scratch(same errors but finally figured it out)).

What I'm wondering is anyone can see any potential problems? (bad design or
pratice(and not stupid shit like ending my methods with ';').


While I think it gives me more than what I need I do love the compactness of
it to generate tree like structures.

What I was originally trying to generate was a tree like structure that
looks like this(need fixed with fonts to view properly):


A
/ \
P B
| / \
Q P C
| / \
Q P D
| |
Q E

Where E and Q are "terminal classes".

From what I can tell _I counts the number of "right recursions" and J the
number of "left". What happens is I actually always get a binary tree but
when its a Left I always make the the sub right one "empty".


As far as I can tell it works and looks very nice but I'm worried that maybe
might be doing something wrong that will cause some "strange behavior" later
on. (because I haven't looked at it hard enough and tested it out yet... I
will later when I get some more time)


One main thing I'm wondering about is that since I use an "empty class"

RDES_T<0, _J-1, empty_class, _property * > Properties;

So I don't generate any "right" sided containers will this effect
performance in any way?

Thanks
Jon
 
T

Thomas Tutone

Jon Slaughter said:
#include <vector>
class empty_class
{
};


template <int _I, int _J, class _element, class _property>
class RDES_T
[snip]

What I'm wondering is anyone can see any potential problems? (bad design
or pratice(and not stupid shit like ending my methods with ';').
[snip]

As far as I can tell it works and looks very nice but I'm worried that
maybe might be doing something wrong that will cause some "strange
behavior" later on. (because I haven't looked at it hard enough and tested
it out yet... I will later when I get some more time)

I haven't looked at your code closely, and for all I know you're going to
flame me because you regard what I'm pointing out to you as "stupid," but
technically your program has undefined behavior. That's because you're not
supposed to use parameter names that begin with an underscore followed by a
capital letter - such things are reserved for the implementation.
Therefore, your use of template parameter names such as "_I" and "_J" causes
undefined behavior. What is the practical ramification of this? Well, it
means that your program may work perfectly on your current compiler, but
when you move it to a different compiler, or upgrade your current compiler
to its successor when it comes out, your program may mysteriously stop
working even though it worked just fine on your current compiler.

To avoid this problem, stick with something like either "I" or "I_" as your
template parameter names.

Cheers,

Tom
 
J

Jon Slaughter

Thomas Tutone said:
Jon Slaughter said:
#include <vector>
class empty_class
{
};


template <int _I, int _J, class _element, class _property>
class RDES_T
[snip]

What I'm wondering is anyone can see any potential problems? (bad design
or pratice(and not stupid shit like ending my methods with ';').
[snip]

As far as I can tell it works and looks very nice but I'm worried that
maybe might be doing something wrong that will cause some "strange
behavior" later on. (because I haven't looked at it hard enough and
tested it out yet... I will later when I get some more time)

I haven't looked at your code closely, and for all I know you're going to
flame me because you regard what I'm pointing out to you as "stupid," but
technically your program has undefined behavior. That's because you're
not supposed to use parameter names that begin with an underscore followed
by a capital letter - such things are reserved for the implementation.
Therefore, your use of template parameter names such as "_I" and "_J"
causes undefined behavior. What is the practical ramification of this?
Well, it means that your program may work perfectly on your current
compiler, but when you move it to a different compiler, or upgrade your
current compiler to its successor when it comes out, your program may
mysteriously stop working even though it worked just fine on your current
compiler.

Nope, I don't think that stupid. While I don't think it is a big issue it
could be. I did that so I would know that the name is a template name and
not part of a class. I actually used 3x_ at first but it looked pretty
ugly. I just wanted to seperate the name of the template "variables" from
names that were used as part of the class decleration.

Though i do think the chances of what you said might happen is pretty small.
Specially with something like _I and _J... if those happen to be defined in
the language then that language probably sucks. I mean, what you said can be
true no matter what though. managed C++ from ms added the reserved word
property and I'm sure there are many gc++ programs that use that as a name.
One always as an issue such as you said no matter what "convention" they
use. I think that its so small and insignificant that its not an issue...
and its very easy to fix in general.
To avoid this problem, stick with something like either "I" or "I_" as
your template parameter names.


I'll take my chances. I don't see it causing any problems for me. If it
does then I'll just refactor it. I'm sure most compilers are smart enough to
know when you are using a reserved word and can report the error properly...

so its very simple:

Compile code on new compiler:

Get error "_I is reserved keyword"

refactor to "__I" or "I"

compile code

run program



Cheers,

Tom


Thanks,
Jon
 
J

Jon Slaughter

Also, is there any way to extend one of the classes different from the rest?
In my graph this is Q. Q actually is another container but it has more info.

I think I need to specialize Q? I'm not sure how though.

A
/ \
P B
| / \
Q P C
| | / \
R Q P D
| | |
R Q E
|
R

Q contains R and Q is basicaly the RDES class. I need to add some variables
and methods to handle those variables to Q that will contain information
about R.

I thought I could do something like

template <int I, class _element, class _property>
class RDES_T<I, 1, _element, _property>
{
public:
char *Name;
};

but it doesn't inherit any of the the things that the RDES_T class has. Just
replaces the class completely.

So I tried to inherit RDES_T as a base but then it gets a circular
definition error.

anyway to "extend" a template and not just create a new "special" one?

Jon
 
J

Jon Slaughter

I guess ultimately it would be nice if each branch of the "tree" could be
its own unique class.

i.e., right now the RDES_T structure generates a tree like


RDES_T
/ \
RDES_T RDES_T
/ / \
RDES_T RDES_T RDES_T
| | |
property property element




What would be nice if it was possible to completely generalize this into a
general binary tree where one could also made each node any class though
would want. But not making pointers to those classes but actually being able
to set the class at the proper location without any trouble.


like (I,J) will locate the proper specialization needed. If say I wanted to
make the tree


RDES_T
/ \
RDES_T RDES_T
/ / \
Q RDES_T RDES_T
| | |
property property element


Then I just create a template Q

temlate <int I, int J, class element, class property>
class Q
{

}

and then specialize RDES on (0,1)

template <class element, class property>
class RDES<0, 1, element, property> : public Q
{
..... (need to keep all stuff that the RDES template has though
}


this should add all of the variables and methods of Q at the point (0,1) in
the graph. (which corresponds to some branch I think its shown above
properly but maybe not.)



Not sure if above makes sense but basicaly it seems one could make a
template to produce a binary tree of arbitrary classes at the nodes? Not
sure what use this would be but it might be cool to just be able to do it?
;)

Maybe one could make it into a general directed graph and then use the
dynamic code generation to do complicated graph algorithms really fast(well,
who knows.. assuming the time it takes to do the algorithms are much longer
than the extra time needed to compile the new code). (Just an idea...
probably actually slower than doing it directly)



Jon
 
G

Guest

Not sure if above makes sense [...]

What kind of self-directed discussion is this? What are you trying to tell
us.

It no good design style, neither in the conventional OOP nor in generative
Programming.
What kind of benefit does your "tree" have, against any tradtional
implementation of a tree structure.

Actually you are only generating an overhead: every node hierarchy is a
different type, but their structure is very similar. The only diference is a
integer count.

I know about generative programming approaches, using templates in a similar
manner like you, to create configurable software families, ie a software
generating system.

// oliver
 
G

Greg

Jon said:
#pragma once
#include <vector>
class empty_class
{
};


template <int _I, int _J, class _element, class _property>
class RDES_T
{
private:
std::vector< RDES_T<_I - 1, _J - 1, _element *, _property *> * >
Container;
public:
RDES_T<0, _J-1, empty_class, _property * > Properties;

RDES_T(void) { };
int Count() { return (int)Container.size(); };
virtual ~RDES_T(void) { };
};



template <class _element, class _property>
class RDES_T<0, 0, _element, _property> { };




Ok, I was having some major problems getting this to work but it was because
it was some syntax problem with

std::vector< RDES_T<_I - 1, _J - 1, _element *, _property *> * >
Container;

(had to put spaces in the right place for the pointer symbols and crap...
was strange)


The above code does not generate any errors and seems to let me generate
everything I need completely through recursion. Its AWESOME actually!!
Thanks Tim and Ben. (I was actually trying to get it to work with my old
code but because I couldn't figure out the errors I just started from
scratch(same errors but finally figured it out)).

What I'm wondering is anyone can see any potential problems? (bad design or
pratice(and not stupid shit like ending my methods with ';').


While I think it gives me more than what I need I do love the compactness of
it to generate tree like structures.

What I was originally trying to generate was a tree like structure that
looks like this(need fixed with fonts to view properly):


A
/ \
P B
| / \
Q P C
| / \
Q P D
| |
Q E

Where E and Q are "terminal classes".

From what I can tell _I counts the number of "right recursions" and J the
number of "left". What happens is I actually always get a binary tree but
when its a Left I always make the the sub right one "empty".

I don't see this particular tree pattern arising from the current
implementation. It is on the right track, but one problem is that the
template generation requires both the "left" and "right" int parameters
to be zero in order to stop the instantiations. But as far as I can
tell that specialization is not enough to prevent _I from becoming
negative while _J is still positive. Another problem is that each
successive instantation of RDES_T adds a pointer type to the _property
type parameter. In other words, if the first RDES_T template is
instatiated with J = 10 and _property = int, then the last RDES_T would
be instatiated would be with J=0 and _property=int********** - which is
unlikely to be correct.

The class template also seems to be complicated than it could be; that
makes it harder to understand its code. For example the _property and
_element type parameters have no apparent purpose and certainly looks
like they could be eliminated. And what is the use of the vector as a
data member? Wouldn't it simplify matters if Property and Container
were the same type?

The names chosen are also a barrier to comprehension. For example, most
programmers would expect RDES_T to be a macro. It's not a familiar
acronym and generally only macros have all uppercased letters.
Furthermore, "RDES_T" is not English. So one can only guess what it
might mean. I would guess that it would have something to do with the
DES encryption standard. In fact I still don't know what it stands for.

I would suggest making the implementation look more like the tree
binary tree above. In that way anyone looking at this code will have
some confidence that the implementation matches the intended design.
Specifically each node in the binary tree would have, depending on
their position: both a left and a right sub node, a left subnode only,
or no nodes at all (when the node is a terminal node). Each node would
also maintain a left-right counter that is manipulated to form the
desired structure of the tree:

(3,3) = (LeftDepth, RightDepth)
/ \
(2,0) (3,2)
/ / \
(1,0) (2,0) (3,1)
/ / \
(1,0) (2,0) (3,0)
/ /
(1,0) (2,0)
/
(1,0)


For example, this pattern can be implemented with this class template:

template <int LeftDepth, int RightDepth, class T>
struct BTreeNode
{
BTreeNode<LeftDepth-1, 0, T> left;
BTreeNode<LeftDepth, RightDepth-1, T> right;

T content;
};

Note how the BTreeNode template class encapsulates other instantiations
of the BTreeNode template. Of course at some point BTreeNode has to
have no other BTreeNodes as members. So two partial specializations of
BTreeNode stop the tree from growing forever:

// when RightDepth is 0 there is only a left subnode

template <int LeftDepth, class T>
struct BTreeNode< LeftDepth, 0, T>
{
BTreeNode< LeftDepth-1, 0, T> left;

T content;
};


// when LeftDepth and RightDepth are both zero there are no
subnodes

template <class T>
struct BTreeNode<0, 0, T>
{
};

And now some simple accesses to make sure the nodes in this BTree are
where we expect them:

...
myNode.left.left.content = 3;
myNode.right.right.left.left.content = 6;

Now, I'm not saying this class should replace the existing
implementation. Rather it's just an example of some techniques that
could make the current implementation a bit more clear, and a bit more
simple. At least in my opinion.
As far as I can tell it works and looks very nice but I'm worried that maybe
might be doing something wrong that will cause some "strange behavior" later
on. (because I haven't looked at it hard enough and tested it out yet... I
will later when I get some more time)


One main thing I'm wondering about is that since I use an "empty class"

RDES_T<0, _J-1, empty_class, _property * > Properties;

So I don't generate any "right" sided containers will this effect
performance in any way?

No, performance is unlikely to be an issue here. On the contrary,
templates generally offer excellent runtime performance by having
performing compile-time optimizations and precalculations.
Thanks
Jon

Here are some more observations on the current implementation:

Whis is RDES_T's destructor virtual? It's unusual for a template class
to have virtual methods. Generally, a class template doesn't need them.

The Count() method should be a const method:

size_t Count() const { return mContainer.size(); }

and its return to type should match mContainer::size()'s return type.
Why would it be different?

One more minor point:

Constructors and destructors should not be declared with a "void"
parameter list. They should have no parameter list. A void parameter
list is a C-ism that is not meaningful in C++. In fact, it's somewhat
surprising that the compiler accepted a constructor and destructor
being declared in this way.

Greg
 
J

Jon Slaughter

Greg said:
Jon Slaughter wrote:
I don't see this particular tree pattern arising from the current
implementation. It is on the right track, but one problem is that the
template generation requires both the "left" and "right" int parameters
to be zero in order to stop the instantiations. But as far as I can
tell that specialization is not enough to prevent _I from becoming
negative while _J is still positive. Another problem is that each
successive instantation of RDES_T adds a pointer type to the _property
type parameter. In other words, if the first RDES_T template is
instatiated with J = 10 and _property = int, then the last RDES_T would
be instatiated would be with J=0 and _property=int********** - which is
unlikely to be correct.
In the way it is used I and J always become zero... I did have that problem
before and maybe I posted the old code were J could have been negative but I
fixed it. But the code does compile(so I can't see how J could become
negative except maybe if J > I but since its recursive eventually J < I and
it will fail(Which it doesn't)). I will try to analyze it and see again
though.

I think I see your point about the pointer stuff. I'm not sure if it is
doing that or not but it was doing that until I fixed the problem with J
going negative. So its probably still doing that. This seems like it will
be an issue. I want Container to hold a vector of *pointers but I guess I
don't really need the * on the lement and property. But the problem seems
that when I define Properties... hell now I'm not even sure why I made any
of the parameters pointers ;/ Only think I need is to have RDES_T a pointer
so I have a pointer of vectors(the reason I do this is I simply don't want
vector copying the objects themselfs since I want to handle it... so I'd
rather it just handle the vector of pointer memory management and stuff.

So I removed all the * except for RDES_T... i.e.

private:
std::vector< RDES_T<_I - 1, _J - 1, _element, _property > * > Container;
public:
// A sub container to hold for the properties of this container
The class template also seems to be complicated than it could be; that
makes it harder to understand its code. For example the _property and
_element type parameters have no apparent purpose and certainly looks
like they could be eliminated. And what is the use of the vector as a
data member? Wouldn't it simplify matters if Property and Container
were the same type?

? Property and container are not of the same type? Container is a vector and
Property is just an inherited structure(its right notes are all "empty").

If you mean what container holds then it has pointes to

RDES_T<_I - 1, _J - 1, _element, _property >

while properties is just the template
RDES_T<0, _J-1, empty_class, _property > Properties;

these are two different things though. The first generating a "true" tree
structure while the second generates. Actually I just realized that I need
something else. I need properties to create a recursive structure in a more
complicated way. Though I think it doesn't matter for the discussion. Its
just a matter of specializing and using the right I,J in the recursive
process.

So how can I do this by removing those parameters? I had to have a way to
parameterize the "terminal" classes, which can be different. (i.e. I might
call the class as RDES_T<4,2, Some_Class, Some_Other_Class>)
But I surely don't know much about templates so if you have any suggestions?
I just did what I could to get the stuff to work and it happend to work...
so I could be doing things completely wrong or using inefficient means.

The names chosen are also a barrier to comprehension. For example, most
programmers would expect RDES_T to be a macro. It's not a familiar
acronym and generally only macros have all uppercased letters.
Furthermore, "RDES_T" is not English. So one can only guess what it
might mean. I would guess that it would have something to do with the
DES encryption standard. In fact I still don't know what it stands for.

Heh, thats ok... The name has nothing to do with the issue. Its an acronym
and doesn't bear any meaning to the issue. You can rename the class to
whatever you like.
I would suggest making the implementation look more like the tree
binary tree above. In that way anyone looking at this code will have
some confidence that the implementation matches the intended design.
Specifically each node in the binary tree would have, depending on
their position: both a left and a right sub node, a left subnode only,
or no nodes at all (when the node is a terminal node). Each node would
also maintain a left-right counter that is manipulated to form the
desired structure of the tree:

(3,3) = (LeftDepth, RightDepth)
/ \
(2,0) (3,2)
/ / \
(1,0) (2,0) (3,1)
/ / \
(1,0) (2,0) (3,0)
/ /
(1,0) (2,0)
/
(1,0)


For example, this pattern can be implemented with this class template:

template <int LeftDepth, int RightDepth, class T>
struct BTreeNode
{
BTreeNode<LeftDepth-1, 0, T> left;
BTreeNode<LeftDepth, RightDepth-1, T> right;

T content;
};

Ok, first your right about the left and right depths naming making it easier
to understand the tree structure... but your wrong in assuming that was the
exact pattern of the problem ;) Remember that there are two different
terminals depending on which end branch you are on... so a T won't cut it..
I want a T class to end on the left and an S class to end on the right. But
also remember that the actual right sides are vectors and contain many
branches. The tree was only an outline of the semantics but not the
semantics itself.

While your example is pretty simple and the names do make sense for a Btree
they do not model my problem well ;/ My actual tree is

(3,3) = (LeftDepth, RightDepth)
/ \
(2,0) (3,2)
\ / \
(1,0) (2,0) (3,1)
\ / \
(1,0) (2,0) (3,0)
\ / \
(1,0) (2,0) (3,0)*
\
(1,0)

Also, I was approaching it from my original model which had nothing to do
with a binary tree(well except that it had a recursive process in it.... but
not a very simple one)


Now, thing to remember about the above tree is that all notes that come from
a right branch are a different class(actually a vector of classes i.e. what
I'm calling a container) and all the that branch right are a properties
class.

all the (1,0)'s are terminal classes and of the same kind BUT (3,0)* is a
final terminating class of the right branches and is a class of its own too.

Now thing to note is that the classes (1,0) and (3,I) are of the fundamental
type except that (1,0) end the recursive process while (3,I) continue it).
See my original threads for code that demonstrates the problem(though it
might be more work to sort out all those details)... I also explain what
lead me to this problem of wanting to define a recursive structure using
templates.



Note how the BTreeNode template class encapsulates other instantiations
of the BTreeNode template. Of course at some point BTreeNode has to
have no other BTreeNodes as members. So two partial specializations of
BTreeNode stop the tree from growing forever:

// when RightDepth is 0 there is only a left subnode

template <int LeftDepth, class T>
struct BTreeNode< LeftDepth, 0, T>
{
BTreeNode< LeftDepth-1, 0, T> left;

T content;
};

Yes, this is what my main problem is about. I have to specialize but when I
do have to "rewrite" the whole class basicaly except for a few minor
changes(i.e. deleted the recursion on one side basicaly). There are many
many more methods and variables that the template will have... and by doing
the recursion each tree has those same functions and variables... actually
all will have those same functions except the end nodes. It seems if I
specialize then I have to basicaly copy the whole class. I was doing this
before manually 7 times and so if I have to do it 2... which isn't much
better because I can/have wrote a utility that just copys my base class and
then refactors it so it works and hence I only have to manage one class.

// when LeftDepth and RightDepth are both zero there are no
subnodes

template <class T>
struct BTreeNode<0, 0, T>
{
};

And now some simple accesses to make sure the nodes in this BTree are
where we expect them:

...
myNode.left.left.content = 3;
myNode.right.right.left.left.content = 6;

Now, I'm not saying this class should replace the existing
implementation. Rather it's just an example of some techniques that
could make the current implementation a bit more clear, and a bit more
simple. At least in my opinion.

Yeah, well the do make it clear in understanding the recursive process but
they then start to move away from the actual model I am trying to do. But
the problem is slightly more complex than just a simple Btree as I have
pointed out. Though thanks for going to the trouble... It does help in
clarifying some issues.

No, performance is unlikely to be an issue here. On the contrary,
templates generally offer excellent runtime performance by having
performing compile-time optimizations and precalculations.

yeah, I figured that but wasn't sure if using an empty class would do
strange things.

Thanks for your time.
Here are some more observations on the current implementation:

Whis is RDES_T's destructor virtual? It's unusual for a template class
to have virtual methods. Generally, a class template doesn't need them.

Ok, I didn't know that. Since it was a generalization from a non-templated
class I just kept that idea. But yeah, I suppose you are right specially
since I'm not inheriting anything in the recursive process(I as before
though)
The Count() method should be a const method:

size_t Count() const { return mContainer.size(); }

and its return to type should match mContainer::size()'s return type.
Why would it be different?

Because Container is a vector of variable size? Returning a const of the
size would say that the size doesn't change? the reason I return int instead
of size_t is I hate dealing with casting from size_t to ints when I have to.
One more minor point:

Constructors and destructors should not be declared with a "void"
parameter list. They should have no parameter list. A void parameter
list is a C-ism that is not meaningful in C++. In fact, it's somewhat
surprising that the compiler accepted a constructor and destructor
being declared in this way.

really? heh... why? whats wrong with saying void as a argument? isn't void
some_func(void) the exact same as void some_func()?

just some quick search gives

http://www.cmis.csiro.au/iap/liar2/node16.html

ofcourse thats not for C++ but surely there is some non compile/programming
language specific reason?




Thanks,
Jon
 

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

Forum statistics

Threads
474,141
Messages
2,570,818
Members
47,367
Latest member
mahdiharooniir

Latest Threads

Top