免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4908 | 回复: 9
打印 上一主题 下一主题

[C++] C++ Static Lib 链接问题 [复制链接]

论坛徽章:
1
双子座
日期:2014-08-29 17:15:03
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-07-22 10:45 |只看该作者 |倒序浏览
Static Lib 里面的代码:

MsgDefs.h:
  1. #pragma once

  2. #include <map>
  3. #include <string>

  4. typedef std::pair<std::string, int> MsgDefKey;
  5. typedef std::map<MsgDefKey, std::string> MsgDefs;
  6. extern MsgDefs gHSVFMsgDefs;
  7. extern int fake_link_var;
  8. int fake_link_func();

  9. #define MSG_DEF(version, msg_id, msg) \
  10.         namespace version##_##msg_id##_def \
  11.         { \
  12.                 MsgDefs::key_type key(#version, msg_id); \
  13.                 struct DefsAppender \
  14.                 { \
  15.                         DefsAppender() \
  16.                         { \
  17.                         gHSVFMsgDefs.insert(MsgDefs::value_type(key, msg)); \
  18.                         } \
  19.                 } defs_appender; \
  20.         }
复制代码
Version1.cpp:
  1. #include "stdafx.h"
  2. #include "MsgDefs.h"
  3. #include <string>

  4. using namespace std;

  5. MsgDefs gHSVFMsgDefs;
  6. const std::string V1_1 = "A1";
  7. MSG_DEF(V1, 1, V1_1)
复制代码
Version2.cpp:
  1. #include "stdafx.h"
  2. #include "MsgDefs.h"
  3. #include <string>

  4. using namespace std;
  5. int fake_link_var = 0;
  6. int fake_link_func()
  7. {
  8.         return 0;
  9. }

  10. const std::string V2_1 = "B1";
  11. MSG_DEF(V2, 1, V2_1)
复制代码
主程序代码:
  1. // LinkTest.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include "MsgDefs.h"
  5. #include <iostream>

  6. int _tmain(int argc, _TCHAR* argv[])
  7. {
  8.         //fake_link_var();
  9.         //fake_link_var++;
  10.         std::cout << gHSVFMsgDefs.size() << std::endl;
  11.         return 0;
  12. }

复制代码
问题: 注释掉fake_link_var那两行,输出结果为1.  启用任意一行代码后,结果为2.  求达人赐教更好的解决方案.

论坛徽章:
1
双子座
日期:2014-08-29 17:15:03
2 [报告]
发表于 2010-07-22 10:46 |只看该作者
如果代码都是放在主程序中的话就没有任何问题(输出结果为2)

论坛徽章:
1
双子座
日期:2014-08-29 17:15:03
3 [报告]
发表于 2010-07-22 11:31 |只看该作者
Up期待滑铁卢进来看看

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
4 [报告]
发表于 2010-07-22 15:14 |只看该作者
翻译单元version1:

  1. MsgDefs gHSVFMsgDefs;
  2. const std::string V1_1 = "A1";
  3. MSG_DEF(V1, 1, V1_1); // 这里没问题
复制代码
version2:

  1. const std::string V2_1 = "B1";
  2. MSG_DEF(V2, 1, V2_1); // 这里需要gHSVFMsgDefs已经初始化
复制代码
但多个翻译单元间的全局变量初始化顺序是没有保证的。



如果MSG_DEF(V2, 1, V2_1)展开定义的那个全局变量先于gHSVFMsgDefs初始化,
那就是向一个未初始化的map插入数据 —— 很危险。
然后gHSVFMsgDefs初始化, 然后MSG_DEF(V1, 1, V1_1)插入一项。
最终就只有1项。

论坛徽章:
1
双子座
日期:2014-08-29 17:15:03
5 [报告]
发表于 2010-07-22 17:02 |只看该作者
回复 4# OwnWaterloo


    首先可以肯定的是代码确实很有问题,如果初始化顺序出错的话。 可能改成Singleton会安全些。  但是改成单件后,却发现结果为0.  调试出来的结果是所有静态对象都没有初始化。
  1. #pragma once

  2. #include <map>
  3. #include <string>

  4. typedef std::pair<std::string, int> MsgDefKey;
  5. typedef std::map<MsgDefKey, std::string> MsgDefs;
  6. //extern MsgDefs gHSVFMsgDefs;

  7. MsgDefs& getInstance()
  8. {
  9.         static MsgDefs obj;
  10.         return obj;
  11. }

  12. extern int fake_link_var;
  13. int fake_link_func();

  14. #define MSG_DEF(version, msg_id, msg) \
  15.         namespace version##_##msg_id##_def \
  16.         { \
  17.                 MsgDefs::key_type key(#version, msg_id); \
  18.                 struct DefsAppender \
  19.                 { \
  20.                         DefsAppender() \
  21.                         { \
  22.                         getInstance().insert(MsgDefs::value_type(key, msg)); \
  23.                         } \
  24.                 } defs_appender; \
  25.         }
复制代码
  1. // LinkTest.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include "MsgDefs.h"
  5. #include <iostream>

  6. int _tmain(int argc, _TCHAR* argv[])
  7. {
  8.         //fake_link_var();
  9.         //fake_link_var++;
  10.         //std::cout << gHSVFMsgDefs.size() << std::endl;
  11.         std::cout << getInstance().size() << std::endl;
  12.         return 0;
  13. }


复制代码

论坛徽章:
0
6 [报告]
发表于 2010-07-22 18:36 |只看该作者
有时看到c++代码会觉得很难看!

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
7 [报告]
发表于 2010-07-22 21:44 |只看该作者
回复 5# srdgame

你先这样试试:

1. 初始化

map的初始化和4楼一样。

作为一个函数中的static 对象, 初始化时机是控制线第1次到达定义处。
同时不要在main之前多线程访问这个函数。

这样就保证初始化顺序是没问题的。

2. 优化

你用的应该是msvc吧?
试试取消优化后会不会得到正确结果?
再试试访问一次那个用于插入项的全局对象。

有可能是因为插入项的全局对象没有被使用, 而被链接器优化了。

  1. ------ def.h ------
  2. #include <map>

  3. extern
  4. std::map
  5. <std::pair<std::string,int>
  6. ,std::string
  7. > msg;

  8. msg& instance() { static msg m; return m; }

  9. struct msg_init
  10. {
  11.   msg_init(char const* v, int id, char const* m)
  12.   {
  13.     instance().insert
  14.     (std::make_pair(std::make_pair(v,id), m);
  15.   }
  16. };


  17. ------ version1.cpp ------
  18. #include "def.h"
  19. msg_init v1("V1", 1, "A1");


  20. ------ version2.cpp
  21. #include "def.h"
  22. msg_init v2("V2", 2, "B1");

  23. ------ linktest.cpp ------
  24. #include <iostream>
  25. #include "def.h"

  26. extern msg_init v1, v2;

  27. int main()
  28. {
  29.       using namespace std;
  30.       // 这里, 试试取消/添加下面2行
  31.       // 会不会产生不同的输出
  32.       cout<<&v1<<endl;
  33.       cout<<&v2<<endl;
  34.       cout<<instance().size()<<endl;
  35.       return 0;
  36. }

复制代码
如果会输出不同结果, 那就肯定不是因为初始化顺序, 而是v1, v2被链接器排除了。
以前在cppblog看到过这样的情况: http://www.cppblog.com/kevinlynx/archive/2010/02/10/105885.html

那个文章里使用的是一个const, 默认是static链接, 所以我认为可能会被优化。
而外部链接, 应该不会被优化才对, 否则造成程序结果有可见差异。
后来也没去验证。

从你的情况来看, 可能真的会被优化。

论坛徽章:
1
双子座
日期:2014-08-29 17:15:03
8 [报告]
发表于 2010-07-23 11:29 |只看该作者
是的,被优化(或者干脆说这就是链接器的工作原则)了。   跟cppblog上描述的问题应该是一样的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
9 [报告]
发表于 2010-07-23 16:39 |只看该作者
回复 8# srdgame

然后?  如何解决?
真的写个函数去引用一下?  太缺乏美感了……

如果不是做成静态库, 会有这个问题么?

论坛徽章:
1
双子座
日期:2014-08-29 17:15:03
10 [报告]
发表于 2010-07-26 14:45 |只看该作者
回复  srdgame

然后?  如何解决?
真的写个函数去引用一下?  太缺乏美感了……

如果不是做成静态 ...
OwnWaterloo 发表于 2010-07-23 16:39



暂时没有思路,目前只能通过写个函数/变量去引用一下。  没去试动态链接。 在可执行程序工程里面是没有问题的(有问题的话CPPUnit早死了)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP