c++ cmake 动态库的 std::string 为空,求指点(maocs-12.6)

2023-07-17 15:52:45 +08:00
 wjx0912

代码和截图: https://github.com/wjx0912/cmake_macos_cpptest

https://github.com/wjx0912/cmake_macos_cpptest/raw/main/screenshot.png

万分感谢

1418 次点击
所在节点    C++
20 条回复
wjx0912
2023-07-17 16:17:24 +08:00
猜测的原因:__attribute__((constructor))时,c++ runtime 还未初始化,std::string 的一些操作可能不稳定。

测试:
```
std::string g_test1;
std::string g_test2;

__attribute__((constructor))
static void init() {
g_test1 = "hello test1";
printf("init: %s\n", g_test1.c_str());
}

void hello_func1(void) {
g_test2 = "hello test2";
printf("Hello World: %s, %s\n", g_test1.c_str(), g_test2.c_str());

return;
}

void hello_func2(void) {
printf("Hello World: %s, %s\n", g_test1.c_str(), g_test2.c_str());

return;
}
```

在 hello_func2 里面,g_test1 无法打印,g_test2 正常。

不知道这个思路是否正确,求大神指点
chingyat
2023-07-17 16:36:56 +08:00
应该是 init() 在 g_test 初始化之前就被调用了。
wjx0912
2023-07-17 16:37:17 +08:00
```
搞定了,把:
std::string g_test1;
std::string g_test2;
改成:
__attribute__((init_priority(101))) std::string g_test1;
__attribute__((init_priority(101))) std::string g_test2;

参考:
https://stackoverflow.com/questions/43941159/global-static-variables-initialization-issue-with-attribute-constructor-i
```
chingyat
2023-07-17 16:38:28 +08:00
```c++
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>

#include "hello.h"

std::string g_test = "initial value";

__attribute__((constructor))
static void init()
{
g_test = "hello test";
printf("init: %s\n", g_test.c_str());
}

void hello_func(void) {
printf("Hello World: %s\n", g_test.c_str());

return;
}
```

执行结果:

```
init: hello test
Hello World: initial value
```
wjx0912
2023-07-17 16:39:33 +08:00
这个不是编译器 bug ,是未指定行为
Shatyuka
2023-07-17 16:40:30 +08:00
`__attribute__((constructor))`在全局变量初始化之前执行。试试给全局变量添加`__attribute__((init_priority(101)))`属性。
wjx0912
2023-07-17 16:41:28 +08:00
@chingyat 嗯。这个是没问题的。但是 g_test 不是固定值(会从文本读取)。runtime 执行一堆 constructor 的顺序问题。
wjx0912
2023-07-17 16:42:39 +08:00
@Shatyuka 正解。谢谢
wjx0912
2023-07-17 16:44:12 +08:00
@Shatyuka windows 的 DllMain.DLL_PROCESS_ATTACH 执行的比较晚,所以不会有这个问题,对吧
yulon
2023-07-17 16:44:22 +08:00
这个问题和 DllMain 一样,在大部分实现上,C 的部分一般比 C++ 运行时要早或者说更底层运行,不要使用 C 方言,不要混用 C/C++ 关于生存周期的部分。
zpd2009
2023-07-17 16:47:18 +08:00
反汇编看了一下,在.init_array 段,执行完 init 以后,又执行了一个函数,把 g_test1 和 g_test2 又初始化为了空
yulon
2023-07-17 16:49:35 +08:00
@yulon MSVC 在 DllMain 里使用 C++ 标准库部分功能会炸或者死锁,Mingw-w64 GCC 相对安全一点,因为包了另一套东西,但是微软一直推荐不要在 DllMain 中执行太复杂的操作,DLL 的核心功能应该放在导出函数中。
zpd2009
2023-07-17 16:54:48 +08:00
如果 g_test1 指定了__attribute__((init_priority(101))),在.init_array 段执行的最后一个函数里,只初始化了 g_test2 ,没有重新初始化 g_test1
wjx0912
2023-07-17 16:58:45 +08:00
wjx0912
2023-07-17 17:00:15 +08:00
@zpd2009 谢谢。还是运行时没完全理解。但这种坑对新手实在是不友好
ysc3839
2023-07-17 17:13:30 +08:00
@wjx0912 #9 不是,DllMain 是最早执行的,但是 CRT 会在执行用户的 DllMain 之前先执行初始化代码。
yulon
2023-07-17 17:17:51 +08:00
@wjx0912 我说整个标准库,又不是单纯的 std::string ,还有语言层面的特性(线程安全保证之类的)有很多 MSVC 直接用标准库来实现,这种碰到了都会炸😅
wjx0912
2023-07-17 17:18:44 +08:00
@ysc3839 谢谢
yulon
2023-07-17 17:25:26 +08:00
@wjx0912 还有你这个测试,是在同一个编译单元内,一般来说同编译单元在编译时初始化顺序就决定了,这个很好优化,不同编译单元的初始化顺序是完全不可预料的,链接器可能会帮你排好序,但是遇到冲突是不可能完全按照你预料的顺序来初始化,只有 C++ 运行时完全初始化后,才能保证所有全局变量都初始化了。
lts9165
2023-07-17 17:32:22 +08:00
std::string& get_test1() {
static std::string test1;
return test1;
}

std::string& get_test2() {
static std::string test2;
return test2;
}

__attribute__((constructor))
static void init() {
get_test1() = "hello test1";
printf("init: %s\n", get_test1().c_str());
}

void hello_func1(void) {
get_test2() = "hello test2";
printf("Hello World: %s, %s\n", get_test1().c_str(), get_test2().c_str());

return;
}

void hello_func2(void) {
printf("Hello World: %s, %s\n", get_test1().c_str(), get_test2().c_str());

return;
}


这是 chatgpt 给出的方案

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/957438

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX