问题
I am having a problem with a large project on visual studio 2005 on which I have run out of ideas.
I can't even put a working code snippet because I don't know what's related, but I will try:
I needed to make each .cpp file in my project have its own ID number, and create an instance of an object (which is globally accessible) that knows that ID. I followed the help on the accepted answer on this thread How to manage file unique IDs in c++ and made it work in a sandbox environment.
Adding files, giving them a unique #define FILEID (FileId::ID_FileName)
and then accessing their instance works fine on the sandbox.
Now comes the trouble - I pasted the code that makes files know their IDS to the main project, and compiled.
So far so good.
Now, I added to one of the existing .cpp files in the project:
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
Still compiles, links, all good.
Adding these lines to a (any) second .cpp file in the project now gives link error:
in which:
- name1: 1st file I added the lines to (alphabeticcaly)
- name2: other unrelated filename (which can also be the 2nd file I added the lines to, but may as well be just some other file)
The error
in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance@$Manager@$02@@SAAAVInstance@@XZ) already defined in name1.obj
Some times the error is only in the second file, and sometimes (between consecutive builds without changes) the error appears on every .cpp file in the folder.
Looking in the intermediate file (the preprocessor output) on the files to which I added the lines shows exactly one appearance of the
template <>
Instance &Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
with the correct FileId::ID_FileName, which is a different name than that of the other file. Still, the linker thinks the same FileId is used in more than one file.
On unrelated files (which also give the exact same error), there is no appearance of getInstance()
at all. Apparently, there shouldn't be a reason for the linker to shout there.
I checked, and no .cpp files include each other somewhere in the project.
I am completely out of ideas as to what could cause this and would appreciate any help.
EDIT 1
ids.h
enum FileId{
ID_file1ID=3,//just to see a non zero number in the debugger, which I do
ID_file2ID,
//and so on
FileIdSize
}
EDIT 2
When these errors start, the compiler starts to behave extremely unexpectedly.
Adding the line sdfsdfgasaedfahjk
to any file STILL COMPILES AND PASSES.
it clearly states the file name to which the line has been added to compiles. It clearly states it links to it. It passes.
I now can't trust the compiler.
No idea what's going on.
回答1:
You have 2 cpp files defining the FILEID
to the same value 3
.
As for a MCVE:
ids.h:
#pragma once
#define File1 3
#define File2 3 //<--same value on purpose
global.h
struct Instance
{
};
struct Factory
{
Instance getInstance(int FileID) { return Instance(); }
};
template <int ID>
struct Manager
{
Factory factory;
Instance& getInstance();
Factory& getTheFactory() { return factory; }
};
template <>
Instance& Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
name1.cpp
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
name2.cpp
#include "ids.h"
#define FILEID File2 // The FileId corresponding to this file
#include "global.h"
As this compiles there is a special implementation for Manager<3>::getInstance(void)
created for both name1.cpp
and name2.cpp
.
You can't use the same value for FILEID
in 2 different compilation units.
EDIT: Check values while compiling
Requires the preprocessor definition __BASE_FILE__="%(Filename)%(Extension)"
(Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions)
template <>
Instance& Manager<FILEID>::getInstance()
{
#define _STR(x) #x
#define STR(x) _STR(x)
#define CHECK_ID() __pragma(message("Initializing \"Instance& Manager<FILEID>::getInstance()\" with FILEID="STR(FILEID)" in "STR(__BASE_FILE__)))
CHECK_ID()
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
Example-Output:
1>------Build started : Project : Test_Call, Configuration : Debug Win32------
1> name1.cpp
1> Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file1ID in "name1.cpp"
1> name2.cpp
1> Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file2ID in "name2.cpp"
1> Test_Call.vcxproj-><Project>\Debug\Test_Call.exe
== == == == == Build: 1 succeeded, 0 failed, 0 up - to - date, 0 skipped == == == == ==
EDIT: Using FileId values as template parameter (MSVE)
id.h
#pragma once
enum FileId {
ID_file1ID = 3,//just to see a non zero number in the debugger, which I do
ID_file2ID,
//and so on
FileIdSize
};
global.h
#pragma once
#include "ids.h"
struct Instance
{
};
struct Factory
{
Instance getInstance(int FileID) { return Instance(); }
};
template <FileId ID>
struct Manager
{
static const FileId manager_id = ID;
static Factory& getTheFactory() { return m_factory; }
static Instance& getInstance()
{
static Instance theInstance = getTheFactory().getInstance(manager_id);
return theInstance;
}
private:
static Factory m_factory;
};
global.cpp
#include "global.h"
Factory Manager<FileId::ID_file1ID>::m_factory;
Factory Manager<FileId::ID_file2ID>::m_factory;
name1.cpp
#include "global.h"
void test1()
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
}
name2.cpp
#include "global.h"
void test2()
{
Instance& a = Manager<FileId::ID_file2ID>::getInstance();
}
test.cpp
#include <iostream>
#include "global.h"
using namespace std;
int main(int argc, char** argv)
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
Instance& b = Manager<FileId::ID_file2ID>::getInstance();
Instance& c = Manager<FileId::ID_file1ID>::getInstance();
Instance* aptr = &a;
Instance* bptr = &b;
Instance* cptr = &c;
printf("aptr==bptr -> %s\n", (aptr == bptr) ? "true" : "false"); //->false
printf("aptr==cptr -> %s\n", (aptr == cptr) ? "true" : "false"); //->true (both use the instance from ID_file1ID
printf("bptr==cptr -> %s\n", (bptr == cptr) ? "true" : "false"); //->false
}
回答2:
This is not an answer, but may prove useful in finding out what is wrong.
The following code is essentially the same as the original answer, but with all complexity stripped away at the expensive of needing boilerplate code in various places.
idmanager.h
struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc...
idmanager.cpp
Instance &getFile1Instance() { static Instance file1instance; return file1instance; } Instance &getFile2Instance() { static Instance file2instance; return file2instance; } // etc...
In each file, place at the start
#include "idmanager.h"
and you can get the static
Instance
of any file in the obvious way.This is as simple as it can possibly get, so copying it into your project simply can't cause a problem.
If the above example worked, then try making it slightly closer to the original answer: move the definitions of the
getFileXInstance
functions into the files themselves, and deleteidmanager.cpp
.idmanager.h
struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc...
file1.cpp
#include "idmanager.h" Instance &getFile1Instance() { static Instance file1instance; return file1instance; }
file2.cpp
// etc...
Clearly this just moves the code around between different .obj files, so should still work.
Now replace each
getFileXInstance
function with astruct
with a single static member function,getInstance
, as follows:idmanager.h
struct Instance {/*...*/}; struct Manager1 { static Instance &getInstance(); // defined in file1.cpp }; struct Manager2 { static Instance &getInstance(); // defined in file2.cpp }; // etc...
file1.cpp
#include "idmanager.h" Instance &Manager1::getInstance() { static Instance file1instance; return file1instance; }
file2.cpp
// etc...
The previous step allows us to reduce the amount of boilerplate code using templates:
idmanager.h
struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file };
file1.cpp
#include "idmanager.h" template <> Instance &Manager<1>::getInstance() { static Instance file1instance; return file1instance; }
This is where linker errors are most likely to start appearing again, if they do at all.
More repetition can also be removed by putting the common code in a shared header
globals.h
, and communicating the preprocessor constantFILEID
to it.idmanager.h
struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file };
file1.cpp
#include "idmanager.h" #define FILEID 1 #include "globals.h"
globals.h
template <> Instance &Manager<FILEID>::getInstance() { static Instance theInstance; return theInstance; }
This last example is now the same as the original answer, with a few differences (no factories, no enums, no getThisFileInstance()
) which are irrelevant to the linker errors. Therefore (assuming the first example worked) you can identify which change broke the program, and that should help to diagnose the real problem.
(note: although your error is exactly that which would appear if multiple files shared the same id, from the comments I assume this is not the case.)
来源:https://stackoverflow.com/questions/32329516/c03-linker-already-defined-symbol-doesnt-appear-on-intermediate-file