>файл atlcom.h хоть раз в жизни открывали? до atlbase.inl проходили? Открывали, и не только этот файл, но и тот, из которого вы скопировали <a href=«habrahabr.ru/company/abbyy/blog/113429/#comment_3644177>»default behavior"
>хотите ослеживать всё — вставьте в начало карты блайнд функцию, сохраняйте всё что угодно.
Да, но как узнать, что произошло потом? Чтобы знать точно, нужно выводить также и вызовы конкретных функций запроса. Поэтому blind-функцию надежнее ставить только в самом конце «карты», когда уже точно не было возможности извлечь интерфейс.
В то же время, если вернуть E_NOINTERFACE (или любой код, для которого FAILED дает «истину») из обычной (не blind) функции запроса, код, вызвавший эту функцию запроса, перестанет перебирать карту — расчет на то, что раз фунцию вызвали, она должна извлечь интерфейс успешно, в противном случае что-то пошло не так и дальше пытаться бессмысленно.
>riid содержит тот интерфейс который хотят запросить, проверок на «идентификатор интерфейса» никаких не надо, всё проверено до этого и именно потому попало в нужную вам функцию.
Он должен содержать тот интерфейс, и это стоит проверить, тем более что это совсем нетрудно.
>смешивает безопасное приведение, например, к базовому типу, с небезопасным — от базового класса к потомкам
Справедливо, в описанном случае безопасность static_cast основана как раз на том, что this — точно указатель на объект производного класса, у которого нет наследников.
> вы как раз просто приводите к базовому типу, так что можно обойтись вообще без кастов:
>IComInterface1* p = this;
>*ppv = p;
Да, мысль очень правильная. Практически убедить разработчиков приводить так будет проблематично — очень параноидально выглядит эта конструкция.
>само по себе появление IComInterface1 дважды, один раз в __uuidof и второй раз при приведении — это уже плохо, уж слишком просто облажаться при копипасте.
В целом — да, но в данном случае это соседние строки, все же после копипаста надо хотя бы раз прочитать, что получилось, иначе постоянно будут возникать очень глупые ошибки. В исходном примере ошибка появилась во многом из-за того, что объявление класса и соответственно список базовых классов находится очень далеко от реализации QueryInterface() и ошибиться намного легче.
Где же здесь что-либо похожее не вызов InternalQueryInterface()?
В целом, предлагаемое вами решение приемлемо, но имеет существенный недостаток — в нем очень много приведений и манипуляций с указателями, в которых легко запутаться и с которыми компилятор не поможет. Например, отступы от начала объекта, если окажутся неправильными, нужно очень долго анализировать. Видите вы в журнале выдачу, где отступ равен 18, — и что, это признак проблемы или нет?
Можно вместо этого сделать шаблонную функцию, параметризованную запрашиваемым интерфейсом, в ней выводить имя конкретной функции (макрос __FUNCTION__ поможет), проверять, что параметр «идентификатор интерфейса» имеет правильное значение, приводить указатель pv (кстати, в ATL он называется хотя бы pThis) сначала к классу объекта (static_cast), потом — к интерфейсу (еще один static_cast). Снова нужно реализовать значительную часть QueryInterface() и снова так, чтобы минимизировать риск ошибки и максимально упростить отладку.
Да, и этого достаточно, пока вам не понадобится записывать в журнал каждый запрос, а тогда вы, скорее всего, используете COM_INTERFACE_ENTRY_FUNC для перенаправления в свою функцию извлечения интерфейса. Кстати, об этом комментарий выше.
Когда вы пишете все приложение и можете сами контролировать иерархию классов, — да. Но иногда вы вынуждены работать с уже существующей иерархией и вынуждены реализовывать интерфейсы, предопределенные третьей стороной.
Неспроста же там написано «могут возникать», а не «будут возникать».
В целом, справедливо, но точно так же можно ошибиться и при 5 интерфейсах, а 5 интерфейсов для COM-объекта — вполне обычное дело. Скажем, IUnknown+IDispatch+ISupportErrorInfo плюс пара каких-нибудь специфичных для этого объекта — уже 5. А специфичных может быть и больше. Просто не стоит забывать, что есть интерфейсы, которые по тем или иным причинам вынужден реализовывать каждый объект.
>почаще использовать ATL
Совершенно справедливо, ATL — отличная штука, но это не отменяет необходимости думать и правильно пользоваться средствами языка.
Например, может возникнуть потребность выводить в журнал все запросы на интерфейс. Для этого в ATL есть макрос COM_INTERFACE_ENTRY_FUNC, который позволяет перенаправить запрос в функцию с предопределенной сигнатурой, которая может делать что угодно. В этой функции придется заново реализовать значительную часть того, что делает реализация выше, и опять есть риск посадить труднообнаружимую ошибку, если неправильно использовать приведения.
Когда мы сокращаем размер распознаваемого изображения, то мы просто немного упрощаем работу приложениям:) При этом можно выбрать сразу целый блок текста, и если там будет, например, несколько телефонных номеров, то программа найдет и распознает их все.
Программы позволяют копировать данные в буфер обмена, для этого после распознавания надо выбрать пункт Send by SMS. Тогда программа скопирует информацию в буфер обмена, ее можно будет вставить в SMS, а также в любые другие приложения.
>хотите ослеживать всё — вставьте в начало карты блайнд функцию, сохраняйте всё что угодно.
Да, но как узнать, что произошло потом? Чтобы знать точно, нужно выводить также и вызовы конкретных функций запроса. Поэтому blind-функцию надежнее ставить только в самом конце «карты», когда уже точно не было возможности извлечь интерфейс.
В то же время, если вернуть E_NOINTERFACE (или любой код, для которого FAILED дает «истину») из обычной (не blind) функции запроса, код, вызвавший эту функцию запроса, перестанет перебирать карту — расчет на то, что раз фунцию вызвали, она должна извлечь интерфейс успешно, в противном случае что-то пошло не так и дальше пытаться бессмысленно.
>riid содержит тот интерфейс который хотят запросить, проверок на «идентификатор интерфейса» никаких не надо, всё проверено до этого и именно потому попало в нужную вам функцию.
Он должен содержать тот интерфейс, и это стоит проверить, тем более что это совсем нетрудно.
Справедливо, в описанном случае безопасность static_cast основана как раз на том, что this — точно указатель на объект производного класса, у которого нет наследников.
> вы как раз просто приводите к базовому типу, так что можно обойтись вообще без кастов:
>IComInterface1* p = this;
>*ppv = p;
Да, мысль очень правильная. Практически убедить разработчиков приводить так будет проблематично — очень параноидально выглядит эта конструкция.
>само по себе появление IComInterface1 дважды, один раз в __uuidof и второй раз при приведении — это уже плохо, уж слишком просто облажаться при копипасте.
В целом — да, но в данном случае это соседние строки, все же после копипаста надо хотя бы раз прочитать, что получилось, иначе постоянно будут возникать очень глупые ошибки. В исходном примере ошибка появилась во многом из-за того, что объявление класса и соответственно список базовых классов находится очень далеко от реализации QueryInterface() и ошибиться намного легче.
В целом, предлагаемое вами решение приемлемо, но имеет существенный недостаток — в нем очень много приведений и манипуляций с указателями, в которых легко запутаться и с которыми компилятор не поможет. Например, отступы от начала объекта, если окажутся неправильными, нужно очень долго анализировать. Видите вы в журнале выдачу, где отступ равен 18, — и что, это признак проблемы или нет?
Можно вместо этого сделать шаблонную функцию, параметризованную запрашиваемым интерфейсом, в ней выводить имя конкретной функции (макрос __FUNCTION__ поможет), проверять, что параметр «идентификатор интерфейса» имеет правильное значение, приводить указатель pv (кстати, в ATL он называется хотя бы pThis) сначала к классу объекта (static_cast), потом — к интерфейсу (еще один static_cast). Снова нужно реализовать значительную часть QueryInterface() и снова так, чтобы минимизировать риск ошибки и максимально упростить отладку.
Неспроста же там написано «могут возникать», а не «будут возникать».
Совершенно справедливо, ATL — отличная штука, но это не отменяет необходимости думать и правильно пользоваться средствами языка.
Например, может возникнуть потребность выводить в журнал все запросы на интерфейс. Для этого в ATL есть макрос COM_INTERFACE_ENTRY_FUNC, который позволяет перенаправить запрос в функцию с предопределенной сигнатурой, которая может делать что угодно. В этой функции придется заново реализовать значительную часть того, что делает реализация выше, и опять есть риск посадить труднообнаружимую ошибку, если неправильно использовать приведения.