是否可以在 C++ 的编译时计算函数长度?

Is it possible to calculate function length at compile time in C++?

我有这段代码:

constexpr static VOID fStart()
{
    auto a = 3;
    a++;
}

__declspec(naked) 
constexpr static VOID fEnd() {};

static constexpr auto getFSize()
{
    return (SIZE_T)((PBYTE)fEnd - (PBYTE)fStart);
}

static constexpr auto fSize = getFSize();
static BYTE func[fSize];

是否可以在不使用任何标准库的情况下在编译期间将“func[fSize]”数组大小声明为“fStart()”的大小?为了稍后将 fStart() 的完整代码复制到此数组中,这是必要的。

标准 C++ 中没有获取函数长度的方法。

您需要使用特定于编译器的方法。

一种方法是让链接器创建一个段,并将您的函数放在该段中。然后使用段的长度。

您也许可以使用一些汇编语言结构来做到这一点;取决于汇编程序和汇编代码。

注意:在嵌入式系统中,移动功能代码是有原因的,例如移动到片上存储器或交换到外部存储器,或者对代码执行校验和。

在某些处理器上以及使用一些已知的编译器和 ABI 约定,您可以执行相反的操作:

在运行时生成机器代码。

对于 Linux 上的 x86/64,我知道 GNU lightning, asmjit, libgccjit 这样做。

elf(5) 格式知道函数的大小。

在 Linux 上,您可以生成 shared libraries (perhaps generate C or C++ code at runtime (like RefPerSys does and GCC MELT did), then compiling it with gcc -fPIC -shared -O) and later dlopen(3) / dlsym(3) it. And dladdr(3) 非常 有用。您将使用函数指针。

还读了一本关于 linkers and loaders 的书。

但是你通常不能不做一些relocation, unless that machine code is position-independent code就移动机器代码(PIC 通常比普通代码慢 运行)。

相关主题是 garbage collection of code (or even of agents). You need to read the garbage collection handbook and take inspiration from implementations like SBCL

还请记住,一个好的优化 C++ 编译器允许展开循环、内联扩展函数调用、删除死代码、进行函数克隆等...因此机器代码函数甚至可能不连续:两个C 函数 foo()bar() 可以共享数十条通用机器指令。

在您的 GPGPU 上阅读 Dragon book, and study the source code of GCC (and consider extending it with your GCC plugin). Look also into the assembler code produced by gcc -O2 -Wall -fverbose-asm -S. Some experimental variants of GCC might be able to generate OpenCL 代码 运行ning(然后,函数结束的概念本身就没有意义)

通过 C 和 C++ 生成的插件,您可以使用 dlclose(3) if you use Ian Taylor's libbacktrace and dladdr to explore your call stack. In 99% of the cases, it is not worth the trouble, since in practice a Linux process (on current x86-64 laptops in 2021) can do perhaps a million of dlopen(3), as my manydl.c 程序演示小心地删除它们(它生成“随机”C 代码,将其编译成一个独特的 /tmp/generated123.so,并且 dlopen 那个,并重复多次)。

(在台式机和服务器计算机上)覆盖 机器代码的唯一原因是持久服务器进程每秒生成机器代码。如果这是您的场景,您应该提到它(使用 Java 类加载器生成 JVM 字节码可能更有意义)。

当然,在 16 位微控制器上,情况非常不同。

Is it possible to calculate function length at compile time in C++?

不,因为在运行时间某些功能不再存在。

编译器以某种方式删除了它们。或者克隆它们。或者将它们内联。

对于 C++,standard containers 实际上很重要:会发生大量模板扩展,包括无用代码,您的优化编译器在某些时候必须将其删除。

(想想 2021 年用最近的 GCC 10.2 或 11 编译。到处使用,并链接 gcc -O3 -flto -fwhole-program:函数 foo23 可能被定义但从未被调用, 然后它不在 ELF 可执行文件中)

下面计算fStart函数的“字节大小”。但是,无法通过这种方式将大小作为 constexpr 获得,因为转换失去了编译时常量(例如参见 [​​=13=]),并且两个不相关的函数指针的差异无法在没有某种铸造。

#pragma runtime_checks("", off)
__declspec(code_seg("myFunc$a")) static void fStart()
{   auto a = 3; a++; }
__declspec(code_seg("myFunc$z")) static void fEnd(void)
{   }
#pragma runtime_checks("", restore)

constexpr auto pfnStart = fStart;                               // ok
constexpr auto pfnEnd = fEnd;                                   // ok
// constexpr auto nStart = (INT_PTR)pfnStart;                   // error C2131

const auto fnSize = (INT_PTR)pfnEnd - (INT_PTR)pfnStart;        // ok
// constexpr auto fnSize = (INT_PTR)pfnEnd - (INT_PTR)pfnStart; // error C2131