Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.10.19 13:43
Оценка:
Исходная задача — выделить из монолита (APP, проект на C++, VS2019) одну из подсистем и оформить её в виде DLL.

То есть, часть С++ классов переместится в отдельную MODULE.DLL.

APP получает С++ объекты из MODULE.DLL и может их dynamic_cast-ить, для получения нужных абстрактных интерфейсов (INTERFACE1,INTERFACE2).

Возник вопрос — нужно ли эти INTERFACE1, INTERFACE2 экспортировать из какой-то третьей DLL? Ну типа Core.DLL.

Или не нужно?

-----
Накатал тест (VS2019) и обнаружил, что эти интерфейсы экспортировать не надо.

То есть просто объявляем их как обычно, без dllimport/dllexport:

class INTERFACE1 {../*pure virtual methods*/..};
class INTERFACE2 {../*pure virtual methods*/..};

и без проблем — dynamic_cast на уровне APP, нормально к ним кастит.

Что удивительно.

Более того, если в MODULE.DLL определить внутренний абстрактный интерфейс (DUMMY_INTERFACE), который не светится в публичных хедерах, а в APP продублировать определение этого DUMMY_INTERFACE — то dynamic_cast<DUMMY_INTERFACE*> на уровне APP тоже отработает без проблем. Понятно, если у дубля будет иная бинарная структура чем у оригинала, то придет северное жывотное.

То есть, dynamic_cast юзает имя класса, без привязки к бинарнику.

Возникает вопрос - стоит ли полагаться на эту фичу?

Или таки надежнее завести CORE.DLL, которая будет эти интерфейсы экспортировать?

Я склоняюсь ко второму варианту — экспортировать. Все таки, это же модули "на базе C++", а не COM

Опять же, мало ли — вдруг потом "фичу" сломают?

Что скажете?

-----
  Примеры реальных абстрактных интерфейсов
class __declspec(novtable) t_smart_interface
{
 public:
  virtual void add_ref()=0;
  virtual void release()=0;
};//class t_smart_interface

class __declspec(novtable) t_err_record:public t_smart_interface
{
 private:
  typedef t_err_record                                self_type;

 public: //typedefs -------------------------------------------------------
  typedef t_smart_object_ptr<self_type>               self_ptr;
  typedef t_smart_object_ptr<const self_type>         self_cptr;

  typedef long                                        error_code_type;
  typedef std::wstring                                string_type;
  typedef long                                        subsystem_id_type;
  typedef t_lcid                                      lcid_type;
  typedef long                                        help_ctx_id_type;

  enum system_id_type
  {
   system_user    =0,

   //Error from WIN32 System
   system_win32   =1,

   //Error from Component Objects services
   system_com     =2,

   //first free index
   system_free    =0x00010000,
  };//enum system_id_type

  enum
  {
   user_subsystem_unk =0,

   //user subsystems [1...0xFFFF] reserved for this library
   user_subsystem_free=0x00010000
  };//enum

 public: //error_record interface -----------------------------------------

  //return simple string with error description -----------
  virtual const char*        what()const throw()=0;

  //extended error informations ---------------------------
  virtual system_id_type     get_system_id()    const=0;

  virtual subsystem_id_type  get_subsystem_id() const=0;

  virtual error_code_type    get_error_code()   const=0;

  virtual bool get_description(lcid_type       lcid,
                               string_type*    source,
                               string_type*    description)const=0;

  virtual bool get_help_info(lcid_type         lcid,
                             string_type*      help_file,
                             help_ctx_id_type* help_context_id)const=0;
};//class t_err_record

Если определить эти интейфейсы с dllexport, то в секции экспорта CORE.DLL появляются методы:
??0t_smart_interface@structure@core@infrastructure@lcpi@@QAE@$$QAV01234@@Z
??0t_smart_interface@structure@core@infrastructure@lcpi@@QAE@ABV01234@@Z
??0t_smart_interface@structure@core@infrastructure@lcpi@@QAE@XZ
??4t_smart_interface@structure@core@infrastructure@lcpi@@QAEAAV01234@$$QAV01234@@Z
??4t_smart_interface@structure@core@infrastructure@lcpi@@QAEAAV01234@ABV01234@@Z

??0t_err_record@structure@core@infrastructure@lcpi@@QAE@$$QAV01234@@Z
??0t_err_record@structure@core@infrastructure@lcpi@@QAE@ABV01234@@Z
??0t_err_record@structure@core@infrastructure@lcpi@@QAE@XZ
??4t_err_record@structure@core@infrastructure@lcpi@@QAEAAV01234@$$QAV01234@@Z
??4t_err_record@structure@core@infrastructure@lcpi@@QAEAAV01234@ABV01234@@Z

По-ходу, это всякие встроенные конструкторы/деструкторы/операторы.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.10.19 17:08
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Возник вопрос — нужно ли эти INTERFACE1, INTERFACE2 экспортировать из какой-то третьей DLL? Ну типа Core.DLL.


КД>Или не нужно?


Нашел такое предупреждение компилятора (C4275):
https://docs.microsoft.com/ru-ru/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=vs-2019

С одной стороны, это предупреждение вылазит когда:

An exported class was derived from a class that wasn't exported.

Типа:

Экспортируемый класс наследует неэкспортируемый класс.


С другой стороны, в этом документе пишут:

You can avoid exporting classes by defining a DLL that defines a class with virtual functions, ...

То есть:

Вы можете избежать экспорта путем определение класса с виртуальными методами ....

Похоже, вывод и ответ на мои сомнения в начальном сообщении такой:

Интерфейсы экспортировать надо. Хотя бы для того, чтобы не провоцировать C4275. Вдруг производный класс тоже будет экспортироваться ...

Конечные классы, целиком и полностью состоящие из виртуальных методов, можно не экспортировать.

Как-то так.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Экспорт интерфейсов из DLL
От: kov_serg Россия  
Дата: 27.10.19 18:23
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Исходная задача — выделить из монолита (APP, проект на C++, VS2019) одну из подсистем и оформить её в виде DLL.

КД>Я склоняюсь ко второму варианту — экспортировать. Все таки, это же модули "на базе C++", а не COM
Не надо экспортировать интерфейсы. Достаточно только объявить интерфейсы в заголовочных файлах.
Лучше сделать как в COM что бы класс умел себя удалять сам (дабы освободиться от конкретного runtime)
Далее проблемы могут возникать с параметрами которые из другого runtime если не по ссылкам передавать, можно огрести даже с тем же std::string или с векторами и т.п.
КД>Опять же, мало ли — вдруг потом "фичу" сломают?
Не сломают, иначе потеряется обратная совместимость.

КД>[cut=Примеры реальных абстрактных интерфейсов]

...
КД>Если определить эти интейфейсы с dllexport, то в секции экспорта CORE.DLL появляются методы:
КД>По-ходу, это всякие встроенные конструкторы/деструкторы/операторы.
То что у вас экспортировалось это просто конструкторы и операторы присвоения по умолчанию (они нафиг никому не нужны)
Re: Экспорт интерфейсов из DLL
От: GhostCoders Россия  
Дата: 28.10.19 05:14
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Исходная задача — выделить из монолита (APP, проект на C++, VS2019) одну из подсистем и оформить её в виде DLL.

Используй для этого beautiful capi — https://github.com/PetrPPetrov/beautiful-capi
Третий Рим должен пасть!
Re[2]: Экспорт интерфейсов из DLL
От: GhostCoders Россия  
Дата: 28.10.19 05:44
Оценка: +1
Здравствуйте, GhostCoders, Вы писали:

GC>Используй для этого beautiful capi — https://github.com/PetrPPetrov/beautiful-capi


Ключевые особенности:
— Распилка всех классов на С функции, потом сборка этих С функций в класс заново в header only style клиенте.
Как результат клиент может использовать как С, так и С++ API
— Нет QueryInterface, как в COM-подобных системах, но зато есть down_cast<>() — это фактически dynamic_cast,
однако обработка на стороне библиотеки, чтобы не было проблем с бинарной несовместимостью RTTI.
Из-за этого библиотека может быть на MSVC 2019, а клиент на Borland C++ compiler или Digital Mars C++ compiler.
— Вся аллокация\деаллокация идет на стороне библиотеки, чтобы не было проблем с разными менеджерами хипов
— Экспортируемые С функции имеют простые, понятные имена. Вместо знаков кракозябров (@!? и прочих),
используется только знак подчеркивания (_). Правило такое: сначала идет имя неймспейса (переведенное в snake_case),
затем добавляется имя класса (переведенное в snake_case), и имя метода в этом же стиле.
Например, метод Show класса Printer неймспейса Hello будет иметь имя hello_printer_show.
— Решается проблема с исключениями
Третий Рим должен пасть!
Re[3]: Экспорт интерфейсов из DLL
От: kov_serg Россия  
Дата: 28.10.19 07:34
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>>Используй для этого beautiful capi — https://github.com/PetrPPetrov/beautiful-capi


GC>Ключевые особенности:

GC>- Распилка всех классов на С функции, потом сборка этих С функций в класс заново в header only style клиенте.
GC> Как результат клиент может использовать как С, так и С++ API
GC>- Нет QueryInterface, как в COM-подобных системах, но зато есть down_cast<>() — это фактически dynamic_cast,
GC> однако обработка на стороне библиотеки, чтобы не было проблем с бинарной несовместимостью RTTI.
GC> Из-за этого библиотека может быть на MSVC 2019, а клиент на Borland C++ compiler или Digital Mars C++ compiler.
GC>- Вся аллокация\деаллокация идет на стороне библиотеки, чтобы не было проблем с разными менеджерами хипов
GC>- Экспортируемые С функции имеют простые, понятные имена. Вместо знаков кракозябров (@!? и прочих),
GC> используется только знак подчеркивания (_). Правило такое: сначала идет имя неймспейса (переведенное в snake_case),
GC> затем добавляется имя класса (переведенное в snake_case), и имя метода в этом же стиле.
GC> Например, метод Show класса Printer неймспейса Hello будет иметь имя hello_printer_show.
GC>- Решается проблема с исключениями

Еще бы файлы описания были бы в человеческом формате, а не только в xml.
Re[4]: Экспорт интерфейсов из DLL
От: GhostCoders Россия  
Дата: 28.10.19 07:46
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Еще бы файлы описания были бы в человеческом формате, а не только в xml.

Если честно не вижу проблемы с XML. Его проще парсить. В студии можно добавить .xsd схему для него (https://github.com/PetrPPetrov/beautiful-capi/blob/master/source/Capi.xsd)
и иметь auto-complete при наборе XML в студии.

Еще есть тикет — https://github.com/PetrPPetrov/beautiful-capi/issues/24
В двух словах: можно создать свой DSL для описания АПИ, парсить его при помощи, скажем LL(1) грамматик (или чем-то похожим),
а затем перегонять в структуру, которая сейчас получается после загрузки XML. То есть остальной код bcapi останется без изменений.
Однако у меня руки до этого не доходят.

Есть еще более интересная задумка — https://github.com/PetrPPetrov/beautiful-capi/issues/74
При помощи clang AST автоматически генерировать XML с описанием АПИ. Есть уже отдельный репозиторий с первыми шагами, но нет времени продолжать это.
Третий Рим должен пасть!
Re[2]: Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 28.10.19 07:47
Оценка:
Здравствуйте, kov_serg, Вы писали:

КД>>Исходная задача — выделить из монолита (APP, проект на C++, VS2019) одну из подсистем и оформить её в виде DLL.

КД>>Я склоняюсь ко второму варианту — экспортировать. Все таки, это же модули "на базе C++", а не COM
_>Не надо экспортировать интерфейсы. Достаточно только объявить интерфейсы в заголовочных файлах.
_>Лучше сделать как в COM что бы класс умел себя удалять сам (дабы освободиться от конкретного runtime)

Так и сделано для большей части классов. См. t_smart_interface — это упрощенный IUnknown.

_>Далее проблемы могут возникать с параметрами которые из другого runtime если не по ссылкам передавать, можно огрести даже с тем же std::string или с векторами и т.п.


Рантайм у меня один и тот же. Я сам это дело буду контролировать.

КД>>Опять же, мало ли — вдруг потом "фичу" сломают?

_>Не сломают, иначе потеряется обратная совместимость.

Если вдруг они решат, что надо сломать — сломают

КД>>Если определить эти интейфейсы с dllexport, то в секции экспорта CORE.DLL появляются методы:

КД>>По-ходу, это всякие встроенные конструкторы/деструкторы/операторы.
_>То что у вас экспортировалось это просто конструкторы и операторы присвоения по умолчанию (они нафиг никому не нужны)

Меня тут посетила мысль.

Нужно явно запретить часть этих методов у одного из (экспортируемых) классов — t_smart_interface. От него наследуется еще один (тоже экспортируемый) класс — t_err_record.
  Старое определение экспортируемого класса
class LCPI_INFRASTRUCTURE_CORE__CFG__CLASS_STG __declspec(novtable) t_smart_interface
{
 public:
  virtual void add_ref()=0;
  virtual void release()=0;
};//class t_smart_interface

  Таблица экспорта DLL
??0t_err_record@structure@core@infrastructure@lcpi@@QEAA@$$QEAV01234@@Z
??0t_err_record@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_err_record@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_err_records_r@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_err_records_r@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_err_records_w@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_err_records_w@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_exception@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_exception@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_lcid@structure@core@infrastructure@lcpi@@QEAA@I@Z
??0t_lcid@structure@core@infrastructure@lcpi@@QEAA@V?$t_explicit@PEBD@1234@@Z
??0t_lcid@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_smart_interface@structure@core@infrastructure@lcpi@@QEAA@$$QEAV01234@@Z
??0t_smart_interface@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_smart_interface@structure@core@infrastructure@lcpi@@QEAA@XZ
??1t_err_records_r@structure@core@infrastructure@lcpi@@UEAA@XZ
??1t_err_records_w@structure@core@infrastructure@lcpi@@UEAA@XZ
??1t_exception@structure@core@infrastructure@lcpi@@UEAA@XZ
??4t_err_record@structure@core@infrastructure@lcpi@@QEAAAEAV01234@$$QEAV01234@@Z
??4t_err_record@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_err_records_r@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_err_records_w@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_lcid@structure@core@infrastructure@lcpi@@QEAAAEAV01234@$$QEAV01234@@Z
??4t_lcid@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_smart_interface@structure@core@infrastructure@lcpi@@QEAAAEAV01234@$$QEAV01234@@Z
??4t_smart_interface@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??_7t_exception@structure@core@infrastructure@lcpi@@6B@
?__autoclassinit2@t_lcid@structure@core@infrastructure@lcpi@@QEAAX_K@Z
?get_kind@t_lcid@structure@core@infrastructure@lcpi@@QEBA?AW4enum_kind@12345@XZ
?get_name@t_lcid@structure@core@infrastructure@lcpi@@QEBAPEBDXZ
?get_number@t_lcid@structure@core@infrastructure@lcpi@@QEBAIXZ
?raise@t_exception@structure@core@infrastructure@lcpi@@UEBAXXZ

  Новое определение этого же класса
class LCPI_INFRASTRUCTURE_CORE__CFG__CLASS_STG __declspec(novtable) t_smart_interface
{
 public:
  t_smart_interface()=default;

  t_smart_interface(const t_smart_interface&)=delete;
  t_smart_interface(t_smart_interface&&)=delete;

 ~t_smart_interface()=default;

  t_smart_interface& operator = (const t_smart_interface&)=delete;
  t_smart_interface& operator = (t_smart_interface&&)=delete;

 public:
  virtual void add_ref()=0;
  virtual void release()=0;
};//class t_smart_interface

  Таблица экспорта DLL
??0t_err_record@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_err_records_r@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_err_records_r@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_err_records_w@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_err_records_w@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_exception@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_exception@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_lcid@structure@core@infrastructure@lcpi@@QEAA@I@Z
??0t_lcid@structure@core@infrastructure@lcpi@@QEAA@V?$t_explicit@PEBD@1234@@Z
??0t_lcid@structure@core@infrastructure@lcpi@@QEAA@XZ
??0t_smart_interface@structure@core@infrastructure@lcpi@@QEAA@XZ
??1t_err_records_r@structure@core@infrastructure@lcpi@@UEAA@XZ
??1t_err_records_w@structure@core@infrastructure@lcpi@@UEAA@XZ
??1t_exception@structure@core@infrastructure@lcpi@@UEAA@XZ
??4t_err_records_r@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_err_records_w@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_lcid@structure@core@infrastructure@lcpi@@QEAAAEAV01234@$$QEAV01234@@Z
??4t_lcid@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??_7t_exception@structure@core@infrastructure@lcpi@@6B@
?__autoclassinit2@t_lcid@structure@core@infrastructure@lcpi@@QEAAX_K@Z
?get_kind@t_lcid@structure@core@infrastructure@lcpi@@QEBA?AW4enum_kind@12345@XZ
?get_name@t_lcid@structure@core@infrastructure@lcpi@@QEBAPEBDXZ
?get_number@t_lcid@structure@core@infrastructure@lcpi@@QEBAIXZ
?raise@t_exception@structure@core@infrastructure@lcpi@@UEBAXXZ

  Стала меньше на 8 элементов. Исчезли:
??0t_err_record@structure@core@infrastructure@lcpi@@QEAA@$$QEAV01234@@Z
??0t_err_record@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??0t_smart_interface@structure@core@infrastructure@lcpi@@QEAA@$$QEAV01234@@Z
??0t_smart_interface@structure@core@infrastructure@lcpi@@QEAA@AEBV01234@@Z
??4t_err_record@structure@core@infrastructure@lcpi@@QEAAAEAV01234@$$QEAV01234@@Z
??4t_err_record@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z
??4t_smart_interface@structure@core@infrastructure@lcpi@@QEAAAEAV01234@$$QEAV01234@@Z
??4t_smart_interface@structure@core@infrastructure@lcpi@@QEAAAEAV01234@AEBV01234@@Z

Этого же эффекта можно было добиться запретом оператора копирования/перемещения. Конструкторы/деструкторы — это я на всякий случай добавил.

Пожалуй, оставлю эти изменения в t_smart_interface.

---
Жаль, что нельзя запретить конструктор по умолчанию и деструктор в t_smart_interface. Они там даром не нужны. Но увы — компилятор их требует
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Экспорт интерфейсов из DLL
От: Mr.Delphist  
Дата: 28.10.19 09:29
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Здравствуйте, GhostCoders, Вы писали:


GC>>Используй для этого beautiful capi — https://github.com/PetrPPetrov/beautiful-capi


GC>Ключевые особенности:

GC>- Распилка всех классов на С функции, потом сборка этих С функций в класс заново в header only style клиенте.
GC> Как результат клиент может использовать как С, так и С++ API
GC>- Нет QueryInterface, как в COM-подобных системах, но зато есть down_cast<>() — это фактически dynamic_cast,
GC> однако обработка на стороне библиотеки, чтобы не было проблем с бинарной несовместимостью RTTI.
GC> Из-за этого библиотека может быть на MSVC 2019, а клиент на Borland C++ compiler или Digital Mars C++ compiler.
GC>- Вся аллокация\деаллокация идет на стороне библиотеки, чтобы не было проблем с разными менеджерами хипов
GC>- Экспортируемые С функции имеют простые, понятные имена. Вместо знаков кракозябров (@!? и прочих),
GC> используется только знак подчеркивания (_). Правило такое: сначала идет имя неймспейса (переведенное в snake_case),
GC> затем добавляется имя класса (переведенное в snake_case), и имя метода в этом же стиле.
GC> Например, метод Show класса Printer неймспейса Hello будет иметь имя hello_printer_show.
GC>- Решается проблема с исключениями

Более того, далеко не все языки умеют импортить сырые плюсовые интерфейсы, даже без учёта приколов с манглингом имён. Плоский сишный интерфейс в этом плане куда более интегрируемый.
Re[4]: Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 28.10.19 09:47
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Более того, далеко не все языки умеют импортить сырые плюсовые интерфейсы, даже без учёта приколов с манглингом имён. Плоский сишный интерфейс в этом плане куда более интегрируемый.


Для меня это не актуально ... от слова совсем

Монолит представляет собой COM-сервер.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Экспорт интерфейсов из DLL
От: GhostCoders Россия  
Дата: 28.10.19 11:57
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Монолит представляет собой COM-сервер.

Почему же не оформляете эти объекты как COM-интерфейсы?
Третий Рим должен пасть!
Re[6]: Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 28.10.19 12:53
Оценка:
Здравствуйте, GhostCoders, Вы писали:

КД>>Монолит представляет собой COM-сервер.

GC>Почему же не оформляете эти объекты как COM-интерфейсы?

Потому что это внутренности. И мне не надо эти внутренности делать по правилам COM-интерфейсов.

Но.

Эти внутренности продублированы еще в одном сервере COM-объектов.

Поэтому я их хочу вытащить в отдельный модуль, который будет обслуживать оба COM-сервера.

  Как-то так
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[7]: Экспорт интерфейсов из DLL
От: GhostCoders Россия  
Дата: 28.10.19 14:00
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Потому что это внутренности. И мне не надо эти внутренности делать по правилам COM-интерфейсов.

Ясно. Тогда вопросов нет.
Третий Рим должен пасть!
Re: Экспорт интерфейсов из DLL
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 28.10.19 20:49
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Или таки надежнее завести CORE.DLL, которая будет эти интерфейсы экспортировать?


Чистые абстрактные классы экспортировать не только не нужно, но и бессмысленно — они же никак не участвуют в связывании. То, что помещается в таблицы при добавлении dllexport к их определениям — это, как тут уже отметили, чисто служебный код, генерируемый компилятором. В других модулях, где эти интерфейсы будут определены, компилятор сгенерит точно такой же код, а без определения их невозможно будет использовать.

По сути, абстрактный класс описывает только сигнатуры виртуальных функций и размер/порядок vtable, а все это не может экспортироваться через структуры PE.
Re[2]: Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 29.10.19 07:19
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

КД>>Или таки надежнее завести CORE.DLL, которая будет эти интерфейсы экспортировать?


ЕМ>Чистые абстрактные классы экспортировать не только не нужно, но и бессмысленно — они же никак не участвуют в связывании.


В конечном итоге, я тоже пришел к такому выводу.

Меня немного напрягало предупреждение C4275, но я решил на него забить.

Все нормально работает и без экспорта.

"CORE.DLL" все равно завел — поместил туда некоторые служебные обычные классы и абстрактные классы с виртуальными деструкторами.

ЕМ>То, что помещается в таблицы при добавлении dllexport к их определениям — это, как тут уже отметили, чисто служебный код, генерируемый компилятором.


Я сократил до одного экспортируемого метода на каждый экспортируемый интерфейс.

Полагаю, это конструктор по умолчанию, изничтожить который не получилось.

ЕМ>В других модулях, где эти интерфейсы будут определены, компилятор сгенерит точно такой же код, а без определения их невозможно будет использовать.


Дело в том, что меня пугал возможный конфликт реализаций этих самых служебных методов.

Типа APP компилируется с двумя (статическими) либами, которые в свою очередь юзают интерфейс. Вдруг реализации сгенерированных методов интерфейса при компиляции APP будут конфликтовать?

Опыта с такими вещами нет, поэтому голова придумывает потенциальные засады
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Экспорт интерфейсов из DLL
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 29.10.19 12:15
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Я сократил до одного экспортируемого метода на каждый экспортируемый интерфейс.


А смысл?

КД>Дело в том, что меня пугал возможный конфликт реализаций этих самых служебных методов.

КД>Типа APP компилируется с двумя (статическими) либами, которые в свою очередь юзают интерфейс. Вдруг реализации сгенерированных методов интерфейса при компиляции APP будут конфликтовать?

Конфликтовать может то, что участвует в связывании компоновщиком (link). В данном случае это только члены производных классов. Это легко проверить, сделав LIB или DLL, где в виртуальные функции интерфейса объявлены в одном порядке, и связав с нею модуль, в котором функции объявлены в другом порядке. Если связывание идет по именам функций, экспортируемых из библиотеки (например, в модуле явно создается объект класса, методы которого экспортируются библиотекой, и объявлены в модуле), то компилятор положит в vtable правильные адреса, взяв их из внешних ссылок. Если же библиотека не экспортирует имен и/или модуль их не импортирует (например, адрес объекта класса, реализованного библиотекой, добывается косвенным образом), то компилятор будет класть в vtable адреса в том порядке, в котором они объявлены в модуле.

  Пример
Файл if1.h:

__interface Interface {

virtual int First (void) const abstract;
virtual int Second (void) const abstract;
virtual int Third (void) const abstract;

};

Interface const & GetIf ();



Файл if2.h:

__interface Interface {

virtual int Third (void) const abstract;
virtual int Second (void) const abstract;
virtual int First (void) const abstract;

};

Interface const & GetIf ();



Файл lib.cpp:

#include "if1.h"

class C1 : public Interface {

public:

virtual int First (void) const;
virtual int Second (void) const;
virtual int Third (void) const;

};

C1 o;

int C1::First (void) const { return 1; }

int C1::Second (void) const { return 2; }

int C1::Third (void) const { return 3; }

Interface const & GetIf () { return o; }



Файл app.cpp:

#include <stdio.h>

#include "if2.h"

__pragma (comment (lib, "lib.lib"))

void main (void) {

Interface const & r = GetIf ();

printf ("First: %u, Second: %u, Third: %u\n", r.First (), r.Second (), r.Third ());

}



Сборка:

cl /c lib.cpp
lib /out:lib.lib lib.obj
cl app.cpp
Re[4]: Экспорт интерфейсов из DLL
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 29.10.19 12:25
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

КД>>Я сократил до одного экспортируемого метода на каждый экспортируемый интерфейс.


ЕМ>А смысл?


1. Исследование

2. Явно запретил недопустимые операции
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.