2014-03-27 134 views
3

我想初始化一个全球地图CPP文件将被忽略

std::map<long, std::string> Global_ID_Mapper; 

拥有一批像“初始化”类:

struct AGlobalMapperInitializer 
{ 
    AGlobalMapperInitializer() 
    { 
     Global_ID_Mapper.insert(std::make_pair(1, "Value1")); 
     Global_ID_Mapper.insert(std::make_pair(2, "Value2")); 
    } 
}; 

我想,以填补在应用程序启动时自动映射所以在我的一个cpp文件中,我只是定义了一个“init”类的全局变量。

// AGlobalMapperInitializer.cpp 

AGlobalMapperInitializer AGlobalMapperInitializer_Value; 

映射器填充是AGlobalMapperInitializer_Value创建的副作用。

问题是,如果cpp不包含除此全局变量之外的任何内容,则cpp明显被链接器忽略。当我将一些有用的其他代码放入cpp中(或者在一些非空cpp中定义全局初始化器时),调用构造函数并填充全局映射器。但是,如果cpp只包含没有在其他文件中引用的全局变量,则会编译cpp,obj文件包含该变量,但链接程序在链接期间未提及它,并且在exe中未被提及。

我该如何坚持将cpp链接到exe? 是否有一些编译指示或虚拟代码放入cpp中以使其不被忽略? 我使用Visual Studio 2012

+1

一个常见的选择是首次使用初始化全局/单例。 – MooseBoys

+0

您可能(Dll)导出整个类AGlobalMapperInitializer并使全局成为该类的静态成员,或者将标题中的全局声明为extern(Dll)在类外部导出。 (否则,全局是一个本地化的翻译单元,并进行了优化) –

+0

链接器会抛弃所有未引用的对象。除了声明不应该导出的导出外,您可以在任何函数中引用它。请注意编译器无法优化此引用,例如'main(){printf(“”,&AGlobalMapperInitializer_Value)); }' – harper

回答

1

感谢Angew和harper帮助找到解决方案。

有2个可能的解决方案:

  1. 便携式一个(但不是因为其性能好)。

    初始化器实例可以不在专用cpp文件中定义,但在h中使用单词static

    // AGlobalMapperInitializer.h 
    // It wasn't mentioned before that this h file is included in many cpp files 
    struct AGlobalMapperInitializer 
    { 
        AGlobalMapperInitializer() 
        { 
         if(!Global_ID_Mapper.insert(std::make_pair(1, "Value1")).second) 
          return; 
         Global_ID_Mapper.insert(std::make_pair(2, "Value2")); 
        } 
    }; 
    
    static AGlobalMapperInitializer AGlobalMapperInitializer_Value; 
    

    由于变量是static,所述AGlobalMapperInitializer_Value在所有的CPP文件,其中包括第h文件单独创建。这些变量中的每一个都试图为全局映射器添加值。当然,只有其中一个成功。通过检查第一个插入的结果部分解决了性能问题。如果第一个值已经插入 - 不需要尝试其他值。

    注意:假设所有这些实例将在同一线程内创建,否则全局映射器应实现插入调用的同步。

  2. 特定于编译器的解决方案(Visual Studio)。

    链接器可以通过添加#pragma comment (linker, "/include:<decorated name>")来强制保留课程。装饰名称可以是类构造函数或任何其他函数。问题在于指定装饰名称硬编码不好也不方便。装饰方法可以随编译器升级而改变。所以,这里可以使用__FUNCDNAME__

    struct AGlobalMapperInitializer 
    { 
        AGlobalMapperInitializer() 
        { 
         // Make sure the class will not be threw away by linker 
         #pragma comment (linker, "/include:"__FUNCDNAME__) 
    
         Global_ID_Mapper.insert(std::make_pair(1, "Value1")); 
         Global_ID_Mapper.insert(std::make_pair(2, "Value2")); 
        } 
    }; 
    

    幸运的是,#pragma可以在类的构造函数中指定。

2

C++不需要一个全局变量x的初始化过程的地方,如果没有来自同一个文件(实际上是翻译单元)作为x函数和变量是不断引用。

见C++ 11 [basic.start.init]§4

这是实现定义的非本地变量,静态存储持续时间的动态是否初始化的main的第一条语句之前完成。如果初始化被推迟到main的第一条语句之后的某个时间点,它应该在第一个odr-use(3.2)之前出现,这个函数或变量在同一个翻译单元中定义的变量被初始化。

如此给力的变量初始化,你必须把它送到他们的其他内容你实际使用的文件,或直接某处使用变量。

+0

很好,它完全解释了我的问题。 但是,你对这个问题有任何想法: '是否有一些编译指示或虚拟代码放入cpp以使其不被忽略?' – Kunis

+0

@ user940014'#编译指示是依赖于编译器的,因此请参阅编译器的文档。至于“虚拟代码”,我的答案列出了选项:使用变量,或将它们与你使用的某些东西(也包括“放入其他东西并使用它”)相关联。 – Angew

+0

@Andew我抓住了你的权利:我不能避免改变一些其他的CPP? 我的意思是,如果我甚至在第一个cpp中放置了一些东西(虚拟代码),我仍然需要在一些将使用这个虚拟代码的cpp中添加一些代码。 – Kunis

1

链接器不包含变量,并且它在未被引用时是初始值设定项。您可以将您的代码中创建这个变量的引用:

int main() 
{ 
    printf("", &AGlobalMapperInitializer_Value)); 
} 

如果你想避免的源代码这种污染你会与链接器的/INCLUDE参数相同的效果。当你尝试上面的黑客时,你必须添加你可以从.map文件中获取的装饰名称。

VS2010在项目属性中提供了Force Symbol Reference这个选项:Configuration Properies - > Linker - > Input。我希望VS2012也一样。

+0

谢谢。我错过了'/ INCLUDE'选项应该与装饰名称一起使用(尽管很明显应该是这种方式)。 所以现在我在我的h文件中添加了'#pragma comment(linker,“/ include:?AGlobalMapperInitializer_Value @@ 3VAGlobalMapperInitializer @@ A”)'并且得到了我想要的。然而,如果将'#pragma'添加到忽略的cpp中,则不起作用。 – Kunis

+0

但是用#pragma你会污染你的源代码。我会考虑将其隐藏在项目属性中。 ;-) – harper

+0

问题是,我有一些静态库,最近编译成几个exe文件。更改lib项目的'强制符号引用'不会有帮助。无论如何,链接器会抛出该符号,为了保持它,我必须更改最终链接项目的'强制符号引用'。但是我想在lib的级别上保留lib的所有细节。此外,我无法访问链接库的一些项目,我将改变。 我发现工作的唯一方法 - '#pragma'。而且我也可以避免使用'__FUNCDNAME__'指定装饰名称硬编码。 – Kunis