从DLL导出STL类 - 为什么没有从返回式警示?(Exporting STL class from

2019-08-04 01:52发布

My question is related to exporting a C++ class with STL inside. For example:

class __declspec(dllexport) Hello
{
    std::string   name;

public:
    std::string&  getName();
    void          setName(const std::string& name);
}

Various articles seems to indicate that this is very bad, which is quite understandable. Everything must be compiled with the same compiler settings and CRT version. Otherwise everything will crash and burn.

Question:

What I don't understand is why only data members seem to have an issue. With the below code, I get: "C4251: needs to have dll-interface to be used by clients of class"; which is apparently fixed by exporting the instantiated std::string:

struct __declspec(dllexport) SomeClass
{
    // Removes the warning?
    // http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html
    //   template class __declspec(dllexport) std::string;

    std::string name;  // Compiler balks at this
}

And the fixed version is:

// Export the instantiations of allocator and basic_string
template class __declspec(dllexport) std::allocator<char>;
template class __declspec(dllexport) std::basic_string<char, std::char_traits<char>, std::allocator<char> >;

struct __declspec(dllexport) SomeClass
{
    std::string name;  // No more balking!
}

(This will give LNK2005 "basic_string already defined" when you try to use the DLL, meaning you have to not link in the CRT on the client - so it ends up using the instantiation in the DLL).

Return types and arguments seem to have no problem with the STL, and do not receive the same treatment data members get from the compiler.

// No exporting required?
struct __declspec(dllexport) SomeOtherClass
{
    std::string  doSomething1();                       // No problemo
    void         doSomething2(const std::string& s);   // No problemo
}

Additional Info (Question is above)

In both:

class A {
    std::string foo() { return std::string(); }
    // std::string& foo(); gives the same result!
    // std::string* foo(); also gives the same result!
}

class B {
    std::string a;
}

Neither seem to export std::basic_string or std::allocator. Rather, they only export the members/functions of the class.

However the fixed version mentioned in the question exports both basic_string and allocator.

Answer 1:

各种文章似乎表明,这是非常糟糕

是的,它可以。 和你的项目设置将让你进入一种他们警告烦恼。 按值将C ++对象,需要您的DLL的客户端使用相同的CRT,使得都在DLL中创建的对象可以通过客户端应用程序被安全销毁。 和周围的其他方法。 这就要求这些模块使用同一个堆。

和你的项目设置, 防止被可能的话,编译器警告的要点。 使所有模块加载一个和唯一实现了CRT的您必须指定CRT的共享版本。

修复了与项目+属性,C / C ++,代码生成,运行库设置。 你现在拥有它/ MT,它必须是/ MD。 改变这所有模块和所有配置。



Answer 2:

这可以归结为如何某些事情被建造。

当编译器看到

__declspec(dllimport)    std::string f();
// ...

{
  std::string tmp = f();
}

它必须弄清楚叫什么,从哪里得到它。 因此,在这种情况下:

std::string tmp; => sizeof( std::string ), new (__stack_addr) std::string;
tmp = f();       => call f(), operator=( std::string )

但是因为它看到的std :: string的完整实现它可以只使用该模板根据一个新的实例。 因此,它可以只是实例化的std :: string的模板函数和收工,并保留功能合并到链接器阶段,其中连接试图找出哪些功能可以折叠成只有一个。 唯一的未知函数为f(),编译器必须从DLL本身导入。 (它标志着他外)。

成员是编译器的一个更大的问题。 它知道按照功能导出(构造函数,拷贝构造函数,赋值运算符,析构函数调用),当你标记一个类为“DLLEXPORT”,它必须导出/导入他们中的每一个。 您可以导出类的明确只有某些部分,宣称只有必要的功能为DLLEXPORT(构造函数/析构函数),并禁止复制如。 这样,您就不必出口的一切。

有关的std :: string一个需要注意的是,它的大小/内容的编译器版本之间的变化,让你永远可以安全地复制编译器版本之间的std :: string。 (例如,在VC6的字符串是3个指针较大,目前这16个字节+规模+的sizeof分配,我认为在VS2012得到优化掉)。 你永远不应该在你的界面使用std :: string对象。 您可以创建在调用者的网站转换成用非出口内联函数为std :: string一个DLL导出的字符串实现。



文章来源: Exporting STL class from DLL - why is there no warning from the return type?