2012-06-18 24 views
12

想象一下,你有克++,需要多個模板專用連接器警告/錯誤

#include <iostream> 

template<typename T> struct A{ 
    int magic; 
    A():magic(1234){} 
    void f(){std::cout<<"default f"<<magic<<std::endl;} 
}; 


void f(A<int>* a); 

則函數f是「a.cpp」

#include "a.h" 
void f(A<int>* a){ 
    a->f(); 
} 

最後定義的文件啊,「 main.cpp中」專業的模板,然後使用˚F

#include "a.h" 
template<> struct A<int>{ 
}; 

int main(){ 
    A<int> a; 
    f(&a); 

} 

顯然編譯器對AO非專業版本和main.o的專用版本,即碰巧有兩種不同的實現A. 執行時,f只能打印垃圾/段錯誤,因爲傳遞的對象與預期的結構不同。

有沒有辦法讓鏈接器警告有兩個版本的A?

+0

這是一個相當難解決的問題,這就是爲什麼沒有強制性的診斷。 – Flexo

+0

HTH:刪除問題中的所有模板 - 您將獲得相同的結果。在struct A的a.h中只保留前向聲明;並將其完整版本移至a.cpp。 main.cpp中 - 只刪除模板... – PiotrNycz

回答

3

原因金不警告這是金僅檢測符號錯配(同一符號被在不兼容的方式將多個目標文件中定義的),並且在該示例沒有這樣的不匹配。

運行Valgrind的下例子併產生這個錯誤,但:

valgrind --track-origins=yes ./a.out 

==11004== Memcheck, a memory error detector 
==11004== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==11004== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info 
==11004== Command: ./a.out 
==11004== 
==11004== Conditional jump or move depends on uninitialised value(s) 
==11004== at 0x40B6D24: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16) 
==11004== by 0x40B703C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16) 
==11004== by 0x40C26DE: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib64/libstdc++.so.6.0.16) 
==11004== by 0x40094F: A<int>::f() (a.h:6) 
==11004== by 0x4008CB: f(A<int>*) (a.cpp:3) 
==11004== by 0x400977: main (main.cpp:7) 
==11004== Uninitialised value was created by a stack allocation 
==11004== at 0x400964: main (main.cpp:5) 

你應該從Address Sanitizer得到更好的報告:

更新:

的一點是,我想在鏈接時檢測錯誤,而不是在執行期間。

我明白你的意思,但目前不可能對任何編譯器(沒有對其他翻譯單位的信息)或連接件(不具有對所涉及的類型信息)要提醒你這。

現在,對於一個調試版本,鏈接器理論上可以這樣做,如果對於每個函數,它還會比較參數類型的調試信息。我建議在bugzilla提交黃金的功能請求。

+1

謝謝。我實際上發現這種情況發生在使用valgrind的代碼中,然後我得出了這個玩具的例子。重點是我想在連接時檢測錯誤,而不是在執行過程中。 –

1

金連接器可能會給與--detect-ODR-侵犯

它可以通過比較每個模板定義的文件和行號,如果他們不都是相同的警告的警告。

+0

謝謝你,但 LD -version GNU金(GNU Binutils的Ubuntu的2.21.53.20110810)1.11 和 G ++ -o測試的main.cpp a.cpp輪候冊, --detect-odr-violations-Wall 不給出警告 –

+0

它使用調試信息,所以請嘗試添加-g –

+0

謝謝。 仍然 克++ -g -c main.cpp中 克++ -g -c a.cpp 克++ -g -o測試main.o AO -Wl, - 檢測-ODR-侵犯-Wall 沒有給出警告 爲相同 g ++ -g -o測試main.cpp a.cpp -Wl, - detect-odr-violations -Wall –

1

我認爲答案是「否」,它會保持這種狀態。

類型只有鏈接器在函數參數或模板參數中出現時纔會看到的名稱(其他一些古怪的東西?也許)。你的例子實際上是一個更簡單的例子,並且要檢測鏈接器是否必須使用ABI(實際上)標記專業化提供的模板參數。但是他們不能這麼做:你必須能夠傳遞指向模板結構的指針,而不必知道它是否指向專業化。即使那樣,即使是那樣,即使ABR變化不大,你也不會得到更激進的意義,這意味着至少要考慮是否需要重新編譯和/或重新鏈接每個庫和可執行文件。如果您的結構是成員struct trojan { A<int> greeks; },那麼無論如何您都會使用相同的類型名稱,並且如果它們從未作爲函數參數或模板參數出現,則鏈接器即使它們不同也不會看到它們。

爲了獲得自動檢測,我會開始使用像叮噹這樣平易近人的OSS C++前端。您將需要(非標準)名稱修改規則來標記模板專用化參數名稱,並使其生成邊帶列表,以查找其引用的所有模板聲明的邊帶列表。然後編寫一個單獨的工具,查看鏈接在一起的所有對象的列表,並在一個對象中找到使用的名稱+參數(不僅僅是引用或聲明)時發出抱怨,該對象也用於另一個對象,但是來自不同的專業化。