1

My assignment is to keep a circular dependency while breaking down a code to multiple files , the code is about a billiard simulation using object orientation , but my problem is not the code but the compilation.

I have wrestled with this and kind of resolved my issue using this post: Resolve build errors due to circular dependency amongst classes

but I keep getting compilation errors after "make" command that says I have defined somethings multiple times ; this is while I have used include guards that prevent the program from including definitions more than once, I somehow feel I have made a small mistake somewhere and I don't seem to find it .

These are my files

ball.cpp

#ifndef BALL_CPP
#define BALL_CPP
#include "ball.hpp"
#include <iostream>
using namespace std;
Ball::Ball(double _x, double _y, double _vx, double _vy, Table *t)
{
     cout << "Ball constructor called" << endl;
     table = t;
     set_location(_x, _y);
     set_speed(_vx, _vy);
     cout << "Initial position is: (" << x << ',' << y << ')' << endl;
}
void Ball::set_location(double _x, double _y)
{
     cout << "Ball::set_location called with (" << _x << ',' << _y << ')' << endl;
     if (!table->contains_point(_x, _y))
          abort();
     x = _x;
     y = _y;
}
void Ball::set_speed(double _vx, double _vy)
{
     cout << "Ball::set_speed called with (" << _vx << ',' << _vy << ')' << endl;
     vx = _vx;
     vy = _vy;
}

void Ball::move(double dt)
{
     cout << "Ball::move called" << endl;
     x += vx * dt;
     y += vy * dt;

     if (!table->contains_point(x, y))
          table->reflect(this);
     cout << "New position is: (" << x << ',' << y << ')' << endl;
}
#endif

ball.hpp


#ifndef BALL_HPP
#define BALL_HPP
#include "table.hpp"
class Ball
{
public:
     Ball(double _x, double _y, double _vx, double _vy, Table *t);
     void move(double dt);
     double get_x() { return x; }
     double get_y() { return y; }
     double get_vx() { return vx; }
     double get_vy() { return vy; }
     void set_location(double _x, double _y);
     void set_speed(double _vx, double _vy);

private:
     double x;
     double y;
     double vx;
     double vy;
     Table *table;
};
#endif

table.cpp:

#ifndef TABLE1CPP
#define TABLE1CPP
using namespace std;
#include <iostream>
#include "ball.hpp"
Table::Table(double w, double h)
{
     cout << "Table constructor called" << endl;
     if (w <= 0 || h <= 0)
          abort();
     width = w;
     height = h;
}
bool Table::contains_point(double x, double y)
{
     cout << "Table::contains_point called" << endl;
     return x >= 0 && x < width && y >= 0 && y < height;
}
void Table::reflect(Ball *b)
{
     cout << "Table::reflect called" << endl;
     double x = b->get_x();
     double y = b->get_y();
     double vx = b->get_vx();
     double vy = b->get_vy();

     while (!contains_point(x, y))
     {
          if (x < 0)
          {
               x = -x;
               vx = -vx;
          }
          if (x >= width)
          {
               x = 2 * width - x;
               vx = -vx;
          }
          if (y < 0)
          {
               y = -y;
               vy = -vy;
          }
          if (y >= height)
          {
               y = 2 * height - y;
               vy = -vy;
          }
     }
     b->set_location(x, y);
     b->set_speed(vx, vy);
}
#endif

table.hpp:

#ifndef TABLE_HPP
#define TABLE_HPP
class Ball;
class Table
{
public:
     Table(double w, double h);
     bool contains_point(double x, double y);
     void reflect(Ball *b);

private:
     double width;
     double height;
};
#endif

And my makefile looks like this :(I am very new to the multi file concept and very inexperienced so i might have a blunder here that I don't understand )

makefile :

output: main.o table.o ball.o
    g++ main.o ball.o table.o -o billiards
main.o: main.cpp
    g++ -c main.cpp
ball1.o:    ball.cpp ball.hpp
    g++ -c ball1.cpp
table1.o: table.cpp table.hpp
    g++ -c table.cpp
clean:
    rm * .o output

main.cpp :

#include <iostream>
#include <cstdlib>
#include "ball.cpp" // should be changed to "ball.hpp"
#include "table.cpp" // should be changed to "table.hpp"
using namespace std;
int main()
{
     Table t(100, 50);
     Ball b(10, 20, 25, 5, &t);
     b.move(10);
}



the errors :


/usr/bin/ld: ball.o: in function `Ball::Ball(double, double, double, double, Table*)':
ball.cpp:(.text+0x0): multiple definition of `Ball::Ball(double, double, double, double, Table*)'; main.o:main.cpp:(.text+0x0): first defined here
/usr/bin/ld: ball.o: in function `Ball::set_location(double, double)':
ball.cpp:(.text+0x10c): multiple definition of `Ball::set_location(double, double)'; main.o:main.cpp:(.text+0x10c): first defined here
/usr/bin/ld: ball.o: in function `Ball::set_speed(double, double)':
ball.cpp:(.text+0x1dc): multiple definition of `Ball::set_speed(double, double)'; main.o:main.cpp:(.text+0x1dc): first defined here
/usr/bin/ld: ball.o: in function `Ball::Ball(double, double, double, double, Table*)':
ball.cpp:(.text+0x0): multiple definition of `Ball::Ball(double, double, double, double, Table*)'; main.o:main.cpp:(.text+0x0): first defined here
/usr/bin/ld: ball.o: in function `Ball::move(double)':
ball.cpp:(.text+0x280): multiple definition of `Ball::move(double)'; main.o:main.cpp:(.text+0x280): first defined here
/usr/bin/ld: table.o: in function `Table::Table(double, double)':
table.cpp:(.text+0x0): multiple definition of `Table::Table(double, double)'; main.o:main.cpp:(.text+0x3be): first defined here
/usr/bin/ld: table.o: in function `Table::Table(double, double)':
table.cpp:(.text+0x0): multiple definition of `Table::Table(double, double)'; main.o:main.cpp:(.text+0x3be): first defined here
/usr/bin/ld: table.o: in function `Table::contains_point(double, double)':
table.cpp:(.text+0x7c): multiple definition of `Table::contains_point(double, double)'; main.o:main.cpp:(.text+0x43a): first defined here
/usr/bin/ld: table.o: in function `Table::reflect(Ball*)':
table.cpp:(.text+0x10a): multiple definition of `Table::reflect(Ball*)'; main.o:main.cpp:(.text+0x4c8): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:2: output] Error 1

  • 3
    You don't have to put include guards in .cpp files BTW, only for header files. – mediocrevegetable1 Apr 30 '21 at 07:51
  • Remove the include guards from the .cpp file. That is not the place where they're supposed to be used. In addition, get rid of the `using namespace std;` before you've even included any standard headers. – PaulMcKenzie Apr 30 '21 at 07:51
  • @mediocrevegetable1 Well I did that and it did not seem to change anything , I had put them for test in the first place thought the program was repeating the definitions in them not the header philes . – Pouriyatajmehrabi Apr 30 '21 at 07:52
  • 4
    The *main.o:main.cpp:(.text+0x0)(* in the error message suggests that somehow ball.cpp is being included in main.cpp. Don't do that. Compile and link cpp files. Include headers. – user4581301 Apr 30 '21 at 07:52
  • On an unrelated note, in the `makefile` you have an `output` target to build the `billiards` program. That target should be `billiards` instead, as in `billiards: main.o table.o ball.o` – Some programmer dude Apr 30 '21 at 07:54
  • @Someprogrammerdude My bad , forgot to put it . Its now there – Pouriyatajmehrabi Apr 30 '21 at 07:54
  • @Pouriyatajmehrabi -- Why are you including the .cpp files in `main.cpp`? I am sure the C++ book you're using to learn from, when including files, include the *header* files, not .cpp files. – PaulMcKenzie Apr 30 '21 at 07:55
  • 3
    In main cpp replace `#include "ball.cpp"` with `#include "ball.hpp"` and replace `#include "table.cpp"` with `#include "table.hpp"`. Problem will go away. You may get different problems, but that would be a new question. – user4581301 Apr 30 '21 at 07:56
  • 1
    @user4581301 your last comment did the trick , the problem stemmed from me not knowing how a make file and in general the compiler works (the linking and the header inclusion ) .Sorry for being such a noob and thanks for your help . – Pouriyatajmehrabi Apr 30 '21 at 08:00
  • Makefile was correct the problem was in what happens when you use `#include`. The compiler (preprocessor, really) replaces the include statement with a copy of the included file. This leaves you with two copies of ball.cpp, ball.cpp and the copy pasted into main. – user4581301 Apr 30 '21 at 08:03
  • @PaulMcKenzie Unfortunately I am learning from some TAs at our university which don't really explain the concept well or if they do , they did not explain circular dependency and in general how Make file works so I got very confused with the include process. – Pouriyatajmehrabi Apr 30 '21 at 08:03
  • @user4581301 Could I use Pragma Once here ? is Pragma once in general a better way to guard from double inclusion ? – Pouriyatajmehrabi Apr 30 '21 at 08:05
  • A `#pragma once` include guard does the same job as an the `#ifdef` include guard, but in a different way. And both have different failure cases, but neither of them can nhelp you here. An include guard prevents inclusion twice in the same [translation unit](https://en.wikipedia.org/wiki/Translation_unit_(programming)), but main.cpp and ball.cpp are different translation units. – user4581301 Apr 30 '21 at 08:09
  • Some extra reading: [#pragma once vs include guards?](https://stackoverflow.com/questions/1143936) and – user4581301 Apr 30 '21 at 08:11
  • [Why aren't my include guards preventing recursive inclusion and multiple symbol definitions?](https://stackoverflow.com/questions/14909997/why-arent-my-include-guards-preventing-recursive-inclusion-and-multiple-symbol) – user4581301 Apr 30 '21 at 08:14

1 Answers1

3

Okay, so, this is what's happening: main.cpp is pulling ball.cpp and table.cpp, so the object file main.o will have the full implementations of all the functions and variables you implemented in ball.cpp and table.cpp. This is an issue, because you also separately compile ball.cpp and table.cpp, so you end up with all the duplications.

I'm guessing you put the include guards in the .cpp files to avoid this problem, however, this doesn't actually work: #define'd macros won't carry over between invocations of the compiler.

What you want to do here is #include the .hpp files instead: main.cpp will be able to see the declarations of the functionalities you're using, but it won't compile the implementations into main.o, so there won't be any conflicts with ball.o or table.o. Additionally, you should probably take the include guards out of the .cpp files: for exactly these reasons of duplication, #includeing C or C++ source files generally isn't done except in a few odd cases which aren't really relevant to your particular situation.

Lux
  • 1,525
  • 1
  • 23
  • 27