Понадобилось работать именно с COM интерфейсами. Решил я тут воспользоваться WRL, чтобы не городить собственные QueryInterface-ы и иметь автоматически подсчет ссылок.
Но что-то встал в ступор, как его собственно использовать?
Сначала я написал 2 интерфейса:
interface DECLSPEC_UUID("{F1DFE67A-B796-4B95-ADE1-8AA030A7546D}") ITest1 : public IUnknown { public: virtual void DoTest1( ) = 0; }; interface DECLSPEC_UUID( "{94DE17EF-8125-491A-971A-69BD06DBAB6F}") ITest2 : public IUnknown { public: virtual void DoTest2( ) = 0; };
И далее написал реализацию их унаследовавшись от RuntimeClass:
class Test12 : public RuntimeClass<RuntimeClassFlags<ClassicCom>, ITest1, ITest2> { public: void DoTest1() override { std::cout << "DoTest1()\n"; } void DoTest2( ) override { std::cout << "DoTest2()\n"; } };
Теперь у меня появляется новый интерфейс:
interface DECLSPEC_UUID("{E053654A-DBC9-480D-B2BE-0FB5C2E95D53}") ITest3 : public ITest2 { public: virtual void DoTest3( ) = 0; };
class Test3 : public Test12, public RuntimeClass<RuntimeClassFlags<ClassicCom>, ITest3> { public: void DoTest3() override { std::cout << "DoTest3()\n"; } };
Теперь при попытке обявления:
Test3 tst;
Я получаю:
Лаааадно. Быть может не надо создавать классы прям так грубо? Говорят надо звать Microsoft::WRL::Make(). Что ж, давайте позовем:
ComPtr<Test3> tst1 = Make<Test3>();
error C2385: ambiguous access of 'Release'
Под спойлером весь код:
Давайте поможем Даше найти Саше создать объект класса Test3?
MrShoor
> Понадобилось работать именно с COM интерфейсами
херово тебе. ну ты там держись.
MrShoor
По-моему это типичная крестопроблема, точнее того, как в С++ реализуются интерфейсы. Понятно, что у тебя Test3 дважды наследует ITest2 и любое перекрытие его методов дает неоднозначность и вынос мозга. Я бы отказался от прямого наследования и получал бы необходимые интерфейсы через функции-члены. А внутри уже держал объекты-имплементации, которые бы эти интерфейсы реализовывали. Обычно этого хватает.
COM не работает с наследованием. В качестве хака можно сделать базовый шаблонный класс:
template <typename T> class Test12 : public T { }; class Test3 : public Test12<ITest3> { }
Что ожидал то?
Что она сама в Test3 подхватит реализацию DoTest2 из Test12? С какой стати, если оно совсем из другой ветки наследования? Она что, должна по принципу "похожести" добавлять реализации всего того, что мы не запрограммировали?
Виртуальное наследование не поможет, потому как с COM оно не совместимо. Виртуальность то должна быть для интерфейсов, если хочешь чтобы нечто подобное работало, а это уже никак не COM.
Решение задачки - добавить реализацию DoTest2 в Test3:
void DoTest2() override { Test12::DoTest2(); }
Или же сделать реализацию Test12 шаблонной и применять ее уже после наследования от ITest3
Zab
> Она что, должна по принципу "похожести" добавлять реализации всего того, что мы
> не запрограммировали?
Мы как раз это запрограммировали в Test12. ITest2 - полностью абстрактный, поэтому взять имплементацию ITest2 из Test12 - было бы вполне себе нормальным решением.
> Виртуальное наследование не поможет, потому как с COM оно не совместимо.
Что значит не совместимо? Вот вполне себе рабочий COM код:
> Решение задачки - добавить реализацию DoTest2 в Test3:
> void DoTest2() override { Test12::DoTest2(); }
Отличное решение. Представь теперь, что у меня в реальной задаче ITest2 содержит 50-100 методов, и при этом наследуется 20-30 раз по различным ITest3, ITest4 и прочим интерфейсам. Сколько там получается? 1000-3000 методов вот такого бойлерплейт кода?
Went
> Понятно, что у тебя Test3 дважды наследует ITest2 и любое перекрытие его
> методов дает неоднозначность и вынос мозга.
Причина мне тоже понятна, и данную проблему как раз решает виртуальное наследование. Но как его добиться через RuntimeClass - непонятно.
> Я бы отказался от прямого наследования и получал бы необходимые интерфейсы
> через функции-члены. А внутри уже держал объекты-имплементации, которые бы эти
> интерфейсы реализовывали. Обычно этого хватает.
Слишком много бойлерплейт кода.
MrShoor
> Вот вполне себе рабочий COM код
И в чем же он рабочий?
interface ITest1 : public virtual IUnknown
MrShoor
> Представь теперь, что у меня в реальной задаче ITest2 содержит 50-100 методов,
> и при этом наследуется 20-30 раз по различным ITest3, ITest4 и прочим
> интерфейсам. Сколько там получается? 1000-3000 методов вот такого бойлерплейт
> кода?
Откуда компилятору, бедняге, знать, где у тебя функции с одинаковыми именами это одно и тоже, а где должны быть разными. Они же из разных веток, разных классов. У него нет никакой причины их совмещать, твое описание таких указаний ему не дает.
Zab
> И в чем же он рабочий?
Во всём. А в чем он не рабочий?
> Это уже не COM, на двоичном уровне. Таблицы виртуальных функций другой формат
> получили, не совместимый с комом.
Чушь же.
> Откуда компилятору, бедняге, знать, где у тебя функции с одинаковыми именами
> это одно и тоже, а где должны быть разными. Они же из разных веток, разных
> классов. У него нет никакой причины их совмещать, твое описание таких указаний
> ему не дает.
Оттуда, что компилятор при виртуальном наследовании делает именно это:
void DoTest2() override { Test12::DoTest2( ); }
Используй ссылки и указатели на COM объекты test, а не сами объекты в шаблонах.
равен
> Используй ссылки и указатели на COM объекты test, а не сами объекты в шаблонах.
Можешь показать на моём примере что ты имеешь ввиду?
MrShoor
> > Используй ссылки и указатели на COM объекты test, а не сами объекты в
> > шаблонах.
> Можешь показать на моём примере что ты имеешь ввиду?
Думаю, он имеет в виду реализацию COM-объектов а-ля-mfc. Уродство, но работать будет. Я бы не стал так делать.
MrShoor
просто не делай так. Ты нарушаешь SOLID. Не надо наследовать интерфейсы друг от друга, COM тут не причём.
Мизраэль
> просто не делай так. Ты нарушаешь SOLID.
SOLID запрещает наследование в ООП? Или что там нарушается?
> Не надо наследовать интерфейсы друг от друга, COM тут не причём.
Ну интерфейсы уже унаследованы. Так что этот совет тут не поможет.
Тема в архиве.