Я знаю, что COM обеспечивает возможность многократного использования на двоичном уровне через языки и приложения. Я считал, что все компоненты, созданные для COM, должны придерживаться стандартного расположения памяти, чтобы быть независимыми от языка.
Я не понимаю то, что "означает стандартное расположение памяти".
Что делает COM независимый от языка?
Во-первых, некоторая техническая подготовка : Компиляторы Си++ обычно генерируют нечто, называемое "vtable" для любого класса с виртуальными функциями. В основном это таблица указателей на функции. Таблица содержит указатель функции на каждый виртуальный метод, реализованный классом.
В COM интерфейсах в основном представлены абстрактные базовые классы, которые реализует компонент, например:
class CSomeComponent : IUnknown, ISomeOtherInterface { ... };
В vtable для CSomeComponent
будут включены указатели функций для всех методов, определенных в этих двух интерфейсах.
struct __imaginary_vtable_for_CSomeComponent
{
// methods required by IUnknown
HRESULT (*QueryInterface)( const IID& iid, void** ppv );
ULONG (*AddRef)();
ULONG (*Release)();
// methods required by ISomeOtherInterface
void (*foo)();
...
};
Любой инстанцированный объект имеет ссылку на переменную своего динамического типа. Таким образом, программа знает, как вызвать соответствующий метод в случае переопределения базового метода в производном классе:
class Base
{
public:
virtual void foo() { ... }
}
class Derived : public Base
{
public:
virtual void foo() { ... } // overrides Base::foo()
virtual void bar() { ... }
}
...
Base* X = new Derived;
X->foo();
Последняя строка должна вызывать Derived::foo
. Это работает, так как объект X
имеет ссылку на vtable for класса Derived
. Как уже говорилось, эта переменная похожа на список указателей функции. Теперь, vtable имеет фиксированную компоновку: Если класс Derived
наследует от класса Base
, то указатель функции для метода foo
будет находиться в том же относительном местоположении в переменной Derived
, что и в переменной Base
:
struct __imaginary_vtable_for_Base
{
void (*foo)();
};
// __imaginary_vtable_for_Base::foo = Base::foo
struct __imaginary_vtable_for_Derived
{
void (*foo)();
void (*bar)();
};
// __imaginary_vtable_for_Derived::foo = Derived::foo
Теперь, если компилятор видит что-то вроде X->foo()
, он знает, что для всех классов, полученных из Base
, метод foo
соответствует первой записи в таблетке. Поэтому он производит вызов первого указателя функции, который в случае X
является вызовом к Derived::foo
.
Ответ на вопрос : Компиляторы могут генерировать COM компоненты только в том случае, если они генерируют ту же самую компоновку для таблиц, которую требует спецификация COM. Таблицы могут быть реализованы различными способами, особенно когда речь идет о множественном наследовании (что требуется для COM-компонентов). Соблюдение определённого vtable-формата необходимо для того, чтобы при вызове метода компонента f
фактически вызывался метод f
, а не какой-то другой метод g
, который случайно окажется на позиции f
в vtable класса компонента. Полагаю, что COM-совместимые компиляторы, по сути, должны выдавать те же векторные компоновки, что и Microsoft Visual C++, так как технология COM была определена компанией Microsoft.
P.S. : Извините, что я настолько технический, надеюсь, что приведенная выше информация будет вам полезна.
] Дон Бокс написал отличное объяснение в первой главе своей книги Essential COM. Вы можете прочитать его [] здесь [] бесплатно. Я рекомендую. [
]Стандартный макет памяти означает макет памяти, который определен (стандартизирован) в спецификации COM, а не тот, который по умолчанию используется вызывающим или определяющим языком. И именно такие вещи делают его независимым от языка! Код на определенном языке, вызывающий COM-объект, не должен заботиться о том, на каком языке был написан этот COM-объект, с точки зрения знания его структуры в памяти.
Насколько я помню, COM - это язык, не зависящий от того, как документируется и открывается его структура (?). Недостатком является то, что структура является "бинарной" и тесно связана с Си/Си++, что затрудняет ее использование из других языков. Хорошая новость заключается в том, что многие языки (например, Python) имеют интерфейс C/C++, что позволяет (модуль Python Win32com) использовать его из других языков.
JSON является языком независимым таким образом, что не привязывается непосредственно к какому-либо языку, хотя Python, Perl и Ruby (?) близки, но почти невозможно использовать из C/C++
Надеюсь, это имеет какой-то смысл
.