воскресенье, 6 января 2013 г.

clang: ещё один посетитель

В clang-c есть функция clang_visitChildren, предназначенная для частичного обхода AST. Она начинает с узла, переданного первым параметром, и вызывает функцию (второй параметр) для каждого дочернего узла.

Функция возвращает одно из трёх значений из enum CXChildVisitResult. В зависимости от этого значения clang прерывает обход, преходит к следующему потомку либо обходит ещё и потомков потомка.

Вот такой код выберет самое раннее объявление класса, даже если оно вложено в пространство имён:

static CXChildVisitResult childVisitor(CXCursor cursor,
                                       CXCursor parent,
                                       CXClientData client_data)
{
    if (cursor.kind == CXCursor_Namespace)
        return CXChildVisit_Recurse;
    if (cursor.kind == CXCursor_ClassDecl)
{
        CXCursor *placeholder = reinterpret_cast<CXCursor*>(client_data);
        *placeholder = self;
        return CXChildVisit_Break;
    }
    return CXChildVisit_Continue;
}

А теперь самая мякотка. Превратим вызов функции с тремя параметрами в вызов лямбды без параметров, вместо этого cursor и parent будут ложиться в контекст, а сам класс будет иметь методы pushContext и popContext:

typedef std::function<CXChildVisitResult()> Visitor;
typedef std::function<CXChildVisitResult(const CXCursor &self, const CXCursor &parent)> VisitorWrapper;

static CXChildVisitResult childVisitor(CXCursor cursor,
                                       CXCursor parent,
                                       CXClientData client_data)
{
    VisitorWrapper &callback = *reinterpret_cast<VisitorWrapper *>(client_data);
    return callback(cursor, parent);
}

void TUConsumer::visitCursorChildren(CXCursor cursor, Visitor callback)
{
    VisitorWrapper wrapper = [&callback, this](const CXCursor &self, const CXCursor &parent)
    {
        pushContext(self, parent);
        auto ret = callback();
        popContext();
        return ret;
    };

    CXClientData data = reinterpret_cast<CXClientData>(&wrapper);
    clang_visitChildren(cursor, childVisitor, data);
}

Комментариев нет:

Отправить комментарий