Dependency Issues with Templates

Pages: 12
I have encountered a problem while working on a library, and have tried to simplify it without losing generality. How can I write the following code such that classes A and B are defined/declared in two separate files?
1
2
3
4
5
6
7
8
9
10
11
template <typename T> class A {
	void f();
};

class B {
	A<int> a;
};

template <typename T> void A<T>::f() {
	throw B;
}


I am currently trying the following, but obviously it's not compiling. (Note that the files are header guarded.)
1
2
3
4
5
6
7
8
9
10
//A.hpp
template <typename T> class A {
	void f();
};

#include "B.hpp"

template <typename T> void A<T>::f() {
	throw B;
}

1
2
3
4
5
6
//B.hpp
#include "A.hpp"

class B {
	A<int> a;
};

1
2
//test.cpp (compiling...)
#include "B.hpp" 
Last edited on
I don't know if I'm following, but do the comment lines at the top indicate the name of the file? If so, you are loading A.hpp inside B.hpp, which creates a loop when B.hpp is called from within A.hpp. As far as I can tell there's no need to load A.hpp from within B.hpp because when B.hpp is included, your A template is already declared.

Sorry if I misunderstood, not sure I understand what's going on...
ausairman wrote:
[Do] the comment lines at the top indicate the name of the file?
Yes

Mathhead200 wrote:
Note that the files are header guarded.
I.e. When A.hpp attempts to include B.hpp it does nothing, because B.hpp has already been included (from test.cpp)
I'm still stuck. Has anyone else had this kind of problem?
closed account (1yR4jE8b)
First, in A::f(), throw B; should be throw B();.

All you need to do is use a forward declaration for B:

A.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef A_HPP
#define A_HPP

class B;

template <typename T> class A {
  void f();
};

template <typename T> void A<T>::f() {
  throw B();
}

#endif /* A_HPP */ 


B.hpp
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef B_HPP
#define B_HPP

#include "A.hpp"

class B {
public:
	A<int> a;
};

#endif /* B_HPP */

I'll give that a try, but I was under the impression that because I need to create an instance of B in A.f() I needed to have (at least) the constructors declared.
Okay it seams to work; thanks! Can you (or someone else) explain why I can get away with forward declaring B here even though I'm instantiating it? I would prefer to be forward declaring where ever I can.
Last edited on
closed account (1yR4jE8b)
I'm not 100% sure, but I think it's because the compiler doesn't generate any code for templates, in this case A, until it is instantiated inside B. By that time, it has enough information to be able to instantatiate B even though all you have is a forward declaration in the header.
I may have jumped the gun there and inadvertantly generalized the problem I'm having. Even though the test case above is solved (although I'm still foggy on why the solution works,) when I try it on the real application I get a compilation error.
c:\users\christopher\cpp\rsc\mhc8\Array.hpp(69) : error C2514: 'mhc8::ArrayIndexOutOfBoundsException' : class has no constructors
        c:\users\christopher\cpp\rsc\mhc8\Array.hpp(28) : see declaration of 'mhc8::ArrayIndexOutOfBoundsException'
        c:\users\christopher\cpp\rsc\mhc8\Array.hpp(67) : while compiling class template member function 'char &mhc8::Array<T>::operator [](int)'
        with
        [
            T=char
        ]
        c:\users\christopher\cpp\rsc\mhc8\String.hpp(17) : see reference to class template instantiation 'mhc8::Array<T>' being compiled
        with
        [
            T=char
        ]

For the sake of the example "ArrayIndexOutOfBoundsException" is B, and "Array<T>" is A<T>.
Last edited on
So, I'm still stuck. (And now a little confused, but I'll deal with that later.)
Okay, so if anyone needs it, I posted the full code online here: http://www.mathhead200.com/cpp/mhc8.zip
Maybe someone can explain why this fix isn't working on the real project, or at least what I can do to resolve the issue.

Edit: Oh yea, the error above was from trying to compiler Object.cpp
Last edited on
(Still have not found a solution to my problem.)
Include ArrayIndexOutOfBoundsException in Array.hpp rather than forward declaring it.
1) ArrayIndexOutOfBoundsException.cpp includes itself? Change to include ArrayIndexOutOfBoundsException.h, after changing the name of ArrayIndexOutOfBoundsException.hpp to ArrayIndexOutOfBoundsException.h (it's not a template)

2) #ednif should be #endif in EmptyStackException.hpp

3) Change the .hpp files to '.h' files for non-templates, and fix the includes.

4) Include ArrayIndexOutOfBoundsException.h in Array.hpp

5) Array<T>::operator= function in Array.hpp needs to return a value

6) operator<< in String.cpp uses 'value' instead of 'str' parameter

7) in string, trimTail should be trimTrail (or vice versa - inconsistent usage)

...

I give up here :P follow compiler errors, and you can fix the rest of your problems after a bit of work
rollie:
re: 1, 2, 5, 6, 7) Thank you for catching those typos and mistakes! I'm surprised the compiler didn't issue any errors or warnings for them (other then 5, it would have caught that later though.)

re: 3) Ignore the extensions for now, It's very likely they will be changed/removed later. (Also, I though .hpp was perfectly fine for C++ Header files.)

re: 4) That's what I had originally (see previous posts,) and it's very likely where my compilation problem still lies (see the warning 6 posts up) as changing it to #include creates a circular dependency issue with files Array.hpp, String.hpp, and ArrayIndexOutOfBoundsException.hpp.

re: ...) (See "re: 1"; also, I can tell you took the time to read though the files, thank you very much.
Please don't give up. I'm sure I could fix these... in time, but I really am at a loss here. I've never had this kind of problem before. My only current solution is to push class declarations/definitions Array, String, ArrayIndexOutOfBoundsException, and method definitions for Array all into the same file, and I don't want to do that.

After I change line 28 in Array.hpp from class ArrayIndexOutOfBoundsException; to #include "ArrayIndexOutOfBoundsException.hpp" I get the following error when trying to compile Object.cpp:
Object.cpp
c:\users\christopher\cpp\rsc\mhc8\Exception.hpp(16) : error C2079: 'mhc8::mhc8::Exception::msg' uses undefined class 'mhc8::String'

I will update the file on my server for you all; they can still be found at http://www.mathhead200.com/cpp/mhc8.zip

At first changing it from an #include to a forward declare seemed weird to me (and it still does,) but it worked in the first small test case (first post,) so I though it was going in the right direction; but I've just changed it back. The errors from when it was a forward declaration are in a previous post.
Last edited on
Slightly offtopic, but:


Array, String, ArrayIndexOutOfBoundsException


These names look familiar. Are you simulating Java in C++? That won't work. You'll end up with all disadvantages of C++ and all disadvantages of Java together. Just for example - you can't implement most Java features in C++ (unless you are going to write a complete JVM - then: good luck) as efficiently as they are implemented in Java. E.g. adding manual bounds checking to arrays would make your arrays slower than in Java, because manual bound checking in C++ cost you some non-zero constant ovehead per each array access, while in Java these checks are in all reasonable use cases totally free. Or another example, you can't add automatic memory management to C++ without making it slow and unreliable.
Last edited on
Yes and no. I don't plan on adding "all" the features of Java, in fact I plan on adding very few of them. I prefer exception handling, so I'm setting up an array class that will throw exceptions instead of segfaulting. If I feel I don't need the two conditional check "overhead" in a particular application I won't use my Array class. I will not be adding auto GC, although I did make a test class once that worked like auto_ptr and it did just that, as in it remembered the number of copies made deleting the internal pointer only when 0 was reached. If I do bring something like that into this library, I would add keep it as a template class, i.e. an option.

If I want Java, I'd use it. In fact I do use it, more often on real project more then C++. But I needed to do something challenging in C++, and since I like the functionality of Java classes better, I though I'd give this a shot. Basically, I'm stealing (some of) the names from Java so I don't have to re-learn them, and because Java has though out there hierarchical structure already. (I very rarely use multiple inheritance in C++ so that won't be much of a problem.)

Also, how does adding auto GC make C++ unreliable? I think it could be done correctly, not saying I can.

Maybe we should move this to a different forum and topic?
Last edited on
Just out of curiosity why use goto here
1
2
3
4
5
6
7
while( n <= len ) {
	for( int i = 0; i < str.length(); i++ )
		if( *this[n + i] != str[i] )
			goto NEXT;
	return n;
	NEXT: n++
}

Wouldnt
1
2
3
4
5
6
7
while( n <= len ) {
	for( int i = 0; i < str.length(); i++ )
		if( *this[n + i] != str[i] )
			n++;
                       else
               		return n;
}
accomplish the same task?
No, it wouldn't.
1) Loop though string A by character (a).
2)     Loop though string B by character (b).
3)         If a != b, check the next character in A (continue with loop at 1.)
4)         Else, continue and check the next character in B (continue with loop at 2.)
5)     If we are here, all the characters in B were found in order in A. Return the starting index of the substring.
1
2
3
4
5
6
7
8
9
10
11
//This is how I would do it without goto, but to me it looks sloppier.
bool possibleMatch;
while( n <= len ) {
	possibleMatch = true;
	for( int i = 0; possibleMatch && i < str.length(); i++ )
		if( *this[n + i] != str[i] )
			possibleMatch = false;
	if( possibleMatch )
		return n;
	n++;
}

And it looks like I forgot another semicolon... :)
Last edited on
I remember a similar threath.
1
2
3
//String.hpp
#include "Array.hpp" //outside
#ifndef MHC8_STRING 


In Array.hpp you've got
1
2
3
4
namespace mhc8{
//...
#include "AIOOBE.hpp" //this shouldn't be inside the namespace
}
That was making an anidating namespace.

With that (an several minor fixes), Object.cpp compiled.
Other fixes:
1
2
3
4
5
6
7
8
9
10
class Exception{
~Exception() throw(); 
virtual const char* what() throw();
}
//.....
int Object::hashCode() const{
return (int) this;//error: cast from ‘const mhc8::Object*’ to ‘int’ loses precision
}

ostream& operator<< // streams have not a copy constructor 


PS: ¿you know about vector::at(), right?

rapidcoder wrote:
while in Java these checks are in all reasonable use cases totally free
¿Could you explain that? ¿How is it done?
Pages: 12