Sign in to follow this  

Compiler strain/template trouble/infinite iteration

This topic is 4804 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm currently sandboxing around with some template ideas and managed to bork a lot of stuff. I'm using Borland C++ BuilderX. I'm trying to template a return type for a member function. Something like:
template<int length, template<typename> class CONT>
class ARRAY{
    public:
	ARRAY();
	~ARRAY();
	template<typename U>
	U& access(int);
    private:
	CONT data[length];
};

where data is an array of a templated container class with a length of "length". The program actually compiles fine but when I step through the debugger, it goes to the initialization of an ARRAY then calls main(). So now my stack looks like this main() ARRAY<..,..>::ARRAY<..,..>() main() It will just keep loading main() and the constructor over and over again until finally the stack gives up and the program dies. I'm sure the problem is in CONT data[length]; CONT should have some template arguments. I would rather not give it arguments because I want to be able to store all kinds of data in the container. Then I could access them with something like
template<int length, template<typename> class CONT>
template<typename U>
U& ARRAY<length, CONT>::access(int index){
    return reinterpret_cast<U&>(data[index%length].getdata());
}

Naturally that isn't working out very well. Any suggestions? or am I following a path of failure?

Share this post


Link to post
Share on other sites
Quote:
Original post by aaron_ds
I would rather not give it arguments because I want to be able to store all kinds of data in the container.


When using a template template parameter in some definition you have to specifiy type arguments, i'm surprised your code even compiles.

Even if you chanage it to normal type parameter it will be a container of some particular type.

Quote:
Original post by aaron_ds
Then I could access them with something like
*** Source Snippet Removed ***
Naturally that isn't working out very well.


I've never seen any STL container that has a "getdata" method, besides there probably isn't any reason to use a cast if your using templated member functions.

Quote:
Original post by aaron_ds
am I following a path of failure?


I think so, i feel there is something your not telling us such as what containers your refering to because like i said no STL container has a getdata method, if you could also say what your exact intentions are we could probably give you a better alternative, you'll need to give a bit more info.

EDIT: Are you sure you need an array of containers? because thats what your is code trying to say.

Share this post


Link to post
Share on other sites
Functions are overloaded by their arguments, not their return values. You can't have a bunch of functions that return different types and all take an integer.

Maybe if CONT had a typedef for whatever type it's using, you could do:

typename CONT::WHATEVER_TYPE & access(int);

For example, STL containers have a ::value_type you could use to find what type of object it contains.

Share this post


Link to post
Share on other sites
Ah, no stl container, just a simple class to encapsulate a templated variable.
I'm suprised it compiles aswell.

I'm attempting to contrive a system to allow for an array of arbitrary types (even more arrays). Thats why I would rather not pass template arguments to the container class. (although this is probably whats causing the problems)

Other than that, its just an exercise in templates. It came out of a need to make a tree that can hold arbitrary types and have any number of children. Though I'm slowly begining to realize that this probably isn't possible in C++. Or atleast I don't quite have the skill level to be able to successfully pull it off.

Share this post


Link to post
Share on other sites
Quote:
Original post by smart_idiot
Functions are overloaded by their arguments, not their return values. You can't have a bunch of functions that return different arguments and all take an integer.


Thats true but for a template functions you can have different return types e.g.:


template < typename Ret >
Ret bar(int i) {
return Ret(i);
}

int main() {

int i = bar<int>(4);

float j = bar<float>(6);

double h = bar<double>(30);


return 0;

}


but as shown the compiler can only deduce template types from function parameters which is not the case in this example so you must explicitly state the type.

Share this post


Link to post
Share on other sites
Quote:
Original post by aaron_ds
Other than that, its just an exercise in templates. It came out of a need to make a tree that can hold arbitrary types and have any number of children. Though I'm slowly begining to realize that this probably isn't possible in C++. Or atleast I don't quite have the skill level to be able to successfully pull it off.


It is possible in a number of ways and its not as complicated as your code is making it out be.

some choices are:

a container of pointers to void (not very safe),
a container of smart pointers to void (a lil-bit more safer),
a container of pointers to some base type (its not really arbitrary types)
a container of boost::any (i'd go with this one).

a quick & safe (not necessary efficient) structure for a heterogeneous n-ary tree with some help with boost libraries:


#include <boost\pool\pool_alloc.hpp>
#include <boost\any.hpp>
#include <boost\smart_ptr.hpp>
#include <list>

template < template < typename U, typename V > class Sequence = std::list >
struct tree {

typedef boost::shared_ptr<tree> tree_node;
typedef boost::pool_allocator<tree_node> node_alloc;
typedef Sequence< tree_node, node_alloc > child_list;

private:

tree* parent;
boost::any val;
child_list kids;

public:

template < typename Tp >
tree(const Tp& b = Tp()): parent(0), kids(), val(b) {}

typename child_list::const_iterator begin() const { return kids.begin(); }

typename child_list::iterator begin() { return kids.begin(); }

typename child_list::const_iterator end() const { return kids.end(); }

typename child_list::iterator end() { return kids.end(); }

typename child_list::size_type size() const { return kids.size(); }

bool empty() const { return kids.empty(); }

};


i leave the rest for you to explore and complete as an exercise [smile] (its late and i'm getting lazy)

boost::pool_allocator
boost::shared_ptr

Share this post


Link to post
Share on other sites
@snk_kid: definatly pointed me in the right direction. ratings++;
I had to change a bit of things around, because boost::shared_ptr doesnt take a template template as its argument.
The data members are fine aswell as the constructor and the empty() method.
However the other methods are causing some problems. (Could be my extreme lack of stl/boost expirence)

eg:
typename child_list::const_iterator begin() const { return kids.begin(); }

I know typename is needed because otherwise child_list::const_iterator would be treated as a static member of child_list. The syntax of the definition looks perfectly fine to me, but I;m getting a nondescript error message of:
F1004 Internal compiler error...
Not very helpful :-/

It does however, seem to work if instead of using a templated class with a container type as a template paramater, that I just hardcode std::list into the class. Although this isn't as generic, it works well enough.
Thanks all.
Cheers!

Share this post


Link to post
Share on other sites
Quote:
Original post by aaron_ds
I had to change a bit of things around, because boost::shared_ptr doesnt take a template template as its argument.


Strange it should do (works with VC++ 7.1 & gcc 3.4.2), i think you'll have to show us what you mean in code, although i did make a lil bo-boo in one of the typedefs it should have been:


typedef boost::shared_ptr< tree< Sequence > > tree_node;


Quote:
Original post by aaron_ds
The data members are fine aswell as the constructor and the empty() method.
However the other methods are causing some problems. (Could be my extreme lack of stl/boost expirence)

eg:
typename child_list::const_iterator begin() const { return kids.begin(); }

I know typename is needed because otherwise child_list::const_iterator would be treated as a static member of child_list. The syntax of the definition looks perfectly fine to me, but I;m getting a nondescript error message of:
F1004 Internal compiler error...
Not very helpful :-/


which compiler & version are you using? VC++ 6.0 is really dodgy for temmplates & the c++ standard support in general.

perhaps if you could post us what you've got so far aswell.

Quote:
Original post by aaron_ds
It does however, seem to work if instead of using a templated class with a container type as a template paramater, that I just hardcode std::list into the class. Although this isn't as generic, it works well enough.


Thats no problem, the reason why i did it in the first place because you could give the client a choice of sequence type containers.

Share this post


Link to post
Share on other sites
This compiles just fine:

#include <oldstl\new.h> //for std::nothrow (dont know why)
#include <boost\pool\pool_alloc.hpp>
#include <boost\any.hpp>
#include <boost\smart_ptr.hpp>
#include <list>

template <template <typename, typename> class Sequence=std::list>
class tree{

typedef boost::shared_ptr<tree<Sequence> > tree_node;
typedef boost::pool_allocator<tree_node> node_alloc;
typedef Sequence<tree_node, node_alloc> child_list;

private:

tree* parent;
boost::any val;
child_list kids;

public:

template < typename Tp >
tree(const Tp& b=Tp()): parent(0), kids(), val(b) {}

typename Sequence::const_iterator begin(void) const{ return kids.begin(); }

typename Sequence::iterator begin() { return kids.begin(); }

typename Sequence::const_iterator end() const { return kids.end(); }

typename Sequence::iterator end() { return kids.end(); }

typename Sequence::size_type size() const { return kids.size(); }

bool empty() const { return kids.empty(); }

};
void food(void){
//it works!!!
tree<> root;
}





For some unknown reason, typename Sequence::const_iterator begin(void) const{ return kids.begin(); }
compiles just fine while anything that would make sense does not.

typename child_list::const_iterator begin(void) const{ return kids.begin(); }
and
typename Sequence<tree_node, node_alloc>::const_iterator begin(void) const{ return kids.begin(); }
both do not compile and give an internal compiling error.

I'm stumped, but it seems to be working.
Thanks again snk_kid, your knowledge of boost and stl is very helpful.

Edit: using Borland C++BuilderX

[Edited by - aaron_ds on October 11, 2004 9:31:50 PM]

Share this post


Link to post
Share on other sites

#include <oldstl\new.h>
#include <boost\pool\pool_alloc.hpp>
#include <boost\any.hpp>
#include <boost\smart_ptr.hpp>
#include <list>
namespace ads{
template <template <typename, typename> class Sequence=std::list>
class tree{
typedef boost::shared_ptr<tree<Sequence> > tree_node;
typedef boost::pool_allocator<tree_node> node_alloc;
typedef Sequence<tree_node, node_alloc> child_list;

private:

tree* parent;
boost::any val;
child_list kids;

public:
/*Public typedef, reduces creation of iterators*/
typedef Sequence<tree_node, node_alloc> child_list;

template <typename Tp >
tree(const Tp& b =Tp() ): parent(0), kids(), val(b) {}

typename Sequence::const_iterator begin(void) const{ return kids.begin(); }

typename Sequence::iterator begin() { return kids.begin(); }

typename Sequence::const_iterator end() const { return kids.end(); }

typename Sequence::iterator end() { return kids.end(); }

typename Sequence::size_type size() const { return kids.size(); }

bool empty() const { return kids.empty(); }

template<typename T>
void add_child(const T& t){
kids.push_back(tree_node(new tree<>(t)));
}

boost::any getdata(void){
return val;
}
};
}



/*Example*/
using boost::any_cast;
typedef ads::tree<> tree;

tree root("root");
root.add_child(1);
root.add_child("subarray");
root.add_child(1.567);


cout<<boost::any_cast<char*>(root.getdata())<<"\n";
tree::child_list::iterator itr=root.begin();



Everything is begining to take shape. I can initialize trees just fine and add data to them aswell.

Accing the data is a bit more complicated than planned.
I publicly typedefed child_list so that I didnt have to type in std::list<boost::shared_ptr<tree<Sequence> >,boost::pool_allocator<boost::shared_ptr<tree<std::list> > > >::iterator
tree::child_list::iterator is less verbose.

There is one problem; however.

tree::child_list::iterator itr=root.begin();
yeild the error Cannot convert '_List_iterator<_Tp,_Nonconst_traits<_Tp> >' to'_List_iterator<boost::shared_ptr<tree>,_Nonconst_triats<boost::shared_ptr<trrr> > >

Perhaps this is because I'm doing
typename Sequence::const_iterator begin(void) const{ return kids.begin(); }
instead of
typename child_list::const_iterator begin() const { return kids.begin(); }

Why would one compile fine and not the other?
The second should compiler better than the former.
What gives?

Share this post


Link to post
Share on other sites
In a word, yes. The problem is that Sequence isn't really a type, so Sequence::iterator is ambiguous; I'm kinda surprised some other errors don't crop up here. (The reason it didn't give a compiler error before is because member functions of template classes are only compiled if you use them in code). I'm not sure why the
typename child_list::iterator line would give an internal compiler error.

What happens if you try
typedef typename Sequence<<tree_node, node_alloc>::iterator iterator;

?

Share this post


Link to post
Share on other sites
These lines compile just fine.
typedef typename Sequence<tree_node, node_alloc>::const_iterator const_iterator;
and
typedef typename child_list::const_iterator const_iterator;

const_iterator begin(void) const{ return kids.begin(); }
won't compile. (Interal compiler error)

It seems that whenever I try using Sequence with template arguments I get an interal compiler error. Strangely, using it without arguments compiles fine.

I don't think tricking the compiler with typedefs is going to work since they are going to get expanded back into something like Sequence<..., ...>

EDIT: God only knows why child_list::iterator begin(void) didn't compile using Borland C++BuilderX with the borland compiler.
I tried using the MiniGW GNU++ compiler for windows and it immediatly rejected Sequence::iterator begin(void) and compiled happily with child_list::iterator begin(void)
I always thought borland's compiler was fairly standards compliant and that it delt with templates well. Is this the general consensus?
Unfortuantly, the MiniGW borked when I tried to cast a char* into a boost::any :-/

I would like to try compiling on MSVC++ but I've found it near impossible to integrate into C++BuilderX without hand editing the toolset files (can't find them online).

[Edited by - aaron_ds on October 13, 2004 11:56:29 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by aaron_ds
EDIT: God only knows why child_list::iterator begin(void) didn't compile using Borland C++BuilderX with the borland compiler.
I tried using the MiniGW GNU++ compiler for windows and it immediatly rejected Sequence::iterator begin(void) and compiled happily with child_list::iterator begin(void)
I always thought borland's compiler was fairly standards compliant and that it delt with templates well. Is this the general consensus?


Perhaps there are some updates for BuilderX on borlands website.

Quote:
Original post by aaron_ds
Unfortuantly, the MiniGW borked when I tried to cast a char* into a boost::any :-/


I would suggest using std::string for real c++ strings and not C-style strings to get the expected behaviour.

Also you probably wont to change this:


boost::any getdata(void){
return val;
}


to this:


const boost::any& getdata() const {
return val;
}

boost::any& getdata() {
return val;
}


or


template < typename T >
const T& get() const {
return boost::any_cast<const T&>(val);
}

template < typename T >
T& get() {
return boost::any_cast<T&>(val);
}


if any_cast is unsuccessful it will throw boost::bad_any_cast e.g.


try {

const std::string& str_ref = boost::any_cast<const std::string&>(root.getdata());

} catch(const boost::bad_any_cast& b) {
/* ... insert exception handling code ... */
}


one more thing you'll wont to change your add_child a little bit to:


template<typename T>
void add_child(const T& t){
tree_node t_ptr(new tree<Sequence>(t));
kids.push_back(t_ptr);
}


This is something that is suggested in boost's Smart Pointer Programming Techniques to always use a named smart pointer variable before passing it some function, i think it has something to do with it being able to handle RAII better this way.

EDIT: one more thing you'll probably wont to make your typedef's public instead of private in tree.

Share this post


Link to post
Share on other sites
Is this legal C++? Aren't default values for template arguments illegal?

class tree
{
public:
template <typename Tp>
tree(const Tp& b = Tp()) // <-- The illegal part? What is Tp for empty ctor?
: parent(0), kids(), val(b) {}
};


Replace with:

class tree
{
public:
tree()
: parent(0), kids(), val() {}

template <typename Tp>
tree(const Tp& b)
: parent(0), kids(), val(b) {}
};

Share this post


Link to post
Share on other sites
Quote:
Original post by dalleboy
Is this legal C++? Aren't default values for template arguments illegal?


No, remember its a constant reference, giving all parameters of a consructor a default value makes a default constructor, this is also how STL handle default values of function parameters for generic code.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
No, remember its a constant reference, giving all parameters of a consructor a default value makes a default constructor, this is also how STL handle default values of function parameters for generic code.

My question still stands: What is Tp for empty ctor?

Share this post


Link to post
Share on other sites

class Test
{
public:
template <typename T>
Test(const T& rT = T())
{
}
};

int main()
{
Test o;
}


Compiling in Comeau gives following output:

Comeau C/C++ 4.3.3 (Aug 6 2003 15:13:37) for ONLINE_EVALUATION_BETA1
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:strict errors C++

"ComeauTest.c", line 12: error: no default constructor exists for class "Test"
Test o;
^

"ComeauTest.c", line 12: warning: variable "o" was declared but never referenced
Test o;
^

Share this post


Link to post
Share on other sites
Quote:
Original post by dalleboy
My question still stands: What is Tp for empty ctor?


invokes default constructor, every type including built-in primitive types have constructors, the above example was a little silly but legal how-ever this is totally fine & done often:


template < typename T >
struct test {

test(const T& g = T()) {}

};

struct foo {};

int main() {

test<foo> t;
test<int> j;

return 0;

}

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
invokes default constructor

Read again. Then answer the following question: The default constructor of what?

The type Tp is a template argument to the tree constructor, not to the tree class. It cannot be deduced automatically, you cannot default construct it.

Share this post


Link to post
Share on other sites
Quote:
Original post by dalleboy
Quote:
Original post by snk_kid
invokes default constructor

Read again. Then answer the following question: The default constructor of what?

The type Tp is a template argument to the tree constructor, not to the tree class. It cannot be deduced automatically, you cannot default construct it.


or you just don't know


struct test {

template < typename T >
test(const T& g = T()) {}

};

int main() {

test t(int);

return 0;

}


You are wright it can't deduce you must explicitly state the type but its still a default constructor, like i said before its abit silly but when i write it the first time it was late and i was tired.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid

int main() {
test t(int);
return 0;
}


That is legal C++, but it doesn't do what you think. You have just defined a function named t that takes and int argument and returns a test object.

Quote:
Original post by snk_kid
You are wright it can't deduce you must explicitly state the type but its still a default constructor, like i said before its abit silly but when i write it the first time it was late and i was tired.

Yes! I'm wright and you are rong. That is all I needed you to say. At last!

Quote:
Original post by snk_kid
its still a default constructor


Well, no it isn't the default constructor for the tree class. You'll still have to (*) add a default constructor to the tree class, as in my prevous example.

*) You don't HAVE to add it, but if you don't you can't instantiate an empty tree object.

Share this post


Link to post
Share on other sites
Quote:
Original post by dalleboy
Quote:
Original post by snk_kid
its still a default constructor


Well, no it isn't the default constructor for the tree class. You'll still have to (*) add a default constructor to the tree class, as in my prevous example.

*) You don't HAVE to add it, but if you don't you can't instantiate an empty tree object.


so your saying this:


struct foo {

foo(int i = 0) {}

};


this is not a default constructor? because it is, you might wont to read this

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
so your saying this:


struct foo {

foo(int i = 0) {}

};


this is not a default constructor? because it is, you might wont to read this

You never give up, do you? Of course that is the default constructor, that is not the issue.

Your example code below (comment mine).

struct test {
template < typename T >
test(const T& g = T()) {} // This is not the default constructor for the 'type' class.
};

The default constructor for the 'type' class is generated by the compiler, it is not the constructor that you have written.

Share this post


Link to post
Share on other sites
template <typename Tp >
tree(const Tp& b =Tp(), tree<>* parent=0 ): parent(parent), kids(), val(b) {}
can't be the default constructor for one reason, because its templated.

If you call it with an argument then the compiler can deduce what type Tp is. Without an argument, the compiler can't tell what Tp should be, and therefore can't create a Tp().

If it was possible to do something like
tree<> atree<int>();
the compiler would be explicitly told what type Tp was and could create a Tp() to fill in for the first argument.

Since
tree<> atree<int>();
tree<> <int>atree();
tree<> atree()<int>;
were all uncompilerable, I conclude that template paramaters can't be passed like this to ctors. Unfortunatly I can't use something like
template <typename Tp = void>
tree(const Tp& b =Tp(), tree<>* parent=0 ): parent(parent), kids(), val(b) {}
because default types for member templates isn't supported.

Since Tp() isn't ever used because its impossible to call a ctor with explicit template parapaters, I would propose using =Loki::NullType(), but aparently that wouldnt work either.

Strangly
tree<> root(Loki::NullType());
won't work either. Maybe there is no workaround for a nulltree.
In any case, it isn't that vital to the project.

Share this post


Link to post
Share on other sites

This topic is 4804 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this