Реализуем ключевое слово для базового super-класса в GCC

  !   Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам)

В некоторых языках базовый класс обозначается super, а в С++ с этим проблемы. Мы использовали микрософтовский непортабельный __super, а с переходом на GCC его лишились. Пришлось сделать свой.

Не годные обходные пути

Все рассуждения упертых формалистов о неоднозначности этого ключевого слова в языке с множественным наследованием классов - нонсенс на практике. Мы нигде не наследуем несколько прикладных классов сливая их в одну гнусную и глючную помойку, а если понадобятся системные вторичные классы или "интерфейсы" (все вещи спорной надобности) то все равно может быть только один основной смысловой базовый класс.

Непосредственное указание каждый раз имени базового класса для вызова его методов создает вреднейшее дублирование связей. Хотя Cx предотвращает ошибочный пропуск виртуальных функций базового класса) в некоторых ситуациях это все равно приведет к глупым ошибкам.

Дубовые хаки с typedef запрещены в PrD-проектах. (И они не решают реальную проблему дублирования).

Истинная причина наездов на __super только в нежелании признать то, что MS породила что то полезное, на практике повышающее надежность кода.

Добавление ключевого слова к Cx

На примере данной проблемы рассмотрим как добавлять новые ключевые слова.

Находим, что CSuperClassSpec отвечает за трансляцию "__super". Но он ничего не делал полезного для MSVC

    class CSuperClassSpec : public CBuiltinName
    {
    public:
        GENERIC(CSuperClassSpec);
 
    protected:
        //virtual void OnTransform() {}
        virtual const char* OnGetDefaultTextRepresentation()
            { return "__super"; }
    };
MapKeyword(new CSuperClassSpec); // __super

Значит наследуем его и переопределяем логику его регенерации.

// CAlterGenericSuper - '__super' support for GCC
 
class CAlterGenericSuper : public CSuperClassSpec
{
public:
    GENERIC(CAlterGenericSuper);
    CAlterGenericSuper();
 
protected:
    //virtual CToken* OnTryExtend();
    //virtual void OnTransform();
    virtual void OnRegenerate(CWriter* pWriter);
    //virtual const char* OnGetDefaultTextRepresentation();
    //virtual bool OnIsSemicolonEnabledAtExpressionEnd();
    //virtual bool OnIsAlwaysLastInExpressionList();
 
private:
};

Генератор кода очевидно должен находить базовый класс для того класса к которому относится метод в котором это ключевое слово упомянуто.

Так как это все нужно в первую очередь для generic-классов, то вместо поиска по дереву деклараций проще реализовать это только для классов, метаинформация о которых уже собрана.

Полезный метод IsGenericMethodImplHeader() не только проверяет, отностся ли текущий метод к object-классу но и возвращает удобные ссылки на метаданные о декларациях этого класса:

void CAlterGenericSuper::OnRegenerate(CWriter* pWriter)
{
    // Detect Generic method we are found in
    nullable<CQualifiedName*> npMethodQualifiedName;
    str sRootClassName;
    nullable<CAlterGenericClass*> npAlterGenericClass;
    nullable<CAlterMember*> npAlterMember;
    GetTransformContext()->
        IsGenericMethodImplHeader(
            FindParentFunctionBodyDeclaration(),
            out npMethodQualifiedName,
            out sRootClassName,
            out npAlterGenericClass,
            out npAlterMember);

Теперь если класс есть generic то

    // It is easy to find the base class for a Generic class
    //   so we only support __super for generics
    if not_null(
        CAlterGenericClass* pAlterGenericClass =
            npAlterGenericClass)
    {
        // it should have a Genric base
        if not_null(
            CAlterGenericClass* pBase =
                pAlterGenericClass->GetBaseClassHeader())
        {

Здесь CAlterGenericClass* pBase ссылается на всю информацию из базового класса, откуда мы можем получить его имя, и вывести его заместо слова __super

            // we dont support namespaces (for generics especially),
            //   just get a simple name
            str sSuperClass =
                pBase->GetClassQualifiedName()->GetRootUnqualifiedNameStr();
 
            // write the result
            pWriter->
                WriteTokenStr(
                    this,
                    sSuperClass,
                    E_LineNumbering_SyncWithToken);
 
            return;
        }
    }

В остальных случаях вызываем обычную логику, рассчивывая не знаю на что. Может быть на Windows-only ветвление, или LL-класс в котором __super определено через typedef.

    // will cause an error for GCC if a macro is not defined
    __super::OnRegenerate(pWriter);
}

После чего регистрируем наше ключевое слово как замену стандартному:

    GetKeywordMapping()->
        MapKeyword(
            new CAlterGenericSuper,
            CKeywordMapping::E_AddKeyword_RemapExisting);

Похожим образом добавляются любые ключевые слова. Нужно создать класс, унаследованный от наиболее подходящего базового токена или создать совой оригинальный. Зарегистрировав его в трансляторе перехватиnь функции его парсинга, разбирая следующие за ним параметры (OnTryExtend()), или трансформации (OnTransform()) для модификации дерева окружающих токенов или генерации выходного С++-текста для него (OnRegenerate()). Получение дополнительной информации из окружающего кода и ее обработка делается на основе существующих примеров расширений Cx.