C ++ pimpl идиома и экспорт структуры данных - PullRequest
0 голосов
/ 28 января 2020

У меня есть вопрос о шаблоне прыщей, и, возможно, один из вас, ребята, может предложить решение, которое я мог бы пропустить. Я реализовал шаблон pimpl, чтобы скрыть реализацию в моей dll от остального мира, но я застрял на том, как скрыть структуру, которая должна быть экспортирована из dll. Ниже приведено простое представление моей проблемы (пропущено несколько частей)

idcom.h

class IDcom_impl;

class IDcom
{
public:
    IDcom_API IDcom();
    void IDcom_API createList();

private:
    std::unique_ptr< IDcom_impl > m_impl;
}

idcom. cpp

IDcom::IDcom() :
    m_impl( new IDcom_impl())
{}

void IDcom::createList()
{
   m_impl->createList();
}

idcom_impl.h

class IDcom_impl
{
public:
    struct IDcom_API ListItem
    {
       std::string var1;
       std::string var2;
    }

    IDcom_impl(){}

    void createList();

    std::vector< ListItem > m_list;
}

Если я хочу расширить ListItem с помощью другой переменной std::string var3, не нарушать ABI и получить список «listitem» из dll, каковы мои варианты? Я знаю, что для того, чтобы приложение узнало что-то о «ListItem», эту структуру необходимо экспортировать, но этого достаточно, и это хороший дизайн

Ответы [ 2 ]

0 голосов
/ 29 января 2020

Я думаю, что у меня есть, но мне нужно, чтобы вы, ребята, проверили это, чтобы убедиться, что у меня хороший дизайн. Таким образом, в основном pimpl реализован правильно, но проблема заключается в расширении структуры данных (скажем, добавление новой переменной-члена, скажем, var3 в оригинальном сообщении), которое у меня есть в dll и должно быть доступно приложению И новой версии dll (скажем, v2) должен работать с текущей версией приложения (скажем, v2) и предыдущей (скажем, v1), поэтому без перекомпиляции приложения (следовательно, совместимость с v1). Я разместил только необходимое (облегчает чтение :-)), код компилируется, запускается и работает, но это хороший дизайн для требований, изложенных выше и в моем оригинальном посте.

idcom_global.h:

#if defined( IDCOM_LIBRARY )
 #define IDCOMDLL_API  __declspec( dllexport )
#else
 #define IDCOMDLL_API  __declspec( dllimport )
#endif

idcom.h:

#include "idcom_listitem.h"

class idcomImpl;

class  idcom
{
public:
    IDCOMDLL_API idcom();
    IDCOMDLL_API ~idcom();
    bool IDCOMDLL_API retrieveList( std::vector< ListItem >& list );

private:
    std::unique_ptr< idcomImpl > m_impl;
};

idcom. cpp:

idcom::idcom() :
    m_impl( new idcomImpl())
{}

idcom::~idcom() = default;

bool idcom::retrieveList( std::vector< ListItem >& list )
{
    return m_impl->retrieveList( list );
}

idcomimpl.h :

class idcomImpl
{
public:
    idcomImpl(){};
    ~idcomImpl(){};
    bool retrieveList( std::vector< ListItem >& worklist );
};

idcomimpl. cpp:

bool idcomImpl::retrieveList( std::vector< ListItem >& worklist )
{
....//implementation here
}

idcom_listitem.h:

struct IDCOMDLL_API ListItem
{
    std::string id;
    std::string lastname;
    std::string firstname;
    std::string prefix;

    //expansion of members here in the future e.g. std::string dateOfBirth;
};
0 голосов
/ 28 января 2020

Проблема здесь в автоматически генерируемом деструкторе. Чтобы получить выгоду от предварительных объявлений, он должен go выглядеть следующим образом:

#include <memory>

class IDcom_impl;

class IDcom
{
public:
    IDcom_API ~IDcom();
    IDcom_API IDcom();

    void IDcom_API createList();

private:
    std::unique_ptr< IDcom_impl > m_impl;
};

idcom. cpp

#include "idcom_impl.h"
#include "idcom.h"

IDcom::~IDcom() = default;

IDcom::IDcom() :
    m_impl( new IDcom_impl())
{}

void IDcom::createList()
{
   m_impl->createList();
}

Обратите внимание, что если вы не объявите деструктор, компилятор попытается автоматически сгенерировать его. в каждой единице перевода, где может произойти разрушение. Чтобы иметь возможность генерировать деструктор, компилятор должен знать, как уничтожить поле m_impl, а это значит, что он должен знать деструктор IDcom_impl.

В результате преимущества шаблона pimpl сводятся на нет.

Если вы объявите деструктор, он будет связан с указанным c модулем перевода, который требует знаний о IDcom_impl, и ни один другой модуль перевода не имеет ничего о IDcom_impl.

...