gpt4 book ai didi

c++ - Visual C++ __forceinline 奇怪的行为

转载 作者:太空宇宙 更新时间:2023-11-04 13:08:22 26 4
gpt4 key购买 nike

我试图强制 Visual C++ 编译器内联一个特定的函数。我知道 inline__forceinline只是一个建议。根据MSDN ,如果编译器无法内联使用 __forceinline 标记的函数关键字然后它生成警告:

If the compiler cannot inline a function declared with __forceinline, it generates a level 1 warning.

但是我没有收到这样的警告。当我调试我的应用程序时,我看到有一个 call <inlined function address>在反汇编窗口中,即我的函数没有内联。那么如何检查我的函数是否内联?这是否意味着如果没有生成警告,那么函数被内联并且问题出现在其他地方?

这是我的功能:

__forceinline SecByteBlock aes_generate_iv(size_t blockSize)
{
const int seedSize = 32;
SecByteBlock rngSeed(seedSize);
OS_GenerateRandomBlock(false, rngSeed, rngSeed.size());

RandomPool rngp;
rngp.IncorporateEntropy(rngSeed, rngSeed.size());

SecByteBlock aesIV(blockSize);
rngp.GenerateBlock(aesIV, aesIV.size());

return aesIV;
}

这是 VC++ 无法内联函数的情况(来自 MSDN ):

  • 函数或其调用者使用/Ob0(调试构建的默认选项)进行编译。
  • 函数和调用者使用不同类型的异常处理(一种是 C++ 异常处理,另一种是结构化异常处理)。
  • 该函数有一个可变参数列表。
  • 该函数使用内联汇编,除非使用/Og、/Ox、/O1 或/O2 进行编译。
  • 该函数是递归的并且没有伴随#pragma inline_recursion(on)。使用 pragma,递归函数被内联到默认深度 16 次调用。要减少内联深度,请使用 inline_depth pragma。
  • 该函数是虚拟的,并且被虚拟地调用。可以内联对虚函数的直接调用。
  • 程序获取函数的地址,并通过指向函数的指针进行调用。可以内联对地址已被占用的函数的直接调用。
  • 该函数还使用裸 __declspec 修饰符标记。

最佳答案

同一个警告有两个版本:

https://msdn.microsoft.com/en-us/library/aa734011(v=vs.60).aspx

Compiler Warning (level 1) C4714
Visual Studio 6.0
'function' : function marked as __forceinline not inlined

https://msdn.microsoft.com/en-us/library/a98sb923.aspx

Compiler Warning (level 4) C4714
Visual Studio 2015
function 'function' marked as __forceinline not inlined

因此,例如,要在 Visual Studio 2015 中启用警告,您必须使用级别 4 (/W4) 而不是默认情况下的级别 3 (/W3)。

下面是关于如何消除此类编译器行为的一些建议。我已经在 MSVC2015 中解决了这些案例集:

  1. 递归调用
  2. SomeType SomeType::operator++(int)/SomeType SomeType::operator--(int)
  3. std::string 由 __forceinline 函数中的值返回

1。递归调用

template <typename T>
__forceinline T int_log2_floor(T v)
{
ASSERT_GT(v, T(0));

if (1 >= v) return 0;

return T((v >= 2 ? int_log2_floor(v / 2) : 0) + 1); // Warning C4714
}

这里的解决方案之一就是展开递归:

template <typename T>
__forceinline T int_log2_floor(T v)
{
ASSERT_GT(v, T(0));

if (1 >= v) return 0;

T ret = 0;
T i = v;

do {
++ret;
i /= 2;
} while (i >= 2);

return ret;
}

2。 `SomeType SomeType::operator++(int)`/`SomeType SomeType::operator--(int)`

class SomeType
{
__forceinline SomeType operator++(int) // Warning C4714
{
const auto it = *this;
m_it++;
return it;
}

//...
iterator m_it
};

这个对我有用:

class SomeType;

SomeType operator++(SomeType & r, int);

class SomeType
{
friend SomeType operator++(SomeType & r, int);

//...
iterator m_it
};

__forceinline SomeType operator++(SomeType & r, int)
{
const auto it = r;
r.m_it++;
return it;

// You still can write here `const auto it = *this; m_it++; return it`,
// but i hell don't know why this works.
}

3。 `std::string` 由 __forceinline 函数中的值返回

这个真的很奇怪,但似乎之间有某种干扰__forceinline-ed 函数介于 inline-ed 函数之间,因为 std::string 基本上使用第二个 - inline

template<typename T>
__forceinline std::string int_to_bin(T i, bool first_bit_is_lowest_bit = false) // Warning C4714
{
std::bitset<sizeof(T) * CHAR_BIT> bs(i);
if (!first_bit_is_lowest_bit) {
return bs.to_string();
}

const std::string bs_str = bs.to_string();
return std::string(bs_str.rbegin(), bs_str.rend());
}

因为warning已经被return-by-value类型触发,这里的解决方法是通过参数返回,通过inline内联:

template<typename T>
__forceinline void int_to_bin_forceinline(std::string & ret, T i, bool first_bit_is_lowest_bit = false)
{
std::bitset<sizeof(T) * CHAR_BIT> bs(i);
if (!first_bit_is_lowest_bit) {
ret = bs.to_string();
return;
}

const std::string bs_str = bs.to_string();
ret = std::string(bs_str.rbegin(), bs_str.rend());
return;
}

template<typename T>
inline std::string int_to_bin(T i, bool first_bit_is_lowest_bit = false)
{
std::string res;
int_to_bin_forceinline(res, i, first_bit_is_lowest_bit);
return res;
}

不用说,所有 3 个版本都可以通过使用 inline 关键字而不是 __forceinline 来解决。

关于c++ - Visual C++ __forceinline 奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41102421/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com