好的,这是我的看法:
struct stack_marker
{
thread_local static uint8_t* marker;
uint8_t stk;
stack_marker()
{
if (marker != nullptr)
{
throw std::runtime_error("second twice marker! don't do it");
}
marker = &stk;
}
};
thread_local uint8_t* stack_marker::marker = nullptr;
void sort3(uint8_t* (&a)[3]); //sorts 3 pointers, see gist
class only_on_stack
{
uint8_t place;
public:
NO_INLINE only_on_stack(int x)
{
uint8_t a;
if (!stack_marker::marker)
{
// not initialized yet, either forgot to put stack_marker in main
// or we are running before main, which is static storage
//throw std::runtime_error("only on stack object created in non-stack");
std::cout << x << ": I'm NOT on stack\n";
return;
}
uint8_t* ptrs[] = {
stack_marker::marker,
&place,
&a
};
sort3(ptrs);
if (ptrs[1] == &place) // place must be in the middle
{
std::cout << x << ": I'm on stack\n";
}
else
{
//throw std::runtime_error("only_on_stack object created in non-stack");
std::cout << x << ": I'm NOT on stack\n";
}
}
};
only_on_stack static_storage(1);
thread_local only_on_stack tl_storage(4);
int NO_INLINE stuff()
{
only_on_stack oos(2);
}
int main()
{
stack_marker mrk;
stuff();
auto test = new only_on_stack(3);
tl_storage; // access thread local to construct, or gcc omits it
}
诚然,我的解决方案并不是所有解决方案中最干净的,但它允许您继续使用常规的本地对象语法。
基本上,诀窍是将 2 个额外的对象放在堆栈上,而不是我们的对象:一个在线程的开头,一个在构造函数。因此,其中一个对象是在我们的对象之后在堆栈上创建的,其中一个是在之前创建的。有了这些信息,我们就可以检查这 3 个对象的地址顺序。如果对象真的在堆栈上,它的地址应该在中间。
但是,C++ 没有定义函数范围内对象的地址顺序,因此执行以下操作:
int main()
{
int a;
int b;
int c;
}
不保证是&b
在&a
and中间&c
。
为了解决这个问题,我们可以保留a
在 main 函数中,b
并c
在不同的force 非内联函数中移动:
void NO_INLINE foo()
{
int b;
int c;
}
int main()
{
int a;
foo();
}
在这种情况下,由于编译器无法知道foo
in main
、&a
>或<的局部变量,&b
所以。通过将相同的东西移动到另一个不可内联的函数,我们可以保证 &b 在and的中间。&c
&a
&b
&c
c
&a
&c
在我的实现中,stuff
function 是foo
函数,我们c
进入的函数是only_on_stack
.
实际工作实现在这里:https ://gist.github.com/FatihBAKIR/dd125cf4f06cbf13bb4434f79e7f1d43
无论堆栈向下还是向上增长,并且无论目标文件类型如何,并且希望是 ABI,它都应该工作,只要编译器不会以某种方式重新排序非内联函数的局部变量。
这-O3
在 linux 上的 g++-6 和 mac os x 上的最新 clang 上进行了测试。它应该可以在 MSVC 上运行,希望有人可以测试它。
两者的输出是:
1: I'm NOT on stack
2: I'm on stack
3: I'm NOT on stack
4: I'm NOT on stack
用法基本上是,您将一个stack_marker
对象放在每个线程(main
包括)的开头并调用另一个不可内联的函数并将其用作您的实际入口点。