我在Visual Studio 2005上的一个大型项目中遇到了问题,我已经没有想法了。
我什至无法放置工作代码片段,因为我不知道相关内容,但我会尝试:
我需要使项目中的每个.cpp文件都有自己的 ID 号,并创建一个知道该 ID 的对象(全局可访问(的实例。我遵循了此线程上接受的答案的帮助 如何在 c++ 中管理文件唯一 ID并使其在沙盒环境中工作。
添加文件,为它们提供独特的#define FILEID (FileId::ID_FileName)
然后访问他们的实例在沙盒上工作正常。
现在麻烦来了——我将使文件知道其 IDS 的代码粘贴到主项目中并进行编译。
目前为止,一切都好。
现在,我添加到项目中现有的.cpp文件之一:
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
还是编译,链接,都很好。
将这些行添加到项目中的(任何(第二个.cpp文件中现在给出链接错误:
其中:
- name1:我添加行的第一个文件(按字母顺序(
- name2:其他不相关的文件名(也可以是我添加行的第二个文件,但也可能是其他一些文件(
错误 in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance@$Manager@$02@@SAAAVInstance@@XZ) already defined in name1.obj
有时错误仅在第二个文件中,有时(在没有更改的连续生成之间(错误出现在文件夹中的每个.cpp文件上。
在我添加行的文件上的中间文件(预处理器输出(中查找,恰好显示
template <>
Instance &Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
使用正确的 FileId::ID_FileName,该名称与其他文件的名称不同。尽管如此,链接器认为在多个文件中使用相同的 FileId。
在不相关的文件(也给出完全相同的错误(上,根本没有getInstance()
的外观。显然,链接器不应该有理由在那里大喊大叫。
我检查了一下,没有.cpp文件在项目中的某个地方相互包含。
我完全不知道是什么原因会导致这种情况并将不胜感激任何帮助。
编辑 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
}
编辑 2
当这些错误开始时,编译器的行为开始非常意外。
将行sdfsdfgasaedfahjk
添加到任何文件仍然编译并通过。
它清楚地说明了该行已添加到编译的文件名。它清楚地表明它链接到它。它过去了。
我现在不能信任编译器。
不知道发生了什么。
您有 2 个 cpp 文件将FILEID
定义为相同的值3
。
至于MCVE:
IDS.h:
#pragma once
#define File1 3
#define File2 3 //<--same value on purpose
全球.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;
};
名称1.cpp
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
名称2.cpp
#include "ids.h"
#define FILEID File2 // The FileId corresponding to this file
#include "global.h"
在编译时,有一个特殊的实现,用于为 name1.cpp
和 name2.cpp
创建的Manager<3>::getInstance(void)
。
不能对 2 个不同编译单元中的FILEID
使用相同的值。
编辑:编译时检查值
需要预处理器定义__BASE_FILE__="%(Filename)%(Extension)"
(配置属性 -> C/C++ -> 预处理器 -> 预处理器定义(
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;
};
示例输出:
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>DebugTest_Call.exe
== == == == == Build: 1 succeeded, 0 failed, 0 up - to - date, 0 skipped == == == == ==
编辑:使用文件ID值作为模板参数(MSVE(
同上
#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
};
全球.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;
};
全球.cpp
#include "global.h"
Factory Manager<FileId::ID_file1ID>::m_factory;
Factory Manager<FileId::ID_file2ID>::m_factory;
名称1.cpp
#include "global.h"
void test1()
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
}
名称2.cpp
#include "global.h"
void test2()
{
Instance& a = Manager<FileId::ID_file2ID>::getInstance();
}
测试.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 -> %sn", (aptr == bptr) ? "true" : "false"); //->false
printf("aptr==cptr -> %sn", (aptr == cptr) ? "true" : "false"); //->true (both use the instance from ID_file1ID
printf("bptr==cptr -> %sn", (bptr == cptr) ? "true" : "false"); //->false
}
这不是一个答案,但可能有助于找出问题所在。
-
以下代码与原始答案基本相同,但剥离了所有复杂性,因为在各个地方需要样板代码的昂贵。
身份经理.h
struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc...
身份经理.cpp
Instance &getFile1Instance() { static Instance file1instance; return file1instance; } Instance &getFile2Instance() { static Instance file2instance; return file2instance; } // etc...
在每个文件中,放在开头
#include "idmanager.h"
您可以以明显的方式获取任何文件的静态
Instance
。这尽可能简单,因此将其复制到您的项目中根本不会引起问题。
-
如果上面的例子有效,那么试着让它稍微接近原始答案:将
getFileXInstance
函数的定义移动到文件本身中,然后删除idmanager.cpp
。身份经理.h
struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc...
文件1.cpp
#include "idmanager.h" Instance &getFile1Instance() { static Instance file1instance; return file1instance; }
文件2.cpp
// etc...
显然,这只是在不同的.obj文件之间移动代码,因此应该仍然有效。
-
现在,将每个
getFileXInstance
函数替换为具有单个静态成员函数的struct
,getInstance
,如下所示:身份经理.h
struct Instance {/*...*/}; struct Manager1 { static Instance &getInstance(); // defined in file1.cpp }; struct Manager2 { static Instance &getInstance(); // defined in file2.cpp }; // etc...
文件1.cpp
#include "idmanager.h" Instance &Manager1::getInstance() { static Instance file1instance; return file1instance; }
文件2.cpp
// etc...
-
上一步允许我们使用模板减少样板代码的数量:
身份经理.h
struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file };
文件1.cpp
#include "idmanager.h" template <> Instance &Manager<1>::getInstance() { static Instance file1instance; return file1instance; }
这是链接器错误最有可能再次开始出现的地方,如果有的话。
-
更多的重复也可以通过将公共代码放在一个共享的标头
globals.h
中,并将预处理器常量FILEID
传达给它。身份经理.h
struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file };
文件1.cpp
#include "idmanager.h" #define FILEID 1 #include "globals.h"
全局.h
template <> Instance &Manager<FILEID>::getInstance() { static Instance theInstance; return theInstance; }
最后一个示例现在与原始答案相同,但有一些与链接器错误无关的差异(没有工厂,没有枚举,没有getThisFileInstance()
(。 因此(假设第一个示例有效(,您可以确定哪个更改破坏了程序,这应该有助于诊断真正的问题。
(注意:虽然您的错误正是多个文件共享同一ID时会出现的错误,但从评论中,我认为情况并非如此。