0

I'm starting a C++ OpenGL project, and I'm beginning with two .cpp files, Scene and boilerplate (with no .h files!). boilerplate contains OpenGL procedures that simply setup the environment.

I thought I'd move them to this separate file to give myself more room in Scene, but now that I've done so and attempted to #include this file in Scene, I'm getting 'duplicate symbol' errors when linking, e.g.

Scanning dependencies of target Coursework
[ 33%] Building CXX object CMakeFiles/Coursework.dir/Scene.cpp.o
[ 66%] Linking CXX executable Coursework
duplicate symbol __Z12checkGLErrorv in:
    CMakeFiles/Coursework.dir/Scene.cpp.o
    CMakeFiles/Coursework.dir/boilerplate.cpp.o
duplicate symbol __Z5setupv in:
    CMakeFiles/Coursework.dir/Scene.cpp.o
    CMakeFiles/Coursework.dir/boilerplate.cpp.o
duplicate symbol _height in:
    CMakeFiles/Coursework.dir/Scene.cpp.o
    CMakeFiles/Coursework.dir/boilerplate.cpp.o
...

Every 'duplicate symbol' comes from the definitions in boilerplate.cpp.

Why am I getting these errors? I thought #includeing is effectively the same as copy-and-pasting that file's code into the current file, yet when I actually do this it builds just fine.

I'm using CMake to build, in case this is relevant (it is a linking error so it could be to do with this?).


Here are the files for reference,

Scene.cpp:

#include <iostream>
#include <glut/glut.h>

#include "boilerplate.cpp"

void draw() {

    glClearColor(0.2f, 0.2f, 0.2f, 1.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // make beautiful models

    glutSwapBuffers();
}

int main(int argc, char **argv) {

    glutInit(&argc, argv);         
    setup();                        

    glutDisplayFunc(draw);          

    checkGLError();                 
    glutReshapeFunc(reshape);
    glutMainLoop();                 

    return 0;
}

boilerplate.cpp:

#include <glut/glut.h>
#include <cstdio>

int width, height;

void checkGLError() {

    int e = 0;
    GLenum error = glGetError();

    while (GL_NO_ERROR != error) {
        e++;
        printf("GL Error %i: %s\n", e, gluErrorString(error));
        error = glGetError(); 
    }
}

void setup() {

    width = 800;                                    
    height = 800;                                   

    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE);  
    glutInitWindowSize(width, height);           
    glutCreateWindow("Coursework");                 
}

void reshape(int _width, int _height) {

    height = _height;
    width = _width;

    GLdouble aspect = static_cast<GLdouble>(width) / static_cast<GLdouble>(height);

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();  
    gluPerspective(45.0, aspect, 1, 1000);

    glMatrixMode(GL_MODELVIEW);  
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.9)
project(Coursework)

set(CMAKE_CXX_STANDARD 11)

find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)

# Put directories in project's 'include path'
include_directories(${OPENGL_INCLUDE_DIRS} ${GLUT_INCLUDE_DIRS})

# Declare the var 'SOURCE_FILES', and give it all relevant source filenames
set(SOURCE_FILES Scene.cpp boilerplate.cpp)

add_executable(${PROJECT_NAME} ${SOURCE_FILES})

target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES} ${GLUT_LIBRARY})
Edward Gargan
  • 153
  • 10
  • 3
    Don't `#include` a .cpp file! You need to make a header file with just the declarations for `boilerplate.cpp`. – Fred Larson Apr 09 '18 at 21:05
  • Do **not** include `.cpp` files in other files – Cory Kramer Apr 09 '18 at 21:05
  • I thought there was no real difference between .h and .cpp files though, and that it was just 'good practice' to separate interface from implementation? – Edward Gargan Apr 09 '18 at 21:07
  • 1
    This question, though closed, might help: https://stackoverflow.com/q/333889/10077 – Fred Larson Apr 09 '18 at 21:09
  • 3
    Of course there's no difference between files. One file is no different than any other file. It's just that what's inside the file that matters. If you include something that defines a symbol, that symbol gets defined by every file that includes. Hence, the duplicate symbols. `.h` files contains only declarations, and not complete definitions. It's not the difference between the filename extensions, it's their content that matters. – Sam Varshavchik Apr 09 '18 at 21:09
  • This one too is pretty similar to your question: https://stackoverflow.com/questions/49741617/c-include-causing-duplicate-symbols – jhh Apr 09 '18 at 21:09
  • @jhh: That *IS* this one! – Fred Larson Apr 09 '18 at 21:14
  • @SamVarshavchik I don't have a corresponding ```.h``` file for ```boilerplate.cpp```, so to fix this I should make one, that just contains just the declarations of ```boilerplate.cpp```'s procedures, then ```#include``` this instead? – Edward Gargan Apr 09 '18 at 21:15
  • @EdwardGargan I suggest you get good C++ reading material, and not try to learn C++ by winging it or lifting snippets of code from a website. – PaulMcKenzie Apr 09 '18 at 21:24

2 Answers2

2

You are right when you say that #include-ing is just like copy-pasting the contents at the respective position.

But if you include a file that does not just contain declarations and class definitions but also implementations in the sense of variable definitions or function/methods with a body, then you might get into troubles if...

a.) you either include the same boilerplate.cpp in several other files; this will lead to redefining the same things -> duplicate symbol errors

b.) you include the boilerplate.cpp only in one file, but the compiler treats the boilerplate.cpp file also as separate translation unit -> duplicate symbols

So avoid including .cpp-files; rather expose the functions provided by that .cpp file as function prototypes (i.e. functions without a body) in an appropriate .h-file; include only the .h-files then.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • For some reason, removing ```boilerplate.cpp``` from the list of source files CMake can see has fixed this issue. Have I just dug myself into a horrible program design? – Edward Gargan Apr 09 '18 at 21:20
  • Probably yes :-) Removing the file from the source file list accords to situation (b) mentioned in the answer; If it is a boilerplate, I assume that there is no "boilerplate.h" provided; in this case you should copy/paste the relevant portions of the boilerplate code into one of your "own" source files rather than including a "boilerplate.cpp"... – Stephan Lechner Apr 09 '18 at 21:27
  • But if I'm _sure_ that I will only include this boilerplate once, ```#include```ing it here is technically OK to do? And yep there is no ```.h``` file, just the ```.cpp```. – Edward Gargan Apr 09 '18 at 21:38
  • It's technically correct; It is just a bit "unusual" and might therefore confuse readers or your code... – Stephan Lechner Apr 09 '18 at 21:41
0

My problem was that I was looking to use #include simply as a copy-and-paste tool, yet I was giving CMake boilerplate.cpp as a separate executable file, and attempting to both link it and #include it in Scene.cpp caused these duplicate symbol errors.

Removing boilerplate.cpp from CMake, i.e. removing it from

set(SOURCE_FILES Scene.cpp boilerplate.cpp)

solved the problem and allowed me to build it.

But as the comments say, the 'proper' way to have done this would have been to give boilerplate.cpp a header file and include this instead. I couldn't understand why this is the preferred way until I read this:

Why should I not include cpp files and instead use a header?

Edward Gargan
  • 153
  • 10