Поделиться через


Основные понятия модели данных C++ отладчика

В этом разделе описываются понятия в модели данных C++ отладчика.

Основные понятия в модели данных

Искусственные объекты в модели данных фактически являются двумя вещами:

  • Словарь кортежей ключей, значений и метаданных.
  • Набор концепций (интерфейсов), поддерживаемых моделью данных. Основные понятия — это интерфейсы, которые клиент (в отличие от модели данных) реализует для предоставления заданного набора семантического поведения. В настоящее время здесь перечислены поддерживаемые наборы понятий.
Интерфейс концепции Description
IDataModelConcept Концепция является родительской моделью. Если эта модель автоматически присоединяется к собственному типу через сигнатуру зарегистрированного типа, метод InitializeObject будет автоматически вызываться при каждом создании экземпляра нового объекта такого типа.
IStringDisplayableConcept Объект можно преобразовать в строку для отображения.
IIterableConcept Объект является контейнером и может быть итерирован.
IIndexableConcept Объект является контейнером и может быть индексирован (доступ через случайный доступ) в одном или нескольких измерениях.
IPreferredRuntimeTypeConcept Объект понимает больше о типах, производных от него, чем базовая система типов может предоставлять и обрабатывать собственные преобразования из статического в тип среды выполнения.
IDynamicKeyProviderConcept Объект является динамическим поставщиком ключей и хочет взять на себя все ключевые запросы из основной модели данных. Этот интерфейс обычно используется в качестве моста к динамическим языкам, таким как JavaScript.
IDynamicConceptProviderConcept Объект является динамическим поставщиком концепций и хочет взять на себя все основные запросы из основной модели данных. Этот интерфейс обычно используется в качестве моста к динамическим языкам, таким как JavaScript.

Концепция модели данных: IDataModelConcept

Любой объект модели, присоединенный к другому объекту модели в качестве родительской модели, должен напрямую поддерживать концепцию модели данных. Концепция модели данных требует поддержки интерфейса, определяемого IDataModelConcept следующим образом.

DECLARE_INTERFACE_(IDataModelConcept, IUnknown)
{
    STDMETHOD(InitializeObject)(_In_ IModelObject* modelObject, _In_opt_ IDebugHostTypeSignature* matchingTypeSignature, _In_opt_ IDebugHostSymbolEnumerator* wildcardMatches) PURE;
    STDMETHOD(GetName)(_Out_ BSTR* modelName) PURE;
}

InitializeObject

Модель данных может быть зарегистрирована в качестве канонического визуализатора или в качестве расширения для заданного собственного типа с помощью методов RegisterModelForTypeSignature или RegisterExtensionForTypeSignature. При регистрации модели с помощью любого из этих методов модель данных автоматически присоединяется как родительская модель к любому собственному объекту, тип которого соответствует сигнатуре, переданной в регистрации. На момент автоматического выполнения этого вложения метод InitializeObject вызывается в модели данных. Он передает объект экземпляра, сигнатуру типа, которая вызвала вложение, и перечислитель, который создает экземпляры типов (в линейном порядке), которые соответствовали любым диким карта в сигнатуре типа. Реализация модели данных может использовать этот вызов метода для инициализации всех необходимых кэшей.

GetName

Если данная модель данных зарегистрирована под именем по умолчанию с помощью метода RegisterNamedModel, интерфейс IDataModelConcept зарегистрированной модели данных должен вернуть это имя из этого метода. Обратите внимание, что модель должна быть зарегистрирована под несколькими именами (по умолчанию или лучшему из них должна быть возвращена здесь). Модель может быть полностью неназванной (если она не зарегистрирована под именем). В таких случаях метод GetName должен возвращать E_NOTIMPL.

Концепция отображаемой строки: IStringDisplayableConcept

Объект, который хочет предоставить преобразование строк для отображения, может реализовать отображаемую строку концепцию через реализацию интерфейса IStringDisplayableConcept. Интерфейс определяется следующим образом:

DECLARE_INTERFACE_(IStringDisplayableConcept, IUnknown)
{
    STDMETHOD(ToDisplayString)(_In_ IModelObject* contextObject, _In_opt_ IKeyStore* metadata, _Out_ BSTR* displayString) PURE;
}

ToDisplayString

Метод ToDisplayString вызывается всякий раз, когда клиент хочет преобразовать объект в строку для отображения (в консоль, в пользовательском интерфейсе и т. д.). Такое преобразование строк не должно использоваться для создания дополнительных программных манипуляций. Само преобразование строки может быть глубоко зависит от метаданных, передаваемых вызову. Преобразование строк должно выполнять каждую попытку соблюдать ключи PreferredRadix и PreferredFormat.

Итерируемая концепция: IIterableConcept и IModelIterator

Объект, который является контейнером других объектов и хочет выразить возможность итерации над этими содержащимися объектами, может поддерживать итерацию концепции путем реализации интерфейсов IIterableConcept и IModelIterator. Существует очень важная связь между поддержкой итерируемой концепции и поддержкой индексируемой концепции. Объект, поддерживающий случайный доступ к содержащимся объектам, может поддерживать индексируемую концепцию в дополнение к итерируемой концепции. В этом случае итерированные элементы также должны создать индекс по умолчанию, который при передаче в индексируемую концепцию относится к тому же объекту. Неспособность удовлетворить эту инвариантную ошибку приведет к неопределенному поведению в узле отладки.

IIterableConcept определяется следующим образом:

DECLARE_INTERFACE_(IIterableConcept, IUnknown)
{
    STDMETHOD(GetDefaultIndexDimensionality)(_In_ IModelObject* contextObject, _Out_ ULONG64* dimensionality) PURE;
    STDMETHOD(GetIterator)(_In_ IModelObject* contextObject, _Out_ IModelIterator** iterator) PURE;
}

Концепция IModelIterator определена следующим образом:

DECLARE_INTERFACE_(IModelIterator, IUnknown)
{
   STDMETHOD(Reset)() PURE;
   STDMETHOD(GetNext)(_COM_Errorptr_ IModelObject** object, _In_ ULONG64 dimensions, _Out_writes_opt_(dimensions) IModelObject** indexers, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
}

IIterableConcept GetDefaultIndexDimensionality

Метод GetDefaultIndexDimensionality возвращает количество измерений для индекса по умолчанию. Если объект не индексируется, этот метод должен возвращать значение 0 и успешно (S_OK). Любой объект, возвращающий ненулевое значение из этого метода, объявляет поддержку контракта протокола, который указывает:

  • Объект поддерживает индексируемую концепцию с помощью поддержки IIndexableConcept
  • Метод GetNext iModelIterator, возвращаемый из метода GetIterator концепции итерации, возвращает уникальный индекс по умолчанию для каждого создаваемого элемента. Такой индекс будет иметь количество измерений, как указано здесь.
  • Передача индексов, возвращаемых методом GetNext метода IModelIterator методу GetAt для индексируемой концепции (IIndexableConcept), будет ссылаться на тот же объект, который был создан GetNext. Возвращается то же значение.

GetIterableConcept в IIterator

Метод GetIterator в итерируемой концепции возвращает интерфейс итератора, который можно использовать для итерации объекта. Возвращаемый итератор должен помнить объект контекста, переданный методу GetIterator. Он не будет передан в методы самого итератора.

Сброс IModelIterator

Метод Reset для итератора, возвращаемого из итерируемой концепции, восстановит положение итератора в том месте, где он был создан при первом создании итератора (до первого элемента). Хотя настоятельно рекомендуется поддерживать метод Сброса итератора, это не обязательно. Итератор может быть эквивалентом итератора ввода C++ итератором итератора только одного прохода пересылки. В таком случае метод сброса может завершиться ошибкой E_NOTIMPL.

GetNext iModelIterator

Метод GetNext перемещает итератор вперед и извлекает следующий итерированный элемент. Если объект индексируется в дополнение к итерации, и это указывается аргументом GetDefaultIndexDimensionality, возвращающим ненулевое значение, этот метод может при необходимости возвращать индексы по умолчанию, чтобы вернуться к созданному значению от индексатора. Обратите внимание, что вызывающий объект может передать 0/nullptr и не получить индексы. Он считается незаконным для вызывающего объекта запрашивать частичные индексы (например, меньше числа, созданного GetDefaultIndexDimensionality).

Если итератор успешно переместился вперед, но произошла ошибка при чтении значения итерированного элемента, метод может вернуть ошибку И заполнить "объект" объектом ошибки. В конце итерации содержащихся элементов итератор вернет E_BOUNDS из метода GetNext. Любой последующий вызов (если не был промежуточный вызов сброса) также вернет E_BOUNDS.

Концепция индексации: IIndexableConcept

Объект, который хочет предоставить случайный доступ к набору содержимого, может поддерживать индексируемую концепцию через поддержку интерфейса IIndexableConcept. Большинство объектов, которые являются индексируемыми, будут итерируемыми, а также через поддержку итерируемой концепции. Однако это не обязательно. При поддержке существует важная связь между итератором и индексатором. Итератор должен поддерживать GetDefaultIndexDimensionality, возвращать ненулевое значение из этого метода и поддерживать контракт, документирующийся там. Интерфейс концепции индексатора определяется следующим образом:

DECLARE_INTERFACE_(IIndexableConcept, IUnknown)
{
    STDMETHOD(GetDimensionality)(_In_ IModelObject* contextObject, _Out_ ULONG64* dimensionality) PURE;
    STDMETHOD(GetAt)(_In_ IModelObject* contextObject, _In_ ULONG64 indexerCount, _In_reads_(indexerCount) IModelObject** indexers, _COM_Errorptr_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(SetAt)(_In_ IModelObject* contextObject, _In_ ULONG64 indexerCount, _In_reads_(indexerCount) IModelObject** indexers, _In_ IModelObject *value) PURE;
}

Ниже показан пример использования индексатора (и его взаимодействия с итератором). В этом примере выполняется итерация содержимого индексированного контейнера и использует индексатор для возврата к значению, которое только что было возвращено. Хотя эта операция функционально бесполезна как написанная, она демонстрирует, как эти интерфейсы взаимодействуют. Обратите внимание, что приведенный ниже пример не связан с сбоем выделения памяти. Предполагается, что создание нового (что может быть плохим предположением в зависимости от среды, в которой существует код- методы COM модели данных не могут иметь исключения C++):

ComPtr<IModelObject> spObject;

//
// Assume we have gotten some object in spObject that is iterable (e.g.: an object which represents a std::vector<SOMESTRUCT>)
//
ComPtr<IIterableConcept> spIterable;
ComPtr<IIndexableConcept> spIndexer;
if (SUCCEEDED(spObject->GetConcept(__uuidof(IIterableConcept), &spIterable, nullptr)) &&
    SUCCEEDED(spObject->GetConcept(__uuidof(IIndexableConcept), &spIndexable, nullptr)))
{
    ComPtr<IModelIterator> spIterator;

    //
    // Determine how many dimensions the default indexer is and allocate the requisite buffer.
    //
    ULONG64 dimensions;
    if (SUCCEEDED(spIterable->GetDefaultIndexDimensionality(spObject.Get(), &dimensions)) && dimensions > 0 &&
        SUCCEEDED(spIterable->GetIterator(spObject.Get(), &spIterator)))
    {
        std::unique_ptr<ComPtr<IModelObject>[]> spIndexers(new ComPtr<IModelObject>[dimensions]);

        //
        // We have an iterator.  Error codes have semantic meaning here.  E_BOUNDS indicates the end of iteration.  E_ABORT indicates that
        // the debugger host or application is trying to abort whatever operation is occurring.  Anything else indicates
        // some other error (e.g.: memory read failure) where the iterator MIGHT still produce values.
        //
        for(;;)
        {
            ComPtr<IModelObject> spContainedStruct;
            ComPtr<IKeyStore> spContainedMetadata;

            //
            // When we fetch the value from the iterator, it will pass back the default indices.
            //
            HRESULT hr = spIterable->GetNext(&spContainedStruct, dimensions, reinterpret_cast<IModelObject **>(spIndexers.get()), &spContainedMetadata);
            if (hr == E_BOUNDS || hr == E_ABORT)
            {
                break;
            }

            if (FAILED(hr))
            {
                //
                // Decide how to deal with failure to fetch an element.  Note that spContainedStruct *MAY* contain an error object
                // which has detailed information about why the failure occurred (e.g.: failure to read memory at address X).
                //
            }

            //
            // Use the indexer to get back to the same value.  We already have them, so there isn't much functional point to this.  It simply
            // highlights the interplay between iterator and indexer.
            //
            ComPtr<IModelObject> spIndexedStruct;
            ComPtr<IKeyStore> spIndexedMetadata;

            if (SUCCEEDED(spIndexer->GetAt(spObject.Get(), dimensions, reinterpret_cast<IModelObject **>(spIndexers.get()), &spIndexedStruct, &spIndexedMetadata)))
            {
                //
                // spContainedStruct and spIndexedStruct refer to the same object.  They may not have interface equality.
                // spContainedMetadata and spIndexedMetadata refer to the same metadata store with the same contents.  They may not have interface equality.
                //
            }
        }
    }
}

GetDimensionality

Метод GetDimensionality возвращает количество измерений, в которых индексируется объект. Обратите внимание, что если объект является итерируемым и индексируемым, реализация GetDefaultIndexDimensionality должна согласиться с реализацией GetDimensionality в отношении количества измерений индексатора.

Getat

Метод GetAt извлекает значение по определенному N-мерному индексу из индексированного объекта. Индексатор N-измерений, где N — это значение, возвращаемое из GetDimensionality, должно поддерживаться. Обратите внимание, что объект может индексироваться в разных доменах различными типами (например, индексируемым по порядковым номерам и строкам). Если индекс выходит из диапазона (или не удается получить доступ), метод вернет сбой; Однако в таких случаях выходной объект по-прежнему может быть установлен на объект ошибки.

Setat

Метод SetAt пытается задать значение по определенному N-мерному индексу из индексированного объекта. Индексатор N-измерений, где N — это значение, возвращаемое из GetDimensionality, должно поддерживаться. Обратите внимание, что объект может индексироваться в разных доменах различными типами (например, индексируемым по порядковым номерам и строкам). Некоторые индексаторы доступны только для чтения. В таких случаях E_NOTIMPL будут возвращены из любого вызова метода SetAt.

Концепция типа предпочтительного типа среды выполнения: IPreferredRuntimeTypeConcept

Узел отладки можно запросить, чтобы попытаться определить реальный тип среды выполнения объекта из статического типа, найденного в символьной информации. Это преобразование может основываться на полностью точных данных (например, C++ RTTI) или на основе сильных эвристики, таких как форма любых таблиц виртуальных функций в объекте. Однако некоторые объекты нельзя преобразовать из статического в тип среды выполнения, так как они не помещаются в эвристики узла отладки (например, они не имеют таблиц RTTI или виртуальных функций). В таких случаях модель данных для объекта может переопределить поведение по умолчанию и объявить, что он знает больше о типе среды выполнения объекта, чем узел отладки способен понять. Это делается с помощью предпочтительной концепции типа среды выполнения и поддержки интерфейса IPreferredRuntimeTypeConcept.

Интерфейс IPreferredRuntimeTypeConcept объявлен следующим образом:

DECLARE_INTERFACE_(IPreferredRuntimeTypeConcept, IUnknown)
{
    STDMETHOD(CastToPreferredRuntimeType)(_In_ IModelObject* contextObject, _COM_Errorptr_ IModelObject** object) PURE;
}

CastToPreferredRuntimeType

Метод CastToPreferredRuntimeType вызывается всякий раз, когда клиент пытается преобразовать из экземпляра статического типа в тип среды выполнения этого экземпляра. Если объект, указанный в вопросе, поддерживает (через одну из присоединенных родительских моделей) предпочтительную концепцию типа среды выполнения, этот метод будет вызван для выполнения преобразования. Этот метод может возвращать исходный объект (преобразование отсутствует или не удалось проанализировать), возвратить новый экземпляр типа среды выполнения, завершиться сбоем из-за неемантических причин (например, вне памяти) или вернуть E_NOT_SET. Код ошибки E_NOT_SET — это очень специальный код ошибки, указывающий на модель данных, которую реализация не хочет переопределить поведение по умолчанию, и что модель данных должна вернуться к тому, что анализ выполняется узлом отладки (например, анализ RTTI, проверка формы таблиц виртуальных функций, и т. д.)

Основные понятия динамического поставщика: IDynamicKeyProviderConcept и IDynamicConceptProviderConcept

Хотя сама модель данных обычно обрабатывает управление ключами и концепциями для объектов, существует время, когда это понятие меньше идеала. В частности, когда клиент хочет создать мост между моделью данных и что-то другое, которое действительно динамически (например, JavaScript), может быть полезно взять на себя управление ключами и концепциями из реализации в модели данных. Поскольку базовая модель данных является одной и единственной реализацией IModelObject, это делается с помощью сочетания двух концепций: концепции поставщика динамических ключей и концепции поставщика динамических концепций. Хотя это было бы типично для реализации обоих или ни другого, нет никаких требований для такого.

Если оба варианта реализованы, необходимо добавить концепцию поставщика динамических ключей перед концепцией динамического поставщика концепций. Оба этих понятия являются особыми. Они эффективно переворачивают переключатель для объекта, изменяя его с "статически управляемым" на "динамически управляемый". Эти понятия можно задать только в том случае, если в объекте нет ключей и концепций, управляемых моделью данных. После добавления этих понятий в объект действие этого неизменяемо.

Существует дополнительная семантическая разница в расширяемости между IModelObject, который является динамическим поставщиком концепций и не является. Эти понятия предназначены для того, чтобы клиенты могли создавать мосты между моделью данных и динамическими языковыми системами, такими как JavaScript. Модель данных имеет концепцию расширяемости, которая несколько отличается от систем, таких как JavaScript, в том, что существует дерево родительских моделей, а не линейная цепочка, как цепочка прототипов JavaScript. Чтобы обеспечить лучшую связь с такими системами, IModelObject, который является динамическим поставщиком концепции, имеет один родительский элемент модели данных. Этот родительский элемент модели данных является обычным объектом IModelObject, который может иметь произвольное количество родительских моделей, как обычно для модели данных. Все запросы к поставщику динамической концепции для добавления или удаления родителей автоматически перенаправляются в один родительский элемент. С точки зрения аутсайдера, он выглядит так, как будто динамический поставщик концепций имеет обычную цепочку стилей дерева родительских моделей. Реализация концепции динамического поставщика концепции является единственным объектом (за пределами основной модели данных), который учитывает промежуточный родительский элемент. Один родительский элемент может быть связан с динамической языковой системой, чтобы обеспечить мост (например, помещенный в цепочку прототипов JavaScript).

Концепция поставщика динамических ключей определяется следующим образом:

DECLARE_INTERFACE_(IDynamicKeyProviderConcept, IUnknown)
{
    STDMETHOD(GetKey)(_In_ IModelObject *contextObject, _In_ PCWSTR key, _COM_Outptr_opt_result_maybenull_ IModelObject** keyValue, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata, _Out_opt_ bool *hasKey) PURE;
    STDMETHOD(SetKey)(_In_ IModelObject *contextObject, _In_ PCWSTR key, _In_ IModelObject *keyValue, _In_ IKeyStore *metadata) PURE;
    STDMETHOD(EnumerateKeys)(_In_ IModelObject *contextObject, _COM_Outptr_ IKeyEnumerator **ppEnumerator) PURE;
}

Концепция поставщика динамических концепций определяется следующим образом:

DECLARE_INTERFACE_(IDynamicConceptProviderConcept, IUnknown)
{
    STDMETHOD(GetConcept)(_In_ IModelObject *contextObject, _In_ REFIID conceptId, _COM_Outptr_result_maybenull_ IUnknown **conceptInterface, _COM_Outptr_opt_result_maybenull_ IKeyStore **conceptMetadata, _Out_ bool *hasConcept) PURE;
    STDMETHOD(SetConcept)(_In_ IModelObject *contextObject, _In_ REFIID conceptId, _In_ IUnknown *conceptInterface, _In_opt_ IKeyStore *conceptMetadata) PURE;
    STDMETHOD(NotifyParent)(_In_ IModelObject *parentModel) PURE;
    STDMETHOD(NotifyParentChange)(_In_ IModelObject *parentModel) PURE;
    STDMETHOD(NotifyDestruct)() PURE;
}

GetKey iDynamicKeyProviderConcept

Метод GetKey в динамическом поставщике ключей является в значительной степени переопределением метода GetKey в IModelObject. Ожидается, что поставщик динамических ключей возвращает значение ключа и все метаданные, связанные с этим ключом. В случае отсутствия ключа (но нет другой ошибки), поставщик должен вернуть значение false в параметре hasKey и успешно выполнить S_OK. Сбой этого вызова считается ошибкой получения ключа и явно остановит поиск ключа через цепочку родительской модели. При возврате false в hasKey и успешном выполнении будет продолжаться поиск ключа. Обратите внимание, что для GetKey он является совершенно законным, чтобы вернуть прямоугольный метод доступа к свойствам в качестве ключа. Это было бы семантически идентично методу GetKey в IModelObject, возвращая метод доступа к свойствам.

Набор ключей IDynamicKeyProviderConcept

Метод SetKey для поставщика динамических ключей фактически переопределяет метод SetKey в IModelObject. Этот параметр задает ключ в динамическом поставщике. Это фактически создание нового свойства в поставщике. Обратите внимание, что поставщик, который не поддерживает какие-либо понятия о создании свойств expando, должен возвращать E_NOTIMPL здесь.

Перечисление IDynamicKeyProviderConcept

Метод EnumerateKeys в динамическом поставщике ключей фактически переопределяет метод EnumerateKeys в IModelObject. Это перечисляет все ключи в динамическом поставщике. Возвращаемый перечислитель имеет несколько ограничений, которые должны соблюдаться реализацией:

  • Он должен вести себя как вызов ПеречислитьKeys, а не ПеречислитьKeyValues или EnumerateKeyReferences. Он должен возвращать значения ключей, не разрешающие какие-либо базовые методы доступа к свойствам (если такая концепция существует в поставщике).
  • С точки зрения одного поставщика динамических ключей, это недопустимо для перечисления нескольких ключей одного и того же имени, которые являются физически отдельными ключами. Это может произойти в разных поставщиках, подключенных через цепочку родительской модели, но это не может произойти с точки зрения одного поставщика.

GetConceptProvider iDynamicConcept

Метод GetConcept для динамического поставщика концепций является фактически переопределением метода GetConcept в IModelObject. Поставщик динамической концепции должен возвращать интерфейс для запрашиваемой концепции, если она существует, а также любые метаданные, связанные с этой концепцией. Если концепция не существует в поставщике, это должно быть указано с помощью ложного значения, возвращаемого в аргументе hasConcept и успешном возвращении. Сбой этого метода — это неспособность получить концепцию и явно остановить поиск концепции. Возвращая значение false для hasConcept, и успешный код продолжит поиск концепции через дерево родительской модели.

Набор SetConcept в IDynamicConceptProvider

Метод SetConcept в динамическом поставщике концепций является фактически переопределением метода SetConcept в IModelObject. Динамический поставщик назначит концепцию. Это может сделать объект итерируемым, индексируемым, строковым преобразуемым и т. д. Обратите внимание, что поставщик, который не разрешает создание концепций на нем, должен возвращать E_NOPTIMPL здесь.

IDynamicConceptProviderConcept's NotifyParent

Вызов NotifyParent для динамического поставщика концепций используется основной моделью данных для информирования динамического поставщика одной родительской модели, которая создается для преодоления парадигмы "нескольких родительских моделей" модели данных на более динамические языки. Любая манипуляция с одной родительской моделью приведет к дальнейшим уведомлениям динамическому поставщику. Обратите внимание, что этот обратный вызов выполняется сразу после назначения концепции поставщика динамических концепций.

IDynamicConceptProviderConcept в NotifyParentChange

Метод NotifyParent в динамическом поставщике концепций является обратным вызовом, сделанным основной моделью данных при выполнении статической манипуляции с одной родительской моделью объекта. Для любой добавленной родительской модели этот метод будет вызываться при первом добавлении родительской модели, а второй раз при удалении родительской модели.

IDynamicConceptProviderConcept's NotifyDestruct

Метод NotifyDestruct для динамического поставщика концепций — это обратный вызов, сделанный основной моделью данных в начале уничтожения объекта, который является динамическим поставщиком концепции. Он предоставляет дополнительные возможности очистки для клиентов, которые требуют его.

--

См. также

Этот раздел является частью серии, в которой описываются интерфейсы, доступные на C++, как использовать их для создания расширения отладчика на основе C++ и использования других конструкций модели данных (например, JavaScript или NatVis) из расширения модели данных C++.

Обзор модели данных отладчика C++

Интерфейсы C++ модели данных отладчика

Объекты C++ модели данных отладчика

Дополнительные интерфейсы модели данных отладчика C++

Основные понятия модели данных C++ отладчика

Скрипты модели данных отладчика C++