5

I am trying to build a C++ project in Xcode 4.6.3.

In my project (the beginnings of a very simple OpenGL game) I have two files:

textures.h:

#pragma once

#include <GLUT/GLUT.h>

void load(); // load textures

GLuint dirt, water; // variables to store texture handles

textures.cpp:

#include "textures.h"

#include "util.h"

void textures::load() {
    dirt = util::loadTexture("/Some/Path/Soil.png");
    water = util::loadTexture("/Some/Path/Water_fresh.png");
}

Here util.h defines the util::loadTexture function.

There are two files that include textures.h. The first (main.cpp) calls the load() function as part of initialization and accesses the dirt variable to bind the Soil.png texture. The second (Chunk.cpp) includes textures.h, but doesn't actually access anything from it yet.

When I try to build the project, it gives me the following error:

duplicate symbol _dirt in:
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/main.o
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/Chunk.o
duplicate symbol _water in:
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/main.o
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/Chunk.o
duplicate symbol _dirt in:
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/main.o
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/textures.o
duplicate symbol _water in:
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/main.o
    /Users/me/Library/Developer/Xcode/DerivedData/OpenGL_Testing-epporrdukapbwzawfhiwnlztzdns/Build/Intermediates/OpenGL Testing.build/Debug/OpenGL Testing.build/Objects-normal/x86_64/textures.o
ld: 4 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I understand that this means there is a duplicate somewhere, or a header is included multiple times. But all of my headers use #pragma once, and I have done search in workspace for "dirt" and such, and there are no other definitions. My other headers work fine, even the ones that are included multiple times. I have googled this problem many times with different keywords, and have taken a look at other similar questions, but all I found was this SO question.

I have encountered other "random" errors before in Xcode - for example, one project kept trying to use a dynamic library that I had deleted and replaced with a static one. The error stayed, even when I created a brand new project. It worked when compiled manually from Terminal.

What am I missing?

Community
  • 1
  • 1
qxz
  • 3,814
  • 1
  • 14
  • 29

1 Answers1

5

#pragma once does not stop a header file being included multiple times.

It stops a header file being included multiple times when compiling a single file. You have (at least) three files so you are declaring the same variables 3 times.

Don't put variable definitions in a header file. Do this instead

// textures.h
extern GLuint dirt, water; // variable declarations


// textures.cpp
GLuint dirt, water; // variable definitions

You can have as many declarations of a variable as you want (provided they are all identical), so it's OK to put declarations in a header file, you must have exactly one variable definition, so you put that in a .cpp file.

The key to remember is that every .cpp file is compiled completely independently of every other .cpp file, this is called separate compilation. So your idea that #pragma once would stop a header file from being included by one .cpp file just because it had been included in another .cpp file just doesn't work. You're not the first to misunderstand this.

john
  • 85,011
  • 4
  • 57
  • 81
  • I have already tried substituting #pragma once for the #ifndef #define #endif that Xcode initially creates, and it still didn't work... – qxz Sep 21 '13 at 06:43
  • Well #ifndef ... works exactly the same as #pragma once, and fails to do what you think it should do for the same reason. What I posted isn't a tip, it's the correct way to use global variables. – john Sep 21 '13 at 06:44
  • Don't stop using #pragma once or #ifndef ... its just that solves a different problem from what you think it does. – john Sep 21 '13 at 06:47
  • Then what exactly do I have to do? Also, #pragma once has worked for my other files, and a quick google search seems to say that #pragma once does prevent multiple inclusion. Although you probably know more than me – qxz Sep 21 '13 at 06:48
  • @qxz Not like this it doesn't Do you know what a translation unit is? #pragma once and header-fencposts prevent multi-inclusion in a single translation unit. It does not, and *cannot* prevent duplicate *definitions* linked in by multiple compiled translation units, each with their own "copy" of what is *supposed* to be a global variable. This answer is correct. – WhozCraig Sep 21 '13 at 06:50
  • I said it in my answer, which bit didn't you understand? Put `extern GLuint dirt, water;` in textures.h and put `GLuint dirt, water;` in textures.cpp – john Sep 21 '13 at 06:50
  • A quick google for pragma once, top hit says 'In the C and C++ programming languages, #pragma once is a non-standard but widely supported preprocessor directive designed to cause the current source file to be included **only once in a single compilation**.' You have three cpp files, therefore you have three compilations, therefore your header file is being included three times, even with pragma once. – john Sep 21 '13 at 06:56
  • OH. Sorry I was a bit slow. – qxz Sep 21 '13 at 06:57
  • I guess with modern IDEs like Xcode you can lose sight of the fact that building a program is a multi-step process. Each .cpp file is compiled separately and then there's a linking step at the end. You just hit a button and your program builds. But you still have to understand what's going on under the hood when issues like this arise. – john Sep 21 '13 at 07:01