【问题标题】:Exception deallocating memory for std::string (trying to use YOLO/Darknet inside UE4)为 std::string 释放内存的异常(尝试在 UE4 中使用 YOLO/Darknet)
【发布时间】:2020-03-10 17:36:54
【问题描述】:

我正在尝试让 YOLO/Darknet 在 UE4 项目中工作。我已将 YOLO 构建为 C++ DLL,并将所有依赖项添加到项目中。它构建得很好,并且代码的某些部分正在运行,但是我在某个时候遇到了异常,经过几天的努力试图弄清楚,我现在不知所措,需要一些帮助。

这是我调用的代码,用于从 UE4 类中创建 YOLO 检测器类:

YOLO_DataPath = FPaths::ProjectDir() + "Plugins/Stereolabs/Source/YOLO/Data/";
std::string YOLO_DataPathC(TCHAR_TO_UTF8(*YOLO_DataPath));

std::string  NamesFile = YOLO_DataPathC + "obj.names";
std::string  CFGFile = YOLO_DataPathC + "yolo-obj.cfg";
std::string  WeightsFile = YOLO_DataPathC + "yolo-obj.weights";

Detector YOLODetector(CFGFile, WeightsFile);

这里是被调用的构造函数(来自 'yolo_v2_class.cpp',第 130 行):

LIB_API Detector::Detector(std::string cfg_filename, std::string weight_filename, int gpu_id) : cur_gpu_id(gpu_id)
{
    wait_stream = 0;
#ifdef GPU
    int old_gpu_index;
    check_cuda( cudaGetDevice(&old_gpu_index) );
#endif

    detector_gpu_ptr = std::make_shared<detector_gpu_t>();
    detector_gpu_t &detector_gpu = *static_cast<detector_gpu_t *>(detector_gpu_ptr.get());

#ifdef GPU
    //check_cuda( cudaSetDevice(cur_gpu_id) );
    cuda_set_device(cur_gpu_id);
    printf(" Used GPU %d \n", cur_gpu_id);
#endif
    network &net = detector_gpu.net;
    net.gpu_index = cur_gpu_id;
    //gpu_index = i;

    _cfg_filename = cfg_filename;
    _weight_filename = weight_filename;

    char *cfgfile = const_cast<char *>(_cfg_filename.c_str());
    char *weightfile = const_cast<char *>(_weight_filename.c_str());

    net = parse_network_cfg_custom(cfgfile, 1, 1);
    if (weightfile) {
        load_weights(&net, weightfile);
    }
    set_batch_network(&net, 1);
    net.gpu_index = cur_gpu_id;
    fuse_conv_batchnorm(net);

    layer l = net.layers[net.n - 1];
    int j;

    detector_gpu.avg = (float *)calloc(l.outputs, sizeof(float));
    for (j = 0; j < NFRAMES; ++j) detector_gpu.predictions[j] = (float*)calloc(l.outputs, sizeof(float));
    for (j = 0; j < NFRAMES; ++j) detector_gpu.images[j] = make_image(1, 1, 3);

    detector_gpu.track_id = (unsigned int *)calloc(l.classes, sizeof(unsigned int));
    for (j = 0; j < l.classes; ++j) detector_gpu.track_id[j] = 1;

#ifdef GPU
    check_cuda( cudaSetDevice(old_gpu_index) );
#endif
}

所有这些代码似乎运行良好,但是当它到达构造函数的末尾时,它遇到了一个异常,它似乎试图删除一个字符串,在这一行(xmemory - c:\Program Files 的第 132 行(x86)\Microsoft Visual Studio 14.0\VC\include\xmemory0):

::operator delete(_Ptr);

完整的调用栈是这样的:

[Inline Frame] yolo_cpp_dll.dll!std::_Deallocate(void * _Ptr, unsigned __int64) Line 132
[Inline Frame] yolo_cpp_dll.dll!std::allocator<char>::deallocate(char *) Line 720
[Inline Frame] yolo_cpp_dll.dll!std::_Wrap_alloc<std::allocator<char> >::deallocate(char * _Count, unsigned __int64) Line 987
[Inline Frame] yolo_cpp_dll.dll!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Tidy(bool) Line 2258
[Inline Frame] yolo_cpp_dll.dll!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::{dtor}() Line 1017
yolo_cpp_dll.dll!Detector::Detector(std::basic_string<char,std::char_traits<char>,std::allocator<char> > cfg_filename, std::basic_string<char,std::char_traits<char>,std::allocator<char> > weight_filename, int gpu_id) Line 177

从我能找到的关于此错误的有限信息来看,这似乎是使用不同编译器编译 DLL 的问题。我已经花了几天时间尝试从头开始编译所有内容的不同版本 - UE4 from source、UE4 项目、YOLO cpp DLL。我已经尝试过完全干净地安装 Visual Studio 2015 和 2017,但每次都遇到同样的问题。

有人知道这里到底发生了什么吗?以及我该如何解决或解决它?

【问题讨论】:

  • 看起来这可能是使用不同编译器编译 DLL 的问题。 -- 好吧,如果你要传递并返回 std::string 有由不同的编译器创建,那么您将遇到内存问题。 See this
  • 这就是我的想法,但这就是为什么我一直在使用相同的编译器从源代码构建所有内容。而且我没有通过引用传递字符串。
  • 另外,函数没有返回任何字符串。
  • 您必须使用相同的编译器、相同的编译器设置、相同的运行时库(必须是运行时的 DLL 版本),并且您如何传递和返回 std::string 并不重要.当您拥有所有这些限制时,您的误差范围非常小。这就是为什么不建议传递和/或返回管理跨模块边界的动态内存的对象。
  • 我明白了,感谢您向我解释...我将不得不再次尝试以相同的设置获取所有内容。但是有没有替代的方法来做到这一点?好像不应该是这么复杂的事情……

标签: c++ memory dll compiler-errors unreal-engine4


【解决方案1】:

简单的方法:永远不要在不同模块之间传递 std::xxx 对象。使用原始 C 类型。内存应该在分配它的模块中释放。

检测器(const char* cfg_filename, const char* weight_filename, int gpu_id)

困难方式:使用相同的编译器/选项编译所有模块(在 UE4 的情况下特别困难)。

【讨论】:

    【解决方案2】:

    正如其他人指出的那样,无故障的方法是将代码隐藏在 C 接口后面,但是如果您使用 C++,这会极大地限制您的接口。因此,如果您想走上艰难的道路,我知道至少有两个项目成功地做到了,您可以查看他们的代码以获取完整的详细信息

    正如这里的大多数 cmets 所指出的,使用相同的编译器和配置,如果您在 Visual Studio 中打开 UE4 项目,您应该能够检查配置。

    那么诀窍就是静态链接 MSVCRT.lib。除非您自己编译 UE4,否则它使用发布模式,因此我们使用 /MD 编译器标志(请参阅 msvc reference)。下面是 CARLA 的构建脚本中的示例,BuildLibCarla.bat#L103-L111,使用 CMake 创建一个静态库,然后这个库被链接到一个 UE4 插件(dll)中。

    Linux

    为了完成,我还将添加 Linux 的详细信息,因为我希望我必须在网上找到这个!

    如果您在 Linux 上构建,事情会变得有点复杂,UE4 链接到 LLVM 的 libc++ 运行时而不是默认的 GNU 的 libstdc++。根据 UE4 版本,他们使用不同版本的 LLVM 工具链(他们倾向于相对频繁地更新它)。它们与 UE4 捆绑在一起,您可以在

    找到它们
    • 头文件和库:Engine/Source/ThirdParty/Linux/LibCxx
    • SDK,二进制文件:Engine/Extras/ThirdPartyNotUE/SDKs/HostLinux/Linux_x64/&lt;clang version&gt;/x86_64-unknown-linux-gnu

    然后编译并链接libc++.alibc++abi.a 的捆绑版本,或者编译你自己的(这就是CARLA 所做的Setup.sh#L37-L58)。请注意,使用从 apt install 获得的 libc++ 和 libc++abi 将不起作用,因为它们没有使用与位置无关的代码 (-fPIC) 进行编译,您需要在 UE4 中链接到 .so。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-26
      • 1970-01-01
      • 2013-03-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2015-07-13
      相关资源
      最近更新 更多