0

C:\CodeBlocks\kool\praks2\src..\include\circle2.h|8|error: 'Line2' does not name a type| C:\CodeBlocks\kool\praks2\src..\include\circle2.h|17|error: 'Line2' has not been declared| ||=== Build finished: 2 errors, 0 warnings ===|

circle2.h :

#ifndef CIRCLE2_H
#define CIRCLE2_H

#include "geometry.h"

class Circle2 {
    public:
        Vector2 p1;
        float r;

        Circle2();
        Circle2(Vector2 np1, float nr);
        float circumference();
        float area();
        bool contains(Vector2 v);
        bool contains(Line2 l);  // error is here.
        void scale(float factor);
        friend ostream& operator <<(ostream& out, const Circle2& cir);

};

#endif // CIRCLE2_H

circle.cpp:

bool Circle2::contains(Line2 l) {
    return 0;
}

geometry.h:

#ifndef GEOMETRY_H
#define GEOMETRY_H

// These are needed to use the functions in our library
#include <iostream>
using namespace std;

// Include own headers
// NB! Add your own headers here!
#include "vector2.h"
#include "line2.h"
#include "circle2.h"


#endif // GEOMETRY_H

This is circle2.cpp:

#include "../include/circle2.h"
#include <math.h>


Circle2::Circle2() {
    p1 = Vector2();
    r = 0;
}

Circle2::Circle2(Vector2 np1, float nr) {
    p1 = np1;
    r = nr;
}

float Circle2::circumference() {
    return 2 * r * M_PI;
}
float Circle2::area() {
    return pow(r, 2) * M_PI;
}
bool Circle2::contains(Vector2 v) {
    if(p1.distanceFrom(v) <= r) return 1;
    return 0;
}
bool Circle2::contains(Line2 l) {
    return 0;
}
void Circle2::scale(float factor) {
    r *= factor;
}
ostream& operator<<(ostream& out, const Circle2& cir) {
    out << "(" << cir.p1 << ", " << cir.r << ")";
    return out;
}

line2.cpp:

    #include "../include/line2.h"
#include <math.h>


Line2::Line2() {
    p1 = Vector2();
    p2 = Vector2();
}

Line2::Line2(Vector2 np1, Vector2 np2) {
    p1 = np1;
    p2 = np2;
}

float Line2::length() {
    return p1.distanceFrom(p2);
}

ostream& operator<<(ostream& out, const Line2& line) {
    out << "(" << line.p1 << " - " << line.p2 << ")";
    return out;
}

line2.h:

    #ifndef LINE2_H
#define LINE2_H

#include "geometry.h"

class Line2 {

    public:
        Vector2 p1;
        Vector2 p2;

        Line2();
        Line2(Vector2 np1, Vector2 np2);
        float length();
        friend ostream& operator <<(ostream& out, const Line2& line);
};
#endif // LINE2_H
Jaanus
  • 16,161
  • 49
  • 147
  • 202
  • Why not circle2.h not included in circle.cpp – Zimbabao Feb 20 '11 at 16:40
  • where is declaration of Line2? – Vivek Goel Feb 20 '11 at 16:41
  • 1
    show us `line2.h`. Also, I recommend not putting `using namespace std` in your header files. – Marlon Feb 20 '11 at 16:45
  • ok i added line2.h, ill look into the namespace std thing – Jaanus Feb 20 '11 at 16:52
  • See Tim's answer below. Note that by #including geometry.h in line2.h, you are saying that Line2 _depends_ on every name declared in geometry. In particular, you are saying that if there is any change to Circle2, you insist the compiler take a fresh look at Line2, and at anything that uses Line2. But why? Why should even a dramatic, substantial change in Circle2 have any impact on Line2? – Thomas L Holaday Feb 20 '11 at 17:35
  • i removed `using namespace::std` and just added alot of `std::` infront of my ostream functions – Jaanus Feb 21 '11 at 09:07

3 Answers3

2

You've currently got everything trying to include everything else. This will get you into trouble.

It looks like Line2 needs to know about Vector2; and Circle2 needs to know about both Line2 and Vector2. Does Vector2 stand on its own, or does it try to use Line2 or Circle2?

If it stands on its own, then you simply need to have Line2 include Vector2, and Circle2 include Line2 and Vector2. Then remove the other include statements that have you running around in circles (e.g. Circle2 includes geometry which includes Circle2...).

If Vector2 tries to use Line2 or Circle2, you'll have a circular dependency. Here's how to solve that:

  • remove the #include statements from the header files (just your geometry/Circle2/etc)
  • forward declare the classes you need in each header (see Baget's answer)
  • change the method signature to take a constant reference as a parameter (e.g. instead of taking Line2, take const Line2&)
  • in the source files, #include the header files of the shapes you need.

The way this works is that the header files no longer have a dependency on the other shapes, so it breaks the circular include cycle. Circle2.cpp includes Line2.h, but Line2.h doesn't include Circle2.h, so the cycle ends there.

Forward declaring the class tells the compiler "there is a class called Line2, but you don't need to know any details about it yet". Changing the methods to use references (or pointers) allows the compiler to not need any class details when processing the header.

When the compiler processes the source (where you'll actually do something with the other class), it now needs to know the details of the other class. This is why you need to include the full class definition in the source file.

EDIT: a little more explanation about what happens with include guards and circular includes.

I'll use the simplest case: A.h includes B.h and B.h includes A.h.

When you attempt to compile A.obj, the preprocessor loads all the include statements, and basically does a cut-n-paste of the included file into the source file (and then hands the result to the compiler). So here's the order this will be processed:

  • In A.cpp, it reaches #include "A.h", where it jumps out of A.cpp, and loads A.h
  • In A.h, it reaches #ifndef A_H. Since that's not set, it continues.
  • In A.h, it reaches #define A_H. Now that's set.
  • In A.h, it reaches #include "B.h", where it jumps out of A.h, and loads B.h
  • In B.h, it reaches the include guard for B_H, which wasn't set, and sets it.
  • In B.h, it reaches #include "A.h", where it jumps out of B.h and loads A.h (again)
  • In A.h, it reaches #ifdef A_H. Since that IS set, it skips until #endif - at the end of the file.
  • Back to B.h, preprocessing continues.

So you can see here that B.h didn't even need to include A.h, since that did nothing anyway. But if you try to take it out, then B.obj won't compile. And when you try to compile B.obj, A.h doesn't need to include B.h. This leads to a weird scenario:

  • when you compile A.obj, the compiler will see the declarations in B.h before A.h.
  • when you compile B.obj, the compiler will see the declarations in A.h before B.h.

In order for this to work, two things must be true:

  • to compile A.obj, the compiler has to be able to compile B.h without A.h
  • to compile B.obj, the compiler has to be able to compiler A.h without B.h

So, to build your whole project, A.h and B.h can't actually rely on including each other. This is where the importance of forward declarations comes into play. This is where you can tell B.h about class A; - without actually using the include statement. Of course you have to follow the rules for forward declarations - only references or pointers, no dereferencing.

Note that if you add the forward declaration, without removing the #include statements, your project will build. Because (as I showed above), sometimes that #include statement does nothing.

I hope that clears things up.

Tim
  • 8,912
  • 3
  • 39
  • 57
  • well at the moment my Line2.h and Circle2.h include the geometry.h which includes the Line2.h and Circle2.h and etc etc..does that make a neverending circle or do the `#ifndef CIRCLE2_H #define CIRCLE2_H #endif` end the circle? – Jaanus Feb 21 '11 at 09:10
  • The `ifndef XXX_H / #define XXX_H` - also known as an include guard - does "break the circle", and allows the precompiler to finish processing. The intent of an include guard is to protect you from including a file twice (e.g., A.h includes B.h and C.h, but B.h also includes C.h). So the fact that it breaks a circular-include is more of a side-effect, than the main purpose. Having a circular-include chain indicates bad design, causes problems, and is completely unnecessary. – Tim Feb 21 '11 at 18:00
1

you can try to add:

class Line2;

before the

class Circle2 {
Baget
  • 3,318
  • 1
  • 24
  • 44
  • Forward declarations only work for pointers and references. His method takes a Line2 by value, so it needs the full class definition. – Tim Feb 20 '11 at 16:45
  • well this got rid of the error by adding that line, but it looks rather messy now, any prettier solution? – Jaanus Feb 20 '11 at 17:06
  • The prettiest solution is that each file #include only the .h files upon which it depends, as Tim describes in his answer. If someone adds Pentagon2, Triangle2, or ConicSection2, why should line2.cpp require a recompilation? – Thomas L Holaday Feb 20 '11 at 17:50
0

Make sure you've #included a definition of the Line2 class.

Bogatyr
  • 19,255
  • 7
  • 59
  • 72