0

I am having a c++ linking problem. I am trying to define a Point2D struct for all of my other files to use. Here it is.

#ifndef Point2D_h
#define Point2D_h

#include <iostream>

struct Point2D {
    float x;
    float y;


    Point2D(float x, float y) : x(x), y(y) {}

    Point2D() : x(0), y(0) {}

    Point2D operator+(const Point2D& a) const {
        return Point2D(a.x + x, a.y + y);
    }

    ….
};

std::ostream& operator<<(std::ostream& os, const Point2D& m) {
    return os << "(" << m.x << ", " << m.y << ")";
}

#endif /* Point2D_h */

The only problem is that I get a duplicate symbol Point2D error when I try to build. I am not sure why Point2D would be a duplicate symbol since it is header guarded

Here are all my other files, whether they are header guarded and whether they use Point2D and of course their imports:

Global.h (Header Guarded)

#include <stdio.h>
#define MAX 9000

Engine Core (Header Guarded)

#import "Global.h"
#import "Tests/EngineTests.hpp"
#import "Engine/Engine.hpp"
#import "Particle/Particle.hpp"

Tests.cpp (Uses Point2D)

#include "EngineTests.hpp"
#include <iostream>
#include "../Engine/Engine.hpp"

Tests.h

#include "Global.h"

Engine.cpp

#include "Engine.hpp"
#include <iostream>

Engine.hpp (Header guarded)

#include "Global.h"
#include "../Particle/ParticleManager.hpp"

Particle.cpp

#include "Particle.hpp"
#include <iostream>

Particle.h (Header guarded, uses Point2D)

#include "Global.h"
#include "../Math/Point.h"

ParticleManager.cpp

#include "ParticleManager.hpp"
#include <iostream>

ParticleManager.h (Header guarded)

#include <stdio.h>
#include "Global.h"
#include "Particle.hpp"

And the linker says the following

duplicate symbol __ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEERK7Point2D in:
    …/arm64/Particle.o
    …larm64/Engine.o
duplicate symbol __ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEERK7Point2D in:
    …/Particle.o
    …/arm64/EngineTests.o
duplicate symbol __ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEERK7Point2D in:
    …/arm64/Particle.o
    …/ParticleManager.o
ld: 3 duplicate symbols for architecture arm64
J.Doe
  • 1,502
  • 13
  • 47
  • 2
    The error is for your `operator<<` function. Don't define functions in header files. (Also, header guards don't protect against linker errors.) – melpomene Aug 11 '18 at 00:26
  • 1
    Header guards stop a header from being included multiple times in one compilation unit (or cpp file if you prefer), they don't prevent the header from being included when other files are being compiled. – Retired Ninja Aug 11 '18 at 00:26
  • https://stackoverflow.com/questions/6964819/function-already-defined-error-in-c – Retired Ninja Aug 11 '18 at 00:28
  • https://stackoverflow.com/questions/6469849/one-or-more-multiply-defined-symbols-found – melpomene Aug 11 '18 at 00:31
  • One resolution is to add the keyword `inline` to the definition of `operator<<`. Another resolution is to just declare it in the header, and provide the definition in the .cpp file. – Cheers and hth. - Alf Aug 11 '18 at 00:34

1 Answers1

3

Short-term solution: use the inline keyword for functions defined in header files

inline Point2D operator+(const Point2D& a) const {
    return Point2D(a.x + x, a.y + y);
}

inline std::ostream& operator<<(std::ostream& os, const Point2D& m) {
    return os << "(" << m.x << ", " << m.y << ")";
}

Long term: Don't have any code inlined unless it's a measurable performance degradation. (Code in header files is forbidden on my team). Have your header files as follows:

#ifndef Point2D_h
#define Point2D_h

#include <iostream>
struct Point2D {
    float x;
    float y;

    Point2D(float x, float y);    
    Point2D();

    Point2D operator+(const Point2D& a) const;

    ….
};

std::ostream& operator<<(std::ostream& os, const Point2D& m);

And a corresponding Point2d.cpp file:

Point2D::Point2D(float x, float y) : x(x), y(y) {}

Point2D::Point2D() : x(0), y(0) {}

Point2D::Point2D operator+(const Point2D& a) const {
    return Point2D(a.x + x, a.y + y);
}

std::ostream& operator<<(std::ostream& os, const Point2D& m) {
    return os << "(" << m.x << ", " << m.y << ")";
}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • I would switch what's “long term” and what's “short term”. E.g. separate compilation still lacks good tool support for browsing code, and using libraries structured that way is still a PITA. – Cheers and hth. - Alf Aug 11 '18 at 00:38