有没有办法在不添加到调用堆栈的情况下调用函数?
Is there a way to call a function without adding to the call stack?
我有一些 goto
的 C++ 代码看起来像
#include <stdlib.h>
void test0()
{
int i = 0;
loop:
i++;
if (i > 10) goto done;
goto loop;
done:
exit(EXIT_SUCCESS);
}
我想去掉 goto
,同时(大部分)保留原始代码的外观;或多或少排除了 for
、while
等(除非它们隐藏在宏后面),因为这会过多地改变现有代码的外观。想象一下,想要最小化现有代码和更改代码之间的“差异”。
一个想法是使用 class 方法:
class test1 final
{
int i = 0;
void goto_loop()
{
i++;
if (i > 10) goto_done();
goto_loop();
}
void goto_done()
{
exit(EXIT_SUCCESS);
}
public:
test1() { goto_loop(); }
};
这有效,但每次调用 goto_loop()
都会添加到调用堆栈中。有什么方法可以进行类似 exec
的函数调用吗?也就是说,以某种方式“内联”调用函数...执行额外的代码 而不 添加到调用堆栈?有没有办法使尾递归显式化?
使用 C++20(甚至 C++23)是可以接受的,尽管 C++17 解决方案会“不错”。
致所有想知道“为什么?”的人真正的原码是BASIC ...
听起来你想要 ensure tail call optimization. At present there's no standard way to do this, but if you're using Clang you can use the clang::musttail
属性。
循环可能会保留代码结构:
void test2()
{
int i = 0;
for (;;) {
i++;
if (i > 10) break;
continue;
}
exit(EXIT_SUCCESS);
}
即使
void test3()
{
for (int i = 0; i < 11; ++i) {
/*Empty*/
}
exit(EXIT_SUCCESS);
}
会更地道(假设一些有用的代码,因为循环实际上是无用的)。
我的解决方案是编写一个 exec()
停止递归的例程:
template<typename Func>
void exec(const Func& f)
{
using function_t = Func;
static std::map<const function_t*, size_t> functions;
const auto it = functions.find(&f);
if (it == functions.end())
{
functions[&f] = 1;
while (functions[&f] > 0)
{
f();
functions[&f]--;
}
functions.erase(&f);
}
else
{
functions[&f]++;
}
}
使用该实用程序,我或多或少可以保留现有代码的外观
class test4 final
{
int i = 0;
void goto_loop_() {
i++;
if (i > 10) goto_done(); }
void goto_loop() { goto_loop_(); static const auto f = [&]() { goto_loop(); }; exec(f); }
void goto_done()
{
exit(EXIT_SUCCESS);
}
public:
test4() { goto_loop(); }
};
(使用 lambda 避免了指向成员函数的指针的麻烦。)
我有一些 goto
的 C++ 代码看起来像
#include <stdlib.h>
void test0()
{
int i = 0;
loop:
i++;
if (i > 10) goto done;
goto loop;
done:
exit(EXIT_SUCCESS);
}
我想去掉 goto
,同时(大部分)保留原始代码的外观;或多或少排除了 for
、while
等(除非它们隐藏在宏后面),因为这会过多地改变现有代码的外观。想象一下,想要最小化现有代码和更改代码之间的“差异”。
一个想法是使用 class 方法:
class test1 final
{
int i = 0;
void goto_loop()
{
i++;
if (i > 10) goto_done();
goto_loop();
}
void goto_done()
{
exit(EXIT_SUCCESS);
}
public:
test1() { goto_loop(); }
};
这有效,但每次调用 goto_loop()
都会添加到调用堆栈中。有什么方法可以进行类似 exec
的函数调用吗?也就是说,以某种方式“内联”调用函数...执行额外的代码 而不 添加到调用堆栈?有没有办法使尾递归显式化?
使用 C++20(甚至 C++23)是可以接受的,尽管 C++17 解决方案会“不错”。
致所有想知道“为什么?”的人真正的原码是BASIC ...
听起来你想要 ensure tail call optimization. At present there's no standard way to do this, but if you're using Clang you can use the clang::musttail
属性。
循环可能会保留代码结构:
void test2()
{
int i = 0;
for (;;) {
i++;
if (i > 10) break;
continue;
}
exit(EXIT_SUCCESS);
}
即使
void test3()
{
for (int i = 0; i < 11; ++i) {
/*Empty*/
}
exit(EXIT_SUCCESS);
}
会更地道(假设一些有用的代码,因为循环实际上是无用的)。
我的解决方案是编写一个 exec()
停止递归的例程:
template<typename Func>
void exec(const Func& f)
{
using function_t = Func;
static std::map<const function_t*, size_t> functions;
const auto it = functions.find(&f);
if (it == functions.end())
{
functions[&f] = 1;
while (functions[&f] > 0)
{
f();
functions[&f]--;
}
functions.erase(&f);
}
else
{
functions[&f]++;
}
}
使用该实用程序,我或多或少可以保留现有代码的外观
class test4 final
{
int i = 0;
void goto_loop_() {
i++;
if (i > 10) goto_done(); }
void goto_loop() { goto_loop_(); static const auto f = [&]() { goto_loop(); }; exec(f); }
void goto_done()
{
exit(EXIT_SUCCESS);
}
public:
test4() { goto_loop(); }
};
(使用 lambda 避免了指向成员函数的指针的麻烦。)