c++ - Exportación de clase STL desde DLL: ¿por qué no hay ninguna advertencia del tipo de retorno?
visual-studio-2010 instantiation (2)
Mi pregunta está relacionada con la exportación de una clase de C ++ con STL dentro. Por ejemplo:
class __declspec(dllexport) Hello
{
std::string name;
public:
std::string& getName();
void setName(const std::string& name);
}
Varios artículos parecen indicar que esto es muy malo , lo cual es bastante comprensible. Todo debe ser compilado con la misma configuración del compilador y la versión CRT. De lo contrario todo se estrellará y arderá.
Pregunta:
Lo que no entiendo es por qué solo los miembros de datos parecen tener un problema. Con el siguiente código, obtengo: " C4251: necesita tener una interfaz dll para ser utilizada por los clientes de clase "; que aparentemente se arregla al exportar la instanciada 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
}
Y la versión fija es:
// 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!
}
(Esto le dará a LNK2005 "basic_string ya definido" cuando intente usar la DLL, lo que significa que no debe vincular el CRT en el cliente, por lo que termina usando la creación de instancias en la DLL).
Los tipos y argumentos de devolución parecen no tener ningún problema con la STL y no reciben los mismos datos de tratamiento que los miembros obtienen del compilador.
// No exporting required?
struct __declspec(dllexport) SomeOtherClass
{
std::string doSomething1(); // No problemo
void doSomething2(const std::string& s); // No problemo
}
Información adicional (la pregunta está arriba)
En ambos:
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;
}
Tampoco parecen exportar std :: basic_string o std :: allocator. Más bien, solo exportan los miembros / funciones de la clase.
Sin embargo, la versión fija mencionada en la pregunta exporta tanto basic_string como el asignador.
Varios artículos parecen indicar que esto es muy malo.
Si puede ser. Y la configuración de tu proyecto te llevará al tipo de problema sobre el que están advirtiendo. Exponer los objetos de C ++ por valor requiere que el cliente de su DLL use el mismo CRT para que la aplicación del cliente pueda destruir de forma segura los objetos que se crean en la DLL. Y a la inversa. Lo que requiere que estos módulos usen el mismo montón.
Y la configuración de su proyecto evita que eso sea posible, lo esencial de la advertencia del compilador. Debe especificar la versión compartida del CRT para que todos los módulos carguen la implementación única del CRT.
Solucione eso con las propiedades de Project +, C / C ++, Generación de código, Configuración de la biblioteca en tiempo de ejecución. Lo tienes ahora en / MT, debe ser / MD. Cambia esto para todos los módulos y todas las configuraciones.
Esto se reduce a cómo se construyen ciertas cosas.
Cuando el compilador ve
__declspec(dllimport) std::string f();
// ...
{
std::string tmp = f();
}
Debe averiguar a qué llamar y de dónde obtenerlo. Así que en este caso:
std::string tmp; => sizeof( std::string ), new (__stack_addr) std::string;
tmp = f(); => call f(), operator=( std::string )
Pero debido a que ve la implementación completa de std :: string, solo puede usar una nueva instancia de la plantilla correspondiente. Así que solo puede crear una instancia de las funciones de plantilla de std :: string y llamarlo un día, y dejar la función uniéndose a la etapa del enlazador, donde el enlazador intenta averiguar qué funciones puede plegar en una sola. La única función desconocida es f () que el compilador debe importar desde la propia dll. (Está marcado externo para él).
Los miembros son un problema más grande para el compilador. Debe conocer las funciones correspondientes para exportar (constructor, copia-constructor, asignación-operador, destructor de llamada) y cuando marca una clase como ''dllexport'', debe exportar / importar cada una de ellas. Puede exportar explícitamente solo ciertas partes de su clase, declarando solo las funciones necesarias como dllexport (ctor / dtor) y no permitir, por ejemplo, copiar. De esta manera, no tienes que exportar todo.
Una nota sobre std :: string es que su tamaño / contenido cambió entre las versiones del compilador, por lo que nunca puede copiar de forma segura una cadena std :: entre versiones del compilador. (Por ejemplo, en VC6, una cadena tenía 3 punteros de tamaño, actualmente tiene 16 bytes + tamaño + tamaño de asignador, que creo que se optimizó en VS2012). Nunca debes usar objetos std :: string en tu interfaz. Puede crear una implementación de cadena exportada por dll que se convierta en el sitio del llamante en una cadena std :: mediante el uso de funciones en línea no exportadas.