Один из способов устранения предупреждения C4251 при использовании stl-классов в dll-интерфейсе - программирование
Подтвердить что ты не робот

Один из способов устранения предупреждения 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. У меня есть вопрос, есть ли другие элегантные подходы к реализации, которые могут сделать то же самое. Спасибо!

4b9b3361

Ответ 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;
};

Ответ 4

Я не уверен, какую проблему вы хотите решить здесь. Существуют две разные проблемы: бинарная совместимость кросс-компилятора и устранение ошибок компоновщика "undefined". Предупреждение C4251, о котором вы указали, говорит о второй проблеме. Это действительно не проблема, особенно с классами SCL, такими как std::string и std::vector. Поскольку они реализованы в CRT, пока обе стороны вашего приложения используют один и тот же CRT, все будет "просто работать". Решение в этом случае состоит в том, чтобы просто отключить предупреждение.

Совместимость бинарных кросс-компиляторов OTOH, что и обсуждается в другом связанном с вами вопросе stackoverflow, представляет собой совершенно другой зверь. Для этого вам в основном нужно хранить все ваши общедоступные файлы заголовков без каких-либо упоминаний о любом классе SCL. Это означает, что вы либо должны все использовать PIMPL, либо использовать абстрактные классы везде (или их сочетание).