GCC
の下で_C99
_を使用しています。
変更できないヘッダーにstatic
inline
と宣言された関数があります。
関数は決して戻りませんが、__attribute__((noreturn))
とマークされていません。
コンパイラに戻らないように指示する方法で関数を呼び出すにはどうすればよいですか?
自分のnoreturn関数から呼び出していますが、「noreturn関数が戻る」という警告を部分的に抑制したいのですが、オプティマイザーなども支援したいと思っています。
属性に宣言を含めようとしましたが、宣言が繰り返されると警告が表示されます。
関数ポインタを作成してそれに属性を適用しようとしましたが、関数属性は指定された関数には適用できないと表示されます。
いくつかの解決策:
__attribute__
を使用して関数を再宣言するヘッダーに__attribute__((noreturn))
を追加して、その関数を変更してみてください。
このばかげたテストが示すように、いくつかの関数を新しい属性で再宣言できます(fopen
に属性を追加します):
#include <stdio.h>
extern FILE *fopen (const char *__restrict __filename,
const char *__restrict __modes)
__attribute__ ((warning ("fopen is used")));
void
show_map_without_care (void)
{
FILE *f = fopen ("/proc/self/maps", "r");
do
{
char lin[64];
fgets (lin, sizeof (lin), f);
fputs (lin, stdout);
}
while (!feof (f));
fclose (f);
}
最後に、次のようなマクロを定義できます
#define func(A) {func(A); __builtin_unreachable();}
(これは、マクロ内でマクロ名がマクロ展開されないという事実を使用します)。
戻ってこないfunc
が、戻ってくると宣言している場合。 int
使用します ステートメント式 like
#define func(A) ({func(A); __builtin_unreachable(); (int)0; })
上記のようなマクロベースのソリューションは、常に機能するとは限りません。 func
が関数ポインタとして渡された場合、または単に誰かが(func)(1)
をコーディングした場合、これは合法ですが醜いです。
noreturn
属性を使用して静的インラインを再宣言するそして次の例:
// file ex.c
// declare exit without any standard header
void exit (int);
// define myexit as a static inline
static inline void
myexit (int c)
{
exit (c);
}
// redeclare it as notreturn
static inline void myexit (int c) __attribute__ ((noreturn));
int
foo (int *p)
{
if (!p)
myexit (1);
if (p)
return *p + 2;
return 0;
}
gCC 4.9(Debian/Sid/x86-64から)をgcc -S -fverbose-asm -O2 ex.c
としてコンパイルすると、期待される最適化を含むアセンブリファイルが得られます。
.type foo, @function
foo:
.LFB1:
.cfi_startproc
testq %rdi, %rdi # p
je .L5 #,
movl (%rdi), %eax # *p_2(D), *p_2(D)
addl $2, %eax #, D.1768
ret
.L5:
pushq %rax #
.cfi_def_cfa_offset 16
movb $1, %dil #,
call exit #
.cfi_endproc
.LFE1:
.size foo, .-foo
#pragma GCC Diagnostics で遊んで、警告を選択的に無効にすることができます。
最後に、 [〜#〜] melt [〜#〜] プラグインを使用して、最近のgcc
をカスタマイズし、単純な拡張機能をコーディングすることができます([ 〜#〜] melt [〜#〜]ドメイン固有言語)目的の関数を実行するときに属性noreturn
を追加します。 register_finish_decl_first
と関数名の一致を使用した、おそらく数十のMELT行です。
私は[〜#〜] melt [〜#〜](フリーソフトウェアGPLv3 +)の主な作者なので、あなたが尋ねれば、おそらくそれをコーディングすることさえできます。ここまたはできれば[email protected]
;戻らない関数の具体的な名前を付けてください。
おそらく、MELTコードは次のようになります。
;;file your_melt_mode.melt
(module_is_gpl_compatible "GPLv3+")
(defun my_finish_decl (decl)
(let ( (tdecl (unbox :tree decl))
)
(match tdecl
(?(tree_function_decl_named
?(tree_identifier ?(cstring_same "your_function_name")))
;;; code to add the noreturn attribute
;;; ....
))))
(register_finish_decl_first my_finish_decl)
実際のMELTコードは少し複雑です。そこでyour_adding_attr_mode
を定義したいとします。詳細はお問い合わせください。
必要に応じてMELT拡張機能をyour_melt_mode.melt
でコーディングしたら(そして、MELTチュートリアルでそのMELT拡張機能をyour_melt_mode.quicklybuilt.so
に documented としてコンパイルすると)、コードを
gcc -fplugin=melt \
-fplugin-arg-melt-extra=your_melt_mode.quicklybuilt \
-fplugin-arg-melt-mode=your_adding_attr_mode \
-O2 -I/your/include -c yourfile.c
つまり、CFLAGS
のMakefile
にいくつかの-fplugin-*
フラグを追加するだけです。
ところで、私はMELTモニターでコーディングしているだけです(github: https://github.com/bstarynk/melt-monitor ...、ファイルmeltmom-process.melt
非常によく似ています。
MELT拡張機能を使用すると、内部GCC AST(GCCTree)が変更されるため、追加の警告は表示されません。宣言された関数のオンザフライ!
MELTを使用してGCCをカスタマイズすることは、GCCの内部ASTを変更するため、おそらく最も防弾のソリューションです。もちろん、これはおそらく最もコストのかかるソリューションです(GCC固有であり、GCCが進化しているとき、たとえば次のバージョンのGCCを使用するときなど、小さな変更が必要になる場合があります)が、私が示しているように、あなたの場合。
PS。 2019年、GCCMELTは放棄されたプロジェクトです。 GCCをカスタマイズする場合(GCCの最近のバージョン(GCC 7、8、9など)の場合)、C++で独自の GCCプラグイン を作成する必要があります。