Как проверить разрешение перегрузки, заданное для данного сайта вызова

Как я могу проверить установленное разрешение перегрузки?

У меня есть 4 конкурирующие функции, используемые в нескольких сайтах. В одном домене вызова я ожидаю, что одна функция будет вызвана, а другой - компилятором. Я не знаю, почему/это не тривиально. Чтобы узнать, что происходит, я использую enable_if/disable_if для включения/выключения функций, но это очень медленно/утомительно/раздражает.

Поэтому я хотел бы, чтобы компилятор сказал мне "Почему?". То есть для этого сайта с одним вызовом:

  • все функции, найденные ADL,
  • все функции в разрешении перегрузки,
  • все функции, отклоненные от установленного разрешения перегрузки и почему они были отклонены, и
  • ряды функций в заданном разрешении перегрузки, а также причина их рангов.

Информация о контроле доступа не требуется.

В принципе, я надеюсь разместить маркировку с сайтом #pragma или аналогичным (__builtin...). Но libclang также будет вариантом.

У меня есть доступ к tip-of-trunk clang и gcc, но при необходимости можно установить другие компиляторы/инструменты.

Ответы

Ответ 1

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

Я не играл с определением наборов перегрузок и т.д. Однако ниже приведена простая отправная точка: плагин clang, который печатает функцию, называемую функцией с определенным именем (в настоящее время жестко закодированным как "foo"). Он также печатает найденные перегрузки.

Я компилирую код и запускаю его с помощью команд (очевидно, они хранятся в файле make):

/opt/llvm-debug/bin/clang -I/usr/include/c++/4.2.1 -I/opt/llvm-debug/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-rtti -c -o MacOS/overloads.o overloads.cpp
/opt/llvm-debug/bin/clang -L/opt/llvm-debug/lib -Wl,-undefined,dynamic_lookup -dynamiclib -o MacOS/overloads.dylib MacOS/overloads.o 
/opt/llvm-debug/bin/clang -cc1 -load MacOS/overloads.dylib -plugin overloads -plugin-arg-overloads argument -fexceptions tst.cpp

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

#include <clang/Frontend/FrontendPluginRegistry.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/AST.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Sema/Sema.h>
#include <clang/Sema/Lookup.h>
#include <llvm/Support/raw_ostream.h>
#include <string>

using namespace clang;
using namespace llvm;
typedef clang::CompilerInstance  CI;
typedef clang::DeclGroupRef      DGR;
typedef clang::DiagnosticsEngine DE;

// ----------------------------------------------------------------------------

namespace
{
    struct Consumer: clang::ASTConsumer
    {
        Consumer(CI& c, std::string const& name): c_(&c), name_(name) {}
        bool HandleTopLevelDecl(clang::DeclGroupRef DG);
        CI*         c_;
        std::string name_;
    };
}

// ----------------------------------------------------------------------------

struct Visitor: RecursiveASTVisitor<Visitor>
{
    CI*         c_;
    std::string name_;
    Visitor(CI* c, std::string const& name): c_(c), name_(name) {}

    bool VisitCallExpr(CallExpr* d);
};

bool Visitor::VisitCallExpr(CallExpr* c) {
    FunctionDecl* fun = c->getDirectCallee();
    if (fun && fun->getNameAsString() == this->name_) {
        SourceLocation w(c->getExprLoc());
        DE &de(this->c_->getDiagnostics());
        int id = de.getCustomDiagID(DE::Warning, "function call: %0");
        int info = de.getCustomDiagID(DE::Note, "function called");
        DiagnosticBuilder(de.Report(w, id))
            << fun->getNameAsString()
            ;
        DiagnosticBuilder(de.Report(fun->getLocStart(), info))
            << fun->getNameAsString()
            ;
        Sema& sema = this->c_->getSema();
        LookupResult result(sema, fun->getDeclName(), w, Sema::LookupOrdinaryName);
        DeclContext* context = fun->getDeclContext();
        if (sema.LookupName(result, sema.getScopeForContext(context))) {
            int over = de.getCustomDiagID(DE::Note, "function overload");
            LookupResult::Filter filter = result.makeFilter();
            while (filter.hasNext()) {
                DiagnosticBuilder(de.Report(filter.next()->getLocStart(), over))
                    ;
            }
            filter.done();
        }
    }
    //else {
    //    // I think the callee was a function object or a function pointer
    //}

    return true;
}

void doDecl(Consumer* c, Decl* d) {
    Visitor(c->c_, c->name_).TraverseDecl(d);
}

// ----------------------------------------------------------------------------

bool Consumer::HandleTopLevelDecl(DeclGroupRef DG) {
    std::for_each(DG.begin(), DG.end(),
        std::bind1st(std::ptr_fun(&doDecl), this));
    return true;
}

// ----------------------------------------------------------------------------

namespace
{
    class Plug
        : public clang::PluginASTAction
    {
    protected:
        ASTConsumer*
        CreateASTConsumer(CompilerInstance& c, llvm::StringRef);
        bool ParseArgs(clang::CompilerInstance const&,
                       std::vector<std::string> const&) {
            return true;
        }
    };
}

ASTConsumer*
Plug::CreateASTConsumer(CompilerInstance& c, llvm::StringRef) {
    return new Consumer(c, "foo");
}

static clang::FrontendPluginRegistry::Add<Plug>
    registerPlugin("overloads", "report overloads of a function at a call");

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