Войти
ПрограммированиеФорумОбщее

Крестопроблемы. COM интерфейсы, множественное наследование + WRL

Страницы: 1 2 Следующая »
#0
1:12, 19 мар. 2019

Понадобилось работать именно с 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;
};
Который я хочу заметить унаследован от ITest2. Теперь я хочу расширить Test12 как-то так:

class Test3 : public Test12, public RuntimeClass<RuntimeClassFlags<ClassicCom>, ITest3>
{
public:
    void DoTest3() override {
        std::cout << "DoTest3()\n";
    }
};
То есть я унаследовался от Test12, и дописал еще один метод DoTest3.

Теперь при попытке обявления:
Test3 tst;
Я получаю:

C2259: 'Test3': cannot instantiate abstract class
due to following members:
'void ITest2::DoTest2(void)': is abstract
see declaration of 'ITest2::DoTest2'

Ладно, понятно, нужно виртуальное наследование. Пишу сюда virtual:
class Test12 : public virtual RuntimeClass<RuntimeClassFlags<ClassicCom>, ITest1, ITest2>
и ничего не меняется. Перед ITest1 и ITest2 в данной строке прописать virtual не канает. В общем никакая комбинация virtual мне не помогла.

Лаааадно. Быть может не надо создавать классы прям так грубо? Говорят надо звать Microsoft::WRL::Make(). Что ж, давайте позовем:

ComPtr<Test3> tst1 = Make<Test3>();
Теперь мы имеем:
error C2385: ambiguous access of 'Release'
Хохо. Если попробовать виртуальное наследование - то ситуация никак не меняется. По прежнему имеем ambiguous при наследовании от IUnknown.

Под спойлером весь код:

+ Показать

Давайте поможем Даше найти Саше создать объект класса Test3?

#1
14:28, 19 мар. 2019

MrShoor
> Понадобилось работать именно с COM интерфейсами
херово тебе. ну ты там держись.

#2
14:47, 19 мар. 2019

MrShoor
По-моему это типичная крестопроблема, точнее того, как в С++ реализуются интерфейсы. Понятно, что у тебя Test3 дважды наследует ITest2 и любое перекрытие его методов дает неоднозначность и вынос мозга. Я бы отказался от прямого наследования и получал бы необходимые интерфейсы через функции-члены. А внутри уже держал объекты-имплементации, которые бы эти интерфейсы реализовывали. Обычно этого хватает.

#3
16:01, 19 мар. 2019

COM не работает с наследованием. В качестве хака можно сделать базовый шаблонный класс:

template <typename T>
class Test12 : public T
{
};

class Test3 : public Test12<ITest3>
{
}
Но это такое.. Именно поэтому я и отказался в свое время от COM

#4
16:45, 19 мар. 2019

Что ожидал то?
Что она сама в Test3 подхватит реализацию DoTest2 из Test12? С какой стати, если оно совсем из другой ветки наследования? Она что, должна по принципу "похожести" добавлять реализации всего того, что мы не запрограммировали?

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

#5
16:53, 19 мар. 2019

Решение задачки - добавить реализацию DoTest2 в Test3:

    void DoTest2() override { Test12::DoTest2(); }

Или же сделать реализацию Test12 шаблонной и применять ее уже после наследования от ITest3

#6
18:41, 19 мар. 2019

Zab
> Она что, должна по принципу "похожести" добавлять реализации всего того, что мы
> не запрограммировали?
Мы как раз это запрограммировали в Test12. ITest2 - полностью абстрактный, поэтому взять имплементацию ITest2 из Test12 - было бы вполне себе нормальным решением.

> Виртуальное наследование не поможет, потому как с COM оно не совместимо.
Что значит не совместимо? Вот вполне себе рабочий COM код:

+ Показать

Только тут все приходится делать руками. Все работает, но не прикольно, что нужно перегружать QueryInterface в каждом наследнике. Собственно поэтому хотел взять RuntimeClass, чтобы оно само в QueryInterface могло, без бойлерплейт кода.

> Решение задачки - добавить реализацию DoTest2 в Test3:
> void DoTest2() override { Test12::DoTest2(); }
Отличное решение. Представь теперь, что у меня в реальной задаче ITest2 содержит 50-100 методов, и при этом наследуется 20-30 раз по различным ITest3, ITest4 и прочим интерфейсам. Сколько там получается? 1000-3000 методов вот такого бойлерплейт кода?

Went
> Понятно, что у тебя Test3 дважды наследует ITest2 и любое перекрытие его
> методов дает неоднозначность и вынос мозга.
Причина мне тоже понятна, и данную проблему как раз решает виртуальное наследование. Но как его добиться через RuntimeClass - непонятно.

> Я бы отказался от прямого наследования и получал бы необходимые интерфейсы
> через функции-члены. А внутри уже держал объекты-имплементации, которые бы эти
> интерфейсы реализовывали. Обычно этого хватает.
Слишком много бойлерплейт кода.

#7
18:45, 19 мар. 2019

MrShoor
> Вот вполне себе рабочий COM код
И в чем же он рабочий?

interface ITest1 : public virtual IUnknown
Это уже не COM, на двоичном уровне. Таблицы виртуальных функций другой формат получили, не совместимый с комом.
#8
18:48, 19 мар. 2019

MrShoor
> Представь теперь, что у меня в реальной задаче ITest2 содержит 50-100 методов,
> и при этом наследуется 20-30 раз по различным ITest3, ITest4 и прочим
> интерфейсам. Сколько там получается? 1000-3000 методов вот такого бойлерплейт
> кода?
Откуда компилятору, бедняге, знать, где у тебя функции с одинаковыми именами это одно и тоже, а где должны быть разными. Они же из разных веток, разных классов. У него нет никакой причины их совмещать, твое описание таких указаний ему не дает.

#9
18:53, 19 мар. 2019

Zab
> И в чем же он рабочий?
Во всём. А в чем он не рабочий?

> Это уже не COM, на двоичном уровне. Таблицы виртуальных функций другой формат
> получили, не совместимый с комом.
Чушь же.

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

void DoTest2() override { Test12::DoTest2(); }
Только сам. Не нужно это руками прописывать.

#10
19:02, 19 мар. 2019

  Используй ссылки и указатели на COM объекты test, а не сами объекты в шаблонах.

#11
19:06, 19 мар. 2019

равен
> Используй ссылки и указатели на COM объекты test, а не сами объекты в шаблонах.
Можешь показать на моём примере что ты имеешь ввиду?

#12
19:08, 19 мар. 2019

MrShoor
> > Используй ссылки и указатели на COM объекты test, а не сами объекты в
> > шаблонах.
> Можешь показать на моём примере что ты имеешь ввиду?
Думаю, он имеет в виду реализацию COM-объектов а-ля-mfc. Уродство, но работать будет. Я бы не стал так делать.

#13
20:19, 19 мар. 2019

MrShoor
просто не делай так. Ты нарушаешь SOLID. Не надо наследовать интерфейсы друг от друга, COM тут не причём.

#14
20:26, 19 мар. 2019

Мизраэль
> просто не делай так. Ты нарушаешь SOLID.
SOLID запрещает наследование в ООП? Или что там нарушается?

> Не надо наследовать интерфейсы друг от друга, COM тут не причём.
Ну интерфейсы уже унаследованы. Так что этот совет тут не поможет.

Страницы: 1 2 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.