Я хочу реализовать пользовательскую реализацию COM в C ++ на платформе типа UNIX, чтобы позволить мне динамически загружать и связывать объектно-ориентированный код. Я думаю, что это будет основано на аналогичном наборе функциональных возможностей, которые POSIX предоставляет для загрузки и вызова dll, т.е. dlopen, dlsym и dlclose.
На самом простом уровне COM реализован с помощью интерфейсов. В c ++, если вас устраивает идея чисто виртуальных или абстрактных базовых классов, то вы уже знаете, как определить интерфейс в c ++
struct IMyInterface {
void Method1() =0;
void Method2() =0;
};
Среда выполнения COM предоставляет множество дополнительных сервисов, которые применяются к среде Windows, но на самом деле не нужны при реализации «мини» COM в одном приложении в качестве средства для динамической связи с большим количеством ОО-интерфейсов, чем это традиционно допускает dlopen. , дслым и т. д.
COM-объекты реализованы в файлах .dll, .so или .dylib в зависимости от вашей платформы. Эти файлы должны экспортировать хотя бы одну стандартизированную функцию: DllGetClassObject
В вашей собственной среде вы можете создать его так, как вам хочется, но взаимодействовать со средой выполнения COM на окнах, очевидно, что имя и параметры должны соответствовать стандарту com.
Основная идея заключается в том, что передается указатель на GUID - 16 байтов, которые уникальным образом назначаются конкретному объекту, и он создает (на основе GUID) и возвращает IClassFactory * фабричного объекта.
Затем фабричный объект используется средой выполнения COM для создания экземпляров объекта при вызове метода IClassFactory :: CreateInstance.
Итак, пока у вас есть
- динамическая библиотека, экспортирующая хотя бы один символ с именем «DllGetClassObject» (или некоторый его вариант)
- Метод DllGetClassObject, который проверяет переданный GUID, чтобы узнать, запрашивается ли и какой объект, и затем выполняет «новый CSomeObjectClassFactory»
- Реализация CSomeObjectClassFactory, которая реализует (наследует) IClassFactory и реализует метод CreateInstance для «новых» экземпляров CSupportedObject.
- CSomeSupportedObject, который реализует пользовательский или определенный COM интерфейс, производный от IUnknown. Это важно, потому что IClassFactory :: CreateInstance передается IID (опять же, 16-байтовый уникальный идентификатор, определяющий интерфейс на этот раз), для которого ему потребуется QueryInterface для объекта.
Я понимаю, что общая идея COM заключается в том, что вы ссылаетесь на несколько функций, например QueryInterface, AddRef и Release, в общей dll (Kernel32.dll), которая затем позволяет вам получить доступ к интерфейсам, которые являются просто таблицей указатели на функции, инкапсулированные указателем на объект, для которого должны быть вызваны указатели на функции. Эти функции доступны через IUnknown, от которого вы должны наследовать.
На самом деле COM реализован с помощью OLE32.dll, который предоставляет API-интерфейс c, называемый CoCreateInstance. Приложение передало CoCreateInstance GUID, который он ищет в реестре Windows, в котором есть БД GUID -> сопоставления «путь к dll». Затем OLE / COM загружает (dlopen) dll, вызывает его метод DllGetClassObject (dlsym), снова передает GUID, при условии, что это успешно, OLE / COM затем вызывает CreateInstance и возвращает полученный интерфейс приложению.
Так как все это работает? Есть ли лучший способ динамически связать и загрузить объектно-ориентированный код? Как работает наследование от dll - каждый ли вызов базового класса должен относиться к открытой функции-члену, т.е. private / protected / public просто игнорируется?
неявное наследование кода c ++ от dll / so / dylib работает путем экспорта каждого метода в классе как «украшенный» символ. Имя метода украшено классом и типом каждого параметра. Это так же, как символы экспортируются из статических библиотек (.a или .lib файлы iirc). Статические или динамические библиотеки, "частные, защищенные и т. Д." всегда выполняются компилятором, анализируя заголовочные файлы, а не компоновщиком.
Я достаточно хорошо разбираюсь в C ++ и шаблонном метапрограммировании, и у меня уже есть полностью рефлексивная система C ++, т.е. свойства-члены, функции-члены и глобальные / статические функции, использующие boost.
классы c ++ обычно могут быть экспортированы только из dll со статической связью - dll, которые загружаются при загрузке, а не через dlopen во время выполнения. COM позволяет динамически загружать интерфейсы c ++, гарантируя, что все типы данных, используемые в COM, являются либо типами pod, либо являются чисто виртуальными интерфейсами. Если вы нарушите это правило, определив интерфейс, который пытается передать повышение или любой другой тип объекта, вы быстро попадете в ситуацию, когда компилятору / компоновщику понадобится больше, чем просто файл заголовка, чтобы выяснить, что происходит, и ваш Тщательно подготовленный «com» dll должен быть статически или неявно связан, чтобы функционировать.
Другое правило COM - никогда не передавать права собственности на объект через границы динамической библиотеки. то есть никогда не возвращать интерфейс или данные из библиотеки DLL и требовать, чтобы приложение удалило их. Все интерфейсы должны реализовывать IUnknown или, по крайней мере, метод Release (), который позволяет объекту выполнить this. Любые возвращаемые типы данных также должны иметь хорошо известный деселектор - если у вас есть интерфейс с методом с именем «CreateBlob», вероятно, должен быть метод приятеля с именем «DeleteBlob».