c++ - 如何检查给定调用站点的重载解决方案集

标签 c++ debugging gcc clang overload-resolution

如何检查重载分辨率集?

我在多个调用站点中使用了 4 个相互竞争的函数。在一个调用站点中,我期望调用一个函数,但编译器会选择另一个函数。我不知道为什么/这不是微不足道的。为了了解发生了什么,我使用 enable_if/disable_if 来打开/关闭功能,但这真的很慢/乏味/烦人。

所以我希望编译器告诉我“为什么?”。也就是说,对于这个单一的调用站点:

  • ADL 找到的所有函数,
  • 重载决议集中的所有函数,
  • 从重载解决方案集中拒绝的所有函数以及它们被拒绝的原因,以及
  • 重载决议集中函数的排名以及排名的原因。

不需要有关访问控制的信息。

基本上,我希望用 #pragma 或类似的 (__builtin...) 标记调用站点。但 libclang 也是一种选择。

我可以使用 tip-of-trunk clang 和 gcc,但可以在必要时安装其他编译器/工具。

最佳答案

我可以想象写一个 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 对象调查一下为什么它不匹配等可以使代码合理地接近您正在寻找的工具,我想想。

关于c++ - 如何检查给定调用站点的重载解决方案集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20092672/

相关文章:

c++ - 交叉编译器错误 : reference 'm_memoryManager' cannot be decalred 'mutable' [-fpermissive]

C 中的常量 : declaration separate from definition

c++ - 是否有平面未排序的 map /集合实现?

c++ - VS10/12 中的 Matlab Shared C/C++ lib 抛出很多异常

c++ - 现代 C++ 的 JSON _json 语法

php - VSCode调试器未在Docker上为Laravel运行

windows - 调试(Win): DIA versus DBGHELP

c++ - 终止 PPL 线程池中的线程

java - 空指针异常和 getMapAsync 错误

c - 如何在C中包含头文件