標籤:

如何在C++中拋出一個編譯錯誤?

詳細說一下,就是代碼本身沒有任何問題 就只是邏輯需要,如何利用代碼實現 將本身沒有任何錯誤的代碼在編譯的時候讓編譯器產生一個error?

例如

//某一個代碼塊

if(something)

{

//這裡要怎麼做才能讓編譯器產生一個我自己定義的error呢?

----------------------------------------------------

分割線

----------------------------------------------------

比如我自己寫了一個test.dll 導出函數名為

void fun(void)

調用的時候採取動態載入

typedef void (_stdcall *FUN)();

HMODULE hDLL = LoadLibrary("test.dll");

FUN fp_fun = FUN(GetProcAddress(hDLL,"fun"));

在獲得導出函數的時候如果將導出函數名寫錯在編譯的時候我希望他就要拋出一個error 但是實際上不會 只會在執行的時候出錯 如何避免由於導出函數名寫錯導致程序崩潰的問題?


我感覺,你用 LoadLibrary() 的話,一般應該是你定義了一個介面,然後 dll 是插件,類似這樣的形式吧?那麼這時候如果出錯應該就是 dll 作者的鍋,你應該拋個異常。如果你是自己寫的 dll,固定的導出函數,沒必要用 LoadLibrary() 載入,直接鏈接到一起就行了啊。


C++ 不行必須上靜態分析

idris 裡面的話可以用 dependent pair 去「要求」入口參數滿足一些條件,然後調用的時候——給出證明。這裡面有兩種做法,一種是基於 Curry-Howard correspondence 構造一個入口參數的命題,不過這麼做非常的蛋疼,而且和 Bool 那套邏輯不太好銜接。

另一種則是用 So,比如

import Data.So

NAMES : List String
NAMES = ["fun", "far"]

InListCheck : String -&> List String -&> Bool
InListCheck s ss with (elemIndex s ss)
| Just x = True
| Nothing = False

InListProof : String -&> List String -&> Type
InListProof s ss = So (InListCheck s ss)

load : (x : String ** InListProof x NAMES) -&> Nat
load (name ** pf) with (elemIndex name NAMES)
| Just n = n + 1
| Nothing impossible

-- 沒有問題,輸出 1
load ("fun" ** Oh)
-- 沒有問題,輸出 2
load ("far" ** Oh)
-- 報錯「類型不匹配:True 不是 False」
load ("what" ** Oh)

dynamic_load : String -&> Nat
dynamic_load x = case (choose $ InListCheck x NAMES) of
Left p =&> load (x ** p)
Right p =&> 0


你需要的是集成在開發流程中的單元測試。測試不過 不然提交


題主這個問題沒辦法,有些情況可以。

linux內核裡面有很多例子。

#define __compiletime_error_fallback(p)do {((void)sizeof(char[1-2*p]));}while(0)

p等於0的時候,編譯器會優化掉這個代碼,沒有任何付作用,p等於1會產生一個編譯錯誤(sizeof(char[-1]))

利用這個可以定義編譯期間斷言。

#define __compiletime_assert(condition, msg, prefix, suffix)

do {

bool __cond = !(condition);

extern void prefix ## suffix(void) __compiletime_error(msg);

if (__cond)

prefix ## suffix();

__compiletime_error_fallback(__cond);

} while (0)

#define compiletime_assert(condition, msg)

_compiletime_assert(condition, msg, __compiletime_assert_, __LINE__)


題主的問題得看問題描述里的那個具體場景的例子才可以確定是什麼意思。

本質上說題主是想讓一部分代碼在編譯時就運行(求值),並在滿足一定條件時觸發一個編譯錯誤。是這樣么題主?

是的話,就那個test.dll的例子而言,光靠標準C++,使用常規的編譯流程來做的話,是做不到的。C++就算到C++17,可以在編譯時求值的功能也做不到「讀一個DLL,看看裡面的導出函數都有哪些,然後看看我這源碼里傳入的字元串的內容是否能匹配上導出函數里的」這種事情。

但是這樣的功能並不是一定要靠標準C++、使用常規的編譯流程做啊。

可以想想其它辦法,例如說使用現有編譯器的API來寫編譯器插件或者靜態分析器——於是就可以在編譯時(或靜態分析時)執行一些自己的邏輯了。

例如說Clang編譯器所開放出來的Clang Static Analyzer框架就可以做這種事情。官網上列舉的WinAPI例子就頗有點跟題主的需求相似的味道:List of potential checkers

題主要做的事情就是寫一個analyzer,檢查GetProcAddress()的參數,第一個參數是不是來自一個LoadLibrary()調用的結果,是的話提取出library名字,主動去載入一下這個library看它是否存在;存在的話,然後第二個參數是否能從這個library的導出表裡找出對應的項。

就這麼簡單。


你怎麼會希望由編譯器拋出運行時錯誤呢。。。。。


這種小事就上靜態分析真是知乎會有的答案啊(逃

你可能需要代碼生成器。你寫一個程序,讀dll,把所有的函數都做成真·函數,第一次運行的時候內部調用GetProcAddress。這樣名字寫錯了就會有編譯錯誤了。譬如說:

auto fp_fun = reinterpret_cast&(TestDll::fun());


你這是要求在編譯期預估運行期的錯誤啊,單純#error不能滿足你的需求,你需要@RednaxelaFX那樣的靜態分析器。

實際工作中,我們為了避免這種字元串敲錯只能到運行時發現的問題,我們一般把常量字元串統一定義好,用宏名或者常量名去訪問,防止敲多了手滑。


Template 可以做到,不過調用 dll 這個屬於運行時行為,不可能實現靜態檢查。這就是為什麼一個 lib 要配一堆 includes 的原因,能編譯時鏈接就鏈接了吧,動態載入一個 dll 除了插件以外最好少用。


使用 error 宏,參考 Error directive - cppreference.com。

#error "error! f**k me on github."

當然另一個答案說的 static_assert:

static_assert(1 &< 0, "Fuck the complier");

也是可以的,只不過這是一個 assert 形式的拋錯,在第一欄要寫上斷言條件,參考 Static Assertion。

------------------ 分割線 ------------------

咦!問題什麼時候更新了?

好吧接下來說說 DLL 的問題。雖然我好幾年沒用 Visual Studio 了,但大概還記得那麼點操作。

項目配置的編譯選項裡面我記得有個編譯前執行腳本?你在那裡寫個腳本,大概意思就是去遍歷你的 DLL 文件,讀取裡面所有的函數名,無論是寫成包裝的形式,還是布爾值檢測是否存在的形式,總之寫到一個頭文件裡面去,如 dll_fun_names.h。

最後,就不用我說了吧?哪怕是 static_assert 也好,#error 也行,或者直接調用那個包裝好的函數。


static_assert(1 &< 0, "Fuck the complier");


我覺得這個需求是「thread.sleep(1000)"//以後由用戶付費進行性能提升

的高級版


程序崩掉是運行時錯誤,編譯過程是查不出來的。你可以在代碼里加上對函數指針是否為空的判斷。如果函數名寫錯並且DLL導出函數里確實沒有你寫的函數名,那麼GetProcAddress( )的結果應當為NULL


題主說代碼沒什麼問題,我的理解是要在運行時拋出編譯錯誤,這是做不到的。這麼多回答也不知道在解釋什麼。


你問題裡面描述的那個載入dll的例子是做不到的


使用if(8 == a)

而不使用if(a == 8)


推薦閱讀:

C++的RAND函數生成的值為什麼存在嚴重的不隨機性?
在c語言中,使用函數指針是否可以提高函數的調用速度 ?
C++中如何定義指向函數指針的指針?

TAG:C | CC | 編譯器 |