如何拒絕編譯器將函數內聯處理?
1. 我需要跨編譯單元鏈接一個被編譯器默認識別為inline的函數,被inline的函數在.o中是沒有符號的,鏈接失敗
2. 由於此內聯函數是類的具體實現,介面未暴露在.h中,所以也不能移到.h中取定義,那麼,有拒絕編譯器內聯特定函數的方式么? 或者 有什麼其他的方法.
除了GCC和MSVC的noinline語法,我想你也可以試試函數指針,構造出一個functions call site給後面的object使用。
void (*ptrfunc)(int);
ptrfunc = some_func;
ptrfunc(25);
而寫到這裡的時候,我的確也測試了一下你說的有關類加上__attribute__(noinline),的確如樓主所說的還是被inline了。
如:
#include &
class X
{
public:
X(int i) : x_(i){}
int getX() __attribute__((noinline)){return x_;}
int x_;
};
int main()
{
X x(7);
}
g++ -c http://a.cc
readelf -Ws a.o | c++filtSymbol table ".symtab" contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS http://a.cc 2: 0000000000000000 0 SECTION LOCAL DEFAULT 2 3: 0000000000000000 0 SECTION LOCAL DEFAULT 44: 0000000000000000 0 SECTION LOCAL DEFAULT 5
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6 6: 0000000000000000 0 SECTION LOCAL DEFAULT 8 7: 0000000000000000 0 SECTION LOCAL DEFAULT 9 8: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 _ZN1XC5Ei 9: 0000000000000000 0 SECTION LOCAL DEFAULT 7 10: 0000000000000000 0 SECTION LOCAL DEFAULT 1 11: 0000000000000000 22 FUNC WEAK DEFAULT 6 X::X(int) 12: 0000000000000000 22 FUNC WEAK DEFAULT 6 X::X(int) 13: 0000000000000000 32 FUNC GLOBAL DEFAULT 2 main但是,如上文所說,我們可以手動把這個function call site弄出來,因為inline的本質就在這裡
#include &
class X
{
public:
X(int i) : x_(i){}
int getX() {return x_;}
int x_;
};
int (X::* ptrmfunc)();
int main()
{
X x(7);
ptrmfunc = X::getX;
// if you want to use pointer to member function.
// printf("%d
", (x.*ptrmfunc)());
}
g++ -c http://a.cc
readelf -Ws a.o | c++filtSymbol table ".symtab" contains 18 entries:
Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS http://a.cc 2: 0000000000000000 0 SECTION LOCAL DEFAULT 3 3: 0000000000000000 0 SECTION LOCAL DEFAULT 54: 0000000000000000 0 SECTION LOCAL DEFAULT 6
5: 0000000000000000 0 SECTION LOCAL DEFAULT 7 6: 0000000000000000 0 SECTION LOCAL DEFAULT 8 7: 0000000000000000 0 SECTION LOCAL DEFAULT 10 8: 0000000000000000 0 SECTION LOCAL DEFAULT 11 9: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 _ZN1XC5Ei 10: 0000000000000000 0 SECTION LOCAL DEFAULT 9 11: 0000000000000000 0 SECTION LOCAL DEFAULT 1 12: 0000000000000000 0 SECTION LOCAL DEFAULT 2 13: 0000000000000000 22 FUNC WEAK DEFAULT 7 X::X(int)14: 0000000000000000 22 FUNC WEAK DEFAULT 7 X::X(int)
15: 0000000000000000 16 FUNC WEAK DEFAULT 8 X::getX() 16: 0000000000000000 16 OBJECT GLOBAL DEFAULT 6 ptrmfunc 17: 0000000000000000 70 FUNC GLOBAL DEFAULT 3 main這樣就可以出來了。把C函數func1聲明為static的,再extern 聲明一個全局函數指針變數func2,再在某地定義func2的值是func1的地址。這樣func2可以像函數一樣用,而且調用絕對不會內聯。
顯式聲明了noinline,結果還是被inline了,說明編譯器未發現對這個函數符號的任何引用,內聯了也不會影響邏輯
你可以構造一個引用關係,隨便怎麼寫都行,只要代碼里出現了對這個函數地址符號的引用,編譯器為了使引用關係合法,不會擅自內聯的
比如 function()函數你是不希望被內聯的別的地方寫個 printf("%x", function);或者 int x= (int)function;
就可以了gcc:
void __attribute__ ((noinline)) maki() {
...
}
void __declspec(noinline) maki() {
...
}
class A
{
public:
int foo_member_always() __attribute__((always_inline)) { return 1337; }
int foo_member() { return 1337; }
};
int foo_normal() { return 1337; }
inline int foo_inline() { return 1337; }
static inline int foo_static_inline() { return 1337; }
extern inline int foo_extern_inline() { return 1337; }
int use()
{
return foo_normal()+foo_static_inline()+foo_inline()+foo_extern_inline()+A().foo_member()+A().foo_member_always();
}
% g++ a.cc -c readelf -Ws a.o | c++filt | grep foo
7: 000000000000000b 11 FUNC LOCAL DEFAULT 4 foo_static_inline()
15: 0000000000000000 15 FUNC WEAK DEFAULT 8 A::foo_member()
16: 0000000000000000 11 FUNC GLOBAL DEFAULT 4 foo_normal()
17: 0000000000000000 11 FUNC WEAK DEFAULT 9 foo_inline()
18: 0000000000000000 11 FUNC WEAK DEFAULT 10 foo_extern_inline()
% g++ -O2 a.cc -c readelf -Ws a.o | c++filt | grep foo
9: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 foo_normal()
% g++ -O3 a.cc -c readelf -Ws a.o | c++filt | grep foo
9: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 foo_normal()
% g++ -O3 -fno-inline a.cc -c readelf -Ws a.o | c++filt | grep foo
6: 0000000000000000 6 FUNC LOCAL DEFAULT 2 A::foo_member() [clone .isra.0]
13: 0000000000000010 6 FUNC GLOBAL DEFAULT 2 foo_normal()
14: 0000000000000000 6 FUNC WEAK DEFAULT 8 foo_extern_inline()
% g++ -O2 -fkeep-inline-functions a.cc -c readelf -Ws a.o | c++filt | grep foo
12: 0000000000000010 6 FUNC LOCAL DEFAULT 5 foo_static_inline()
22: 0000000000000000 6 FUNC WEAK DEFAULT 9 A::foo_member_always()
23: 0000000000000000 6 FUNC WEAK DEFAULT 11 A::foo_member()
24: 0000000000000000 6 FUNC GLOBAL DEFAULT 5 foo_normal()
25: 0000000000000000 6 FUNC WEAK DEFAULT 14 foo_inline()
26: 0000000000000000 6 FUNC WEAK DEFAULT 16 foo_extern_inline()
% g++ -O3 -fkeep-inline-functions a.cc -c readelf -Ws a.o | c++filt | grep foo
12: 0000000000000010 6 FUNC LOCAL DEFAULT 5 foo_static_inline()
22: 0000000000000000 6 FUNC WEAK DEFAULT 9 A::foo_member_always()
23: 0000000000000000 6 FUNC WEAK DEFAULT 11 A::foo_member()
24: 0000000000000000 6 FUNC GLOBAL DEFAULT 5 foo_normal()
25: 0000000000000000 6 FUNC WEAK DEFAULT 14 foo_inline()
26: 0000000000000000 6 FUNC WEAK DEFAULT 16 foo_extern_inline()
文檔可以參閱 info "(gcc) inline" 和 man gcc
If you specify both "inline" and "extern" in the function definition,
then the definition is used only for inlining. In no case is thefunction compiled on its own, not even if you refer to its addressexplicitly. Such an address becomes an external reference, as if youhad only declared the function, and had not defined it.
This combination of "inline" and "extern" has almost the effect of a
macro. The way to use it is to put a function definition in a headerfile with these keywords, and put another copy of the definition(lacking "inline" and "extern") in a library file. The definition inthe header file causes most calls to the function to be inlined. If anyuses of the function remain, they refer to the single copy in thelibrary."-fkeep-inline-functions" In C, emit "static" functions that are declared "inline" into theobject file, even if the function has been inlined into all of its
callers. This switch does not affect functions using the "extern inline" extension in GNU C90. In C++, emit any and all inline functions into the object file.GCC和msvc都支持noinline的,語法稍有不同。
為啥不用export
個人覺得這個時候應該優先考慮重構一下代碼的物理結構,而不是去用編譯器trick。
推薦閱讀:
※為什麼編譯器不能「合成「純虛析構函數的函數定義??
※第一個 C 語言編譯器是用什麼語言編寫的?
※編程語言是語法比較重要還是編譯器的具體實現比較重要?
※符號表和抽象語法樹是什麼關係?兩者在編譯器設計中是否必需?
※程序設計中,堆和棧比較重要。棧存取速度大於堆,而且編譯器可以修改棧大小,這個值可以隨意設置嗎?