Test pseudo-namespaces in C
In C you can create pseudo-namespaces by abusing structs of function pointers, making function calls look like `module.func(x)` instead of `func(x)`. A lot of people online spout that this creates overhead, but this should show that clang>=15.0.7 will still inline such function calls. GCC 11.3.1 is unable to inline the function, but will still call the function directly.
This commit is contained in:
20
CMakeLists.txt
Normal file
20
CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
project(Namespaces)
|
||||||
|
|
||||||
|
add_executable(main
|
||||||
|
module.c
|
||||||
|
main.c)
|
||||||
|
|
||||||
|
target_compile_options(main
|
||||||
|
PRIVATE
|
||||||
|
-Werror -Wall -Wextra -O3 -g -std=c2x -flto)
|
||||||
|
|
||||||
|
target_link_options(main
|
||||||
|
PRIVATE
|
||||||
|
-flto -Wl,-O2)
|
||||||
|
|
||||||
|
target_compile_definitions(main
|
||||||
|
PRIVATE
|
||||||
|
-DNAMESPACES)
|
||||||
29
Makefile
Normal file
29
Makefile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
CC ?= clang
|
||||||
|
|
||||||
|
# compiler specific flags
|
||||||
|
CFLAGS.gcc :=
|
||||||
|
CFLAGS.clang :=
|
||||||
|
|
||||||
|
# general flags
|
||||||
|
CFLAGS := \
|
||||||
|
-Wall -Wextra -Werror -Wpedantic \
|
||||||
|
-O3 -g -flto \
|
||||||
|
-std=c2x \
|
||||||
|
${CFLAGS.${CC}} \
|
||||||
|
-DNAMESPACES \
|
||||||
|
|
||||||
|
LDFLAGS := \
|
||||||
|
-flto \
|
||||||
|
-Wl,-O3
|
||||||
|
|
||||||
|
dump.asm : test
|
||||||
|
gdb $< -batch -ex 'disassemble main' > $@
|
||||||
|
(gdb $< -batch -ex 'disassemble succ' >> $@) || true
|
||||||
|
|
||||||
|
test : main.o module.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
%.o : %.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ -c $^
|
||||||
|
|
||||||
21
build.sh
Executable file
21
build.sh
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
requirements=("cmake" "gdb")
|
||||||
|
|
||||||
|
for cmd in $requirements; do
|
||||||
|
if ! command -v gdb; then
|
||||||
|
echo "gdb not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p build
|
||||||
|
(cd build
|
||||||
|
rm * -rf
|
||||||
|
CC=clang cmake ..
|
||||||
|
make -j2
|
||||||
|
)
|
||||||
|
|
||||||
|
gdb ./build/main -batch -ex 'disassemble main' > dump.asm
|
||||||
|
gdb ./build/main -batch -ex 'disassemble succ' 1>> dump.asm 2>/dev/null || true
|
||||||
|
|
||||||
10
dump.asm
Normal file
10
dump.asm
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Dump of assembler code for function main:
|
||||||
|
0x0000000000401130 <+0>: push %rax
|
||||||
|
0x0000000000401131 <+1>: mov $0x402010,%edi
|
||||||
|
0x0000000000401136 <+6>: mov $0x6,%esi
|
||||||
|
0x000000000040113b <+11>: xor %eax,%eax
|
||||||
|
0x000000000040113d <+13>: call 0x401030 <printf@plt>
|
||||||
|
0x0000000000401142 <+18>: xor %eax,%eax
|
||||||
|
0x0000000000401144 <+20>: pop %rcx
|
||||||
|
0x0000000000401145 <+21>: ret
|
||||||
|
End of assembler dump.
|
||||||
22
main.c
Normal file
22
main.c
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int x = 4;
|
||||||
|
|
||||||
|
#ifdef NAMESPACES
|
||||||
|
#pragma message "namespaces enabled"
|
||||||
|
x = module.succ(x);
|
||||||
|
x = module.succ(x);
|
||||||
|
#else
|
||||||
|
x = succ(x);
|
||||||
|
x = succ(x);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("%d\n", x);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
12
module.c
Normal file
12
module.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
|
inline int succ(int x)
|
||||||
|
{
|
||||||
|
return x+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct exports module = {
|
||||||
|
.succ = succ,
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user