2013-11-20 23 views
14

如何檢查重載分辨率設置?如何檢查爲給定呼叫站點設置的過載分辨率

我有4個競爭函數用於多個呼叫站點。在一個調用站點中,我希望調用一個函數,但編譯器會提取另一個函數。我不知道爲什麼/這不是微不足道的。要了解正在發生的事情,我正在使用enable_if/disable_if開啓/關閉功能,但這實在太慢/乏味/煩人。

所以我想讓編譯器告訴我「爲什麼?」。也就是說,這個單一調用點:由ADL發現

  • 的所有功能,
  • 在重載決議集
  • 從重載決議拒絕了所有功能的所有功能,設置和爲什麼會被拒絕,並
  • 重載決議集中函數的等級以及其排名的原因。

有關訪問控制的信息不是必需的。

基本上我希望用#pragma或類似的標記呼叫站點(__builtin ...)。但是libclang也是一個選項。

我可以使用tip-of-trunk clang和gcc,但可以根據需要安裝其他編譯器/工具。

+2

稍微偏離主題,但是當你不能穩定地預測將從重載的堆中調用什麼特定的函數時,這是一個糟糕的設計。你的重載版本應該有明顯的區別。 – Mikhail

+1

@Mikhail我在預測正確的過載。它沒有被拾取,因爲它在一個const對象中調用了一個非const成員函數(我忘記了添加一個const超載...我的不好)。我知道,不要混用通用引用+重載......除非它是正確的事情。編譯器可以讓它更容易處理。 – gnzlbg

回答

5

我可以想象,編寫一個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 

鐺的使用是建立與調試信息的版本:否則它不」 t似乎找到了一個調試符號。我應該找出如何直接構建一個工具,而不是從叮噹中運行。

#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"); 

該代碼並不漂亮,並不是真的在做你正在尋找的東西。但是,將函數聲明格式化得更好一點,可能需要調查一下對象爲什麼它不匹配等等,我認爲可以使代碼與您正在尋找的工具相當接近。