Один из способов устранения предупреждения C4251 при использовании stl-классов в dll-интерфейсе
Это не очень хорошая практика использования stl-классов в dll-интерфейсе как Общая практика в работе с предупреждением c4251: class... нуждается в dll-интерфейсе, Приведен пример:
#include <iostream>
#include <string>
#include <vector>
class __declspec(dllexport) HelloWorld
{
public:
HelloWorld()
{
abc.resize(5);
for(int i=0; i<5; i++)
abc[i] = i*10;
str="hello the world";
}
~HelloWorld()
{
}
std::vector<int> abc;
std::string str;
};
При компиляции этого файла можно наблюдать следующие предупреждения:
warning C4251: 'HelloWorld::str' : class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'HelloWorld'
warning C4251: 'HelloWorld::abc' : class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'HelloWorld'
Тогда возникает вопрос, как реализовать одну и ту же функциональность без использования STL-класса и строки. Одна из реализаций, о которых я подумал, выглядит следующим образом:
class __declspec(dllexport) HelloWorld2
{
public:
HelloWorld2()
{
abc_len = 5;
p_abc = new int [abc_len];
for(int i=0; i<abc_len; i++)
p_abc[i] = i*10;
std::string temp_str("hello_the_world");
str_len = temp_str.size();
p_str = new char[str_len+1];
strcpy(p_str,temp_str.c_str());
}
~HelloWorld2()
{
delete []p_abc;
delete []p_str;
}
int *p_abc;
int abc_len;
char *p_str;
int str_len;
};
Как вы можете видеть, в новой реализации мы используем int * p_abc для замены вектора abc и char * p_str для замены строки str. У меня есть вопрос, есть ли другие элегантные подходы к реализации, которые могут сделать то же самое. Спасибо!
Ответы
Ответ 1
Я думаю, у вас есть как минимум три возможных варианта решения этой проблемы:
-
Вы все равно можете хранить классы STL/шаблоны для своих полей, а также использовать их в качестве типов параметров, если вы сохраняете все поля частными (что в любом случае является хорошей практикой). Здесь обсуждается это. Чтобы удалить предупреждения, вы можете просто использовать команды #pragma
.
-
Вы можете создавать небольшие классы-оболочки с частными полями STL и публиковать поля типов типов-оболочек для публики, но это, скорее всего, приведет только к перемещению предупреждений в другие места.
-
Вы можете использовать PIMPL idiom, чтобы скрыть ваши STL-поля в классе частной реализации, что даже не будет экспортируется из вашей библиотеки и не виден за ее пределами.
-
Или вы можете фактически экспортировать все необходимые специализации классов шаблонов, как это предлагается в предупреждении C4251, таким образом, который описан здесь. Короче говоря, вам придется вставить следующие строки кода перед классом HelloWorld
:
...
#include <vector>
template class __declspec(dllexport) std::allocator<int>;
template class __declspec(dllexport) std::vector<int>;
template class __declspec(dllexport) std::string;
class __declspec(dllexport) HelloWorld
...
Кстати, порядок экспорта выглядит важен: шаблон векторного класса использует шаблон класса распределителя внутри, поэтому экземпляр-распределитель должен быть экспортирован до создания вектора.
-
Прямое использование intrinsics - это, вероятно, ваш худший вариант, но если вы инкапсулируете свои поля
int *p_abc;
int abc_len;
чем-то вроде class MyFancyDataArray
и поля
char *p_str;
int str_len;
в чем-то вроде class MyFancyString
, тогда это будет более достойное решение (подобное тому, которое описано во второй точке). Но, вероятно, это не самая лучшая привычка слишком часто изобретать велосипед.
Ответ 2
ИЛИ сделайте самую легкую вещь, переместите __declspec
в ТОЛЬКО членов, которые вы хотите экспортировать:
class HelloWorld
{
public:
__declspec(dllexport) HelloWorld()
{
abc.resize(5);
for(int i=0; i<5; i++)
abc[i] = i*10;
str="hello the world";
}
__declspec(dllexport) ~HelloWorld()
{
}
std::vector<int> abc;
std::string str;
};
Ответ 3
Просто используйте Указатель на реализацию (pImpl) idiom.
Ответ 4
Я не уверен, какую проблему вы хотите решить здесь. Существуют две разные проблемы: бинарная совместимость кросс-компилятора и устранение ошибок компоновщика "undefined". Предупреждение C4251, о котором вы указали, говорит о второй проблеме. Это действительно не проблема, особенно с классами SCL, такими как std::string
и std::vector
. Поскольку они реализованы в CRT, пока обе стороны вашего приложения используют один и тот же CRT, все будет "просто работать". Решение в этом случае состоит в том, чтобы просто отключить предупреждение.
Совместимость бинарных кросс-компиляторов OTOH, что и обсуждается в другом связанном с вами вопросе stackoverflow, представляет собой совершенно другой зверь. Для этого вам в основном нужно хранить все ваши общедоступные файлы заголовков без каких-либо упоминаний о любом классе SCL. Это означает, что вы либо должны все использовать PIMPL, либо использовать абстрактные классы везде (или их сочетание).