Given a struct, for instance:
struct A {
char a;
char b;
} __attribute__((packed));
I want the offset of b
(in this example, 1) in the struct to be printed at compile time - I don't want to have to run the program and call something like printf("%zu", offsetof(struct A, b));
because printing is non-trivial on my platform. I want the offset to be printed by the compiler itself, something like:
> gcc main.c
The offset of b is 1
I've tried a few approaches using #pragma message
and offsetof
, with my closest being:
#define OFFSET offsetof(struct A, b)
#define XSTR(x) STR(x)
#define STR(x) #x
#pragma message "Offset: " XSTR(OFFSET)
which just prints:
> gcc main.c
main.c:12:9: note: #pragma message: Offset: __builtin_offsetof (struct A, b)
which does not print the numeric offset. It's possible to binary-search the offset at compile time by using _Static_assert
- but my real structs are big and this can get a bit cumbersome.
I suspect the stated constraint “I want the offset to be printed by the compiler itself” is an XY problem and that we merely need the offset to be printed by the build tools on the system used for building, not specifically by the compiler.
#if GenerateStructureOffsets
__asm__("# offsetof(struct A, b) = %c0" : : "i" (offsetof(struct A, b)));
#endif
Given this macro:
#define PRINT_OFFSETOF(A, B) char (*__daniel_kleinstein_is_cool)[sizeof(char[offsetof(A, B)])] = 1
Surprisingly, looks like __builtin_choose_expr
works inside __deprecated__
function attribute. The following program:
#include <stddef.h>
struct A {
char a;
char b;
} __attribute__((packed));
#define printval_case(x, xstr, y, ...) __builtin_choose_expr(x == y, xstr"="#y, __VA_ARGS__)
#define printval(x) do { \
__attribute__((__deprecated__( \
printval_case(x, #x, 0, \
printval_case(x, #x, 1, \
printval_case(x, #x, 2, \
printval_case(x, #x, 3, \
/* etc... */ \
(void)0 )))) \
))) void printval() {} \
printval(); \
} while (0)
int main() {
printval(offsetof(struct A, a));
printval(offsetof(struct A, b));
}
When compiled, then gcc will output:
<source>:23:30: warning: 'printval' is deprecated: offsetof(struct A, a)=0 [-Wdeprecated-declarations]
<source>:24:30: warning: 'printval' is deprecated: offsetof(struct A, b)=1 [-Wdeprecated-declarations]
In a similar fashion you could embed the value into the executable, (similarly to how CMake detects compiler stuff ):
#include <stddef.h>
struct A {
char a;
char b;
} __attribute__((packed));
#define printval_case(x, xstr, y, ...) __builtin_choose_expr(x == y, xstr"="#y, __VA_ARGS__)
#define embedval(x) do { \
static const __attribute__((__used__)) const char unused[] = \
printval_case(x, #x, 0, \
printval_case(x, #x, 1, \
printval_case(x, #x, 2, \
printval_case(x, #x, 3, \
/* etc... */ \
(void)0 )))); \
} while (0)
int main() {
embedval(offsetof(struct A, a));
embedval(offsetof(struct A, b));
}
then:
$ gcc file.c && strings ./a.out | grep offsetof
offsetof(struct A, b)=1
offsetof(struct A, a)=0
Perhaps a new pre-processing step would be acceptable. This could then be done as a separate step that won't affect your production binary.
offsetdumper.sh
#!/bin/bash
#
# pre-process some source file(s), add a macro + main() and a file with rules
# describing the interesting symbos. Compile and run the result.
dumprulefile="$1"
shift
# Define your own macros, like OFFSET, in the "Here Document" below:
{
gcc -E "$@" && cat<<EOF
#define OFFSET(x,y) do { printf("%s::%s %zu\n", #x, #y, offsetof(x,y)); } while(0)
#include <stddef.h>
#include <stdio.h>
int main() {
EOF
cat "$dumprulefile"
echo '}'
} | g++ -x c - && ./a.out
rules
OFFSET(A,a);
OFFSET(A,b);
source.h
typedef struct {
char a;
char b;
} __attribute__((packed)) A;
Example:
$ ./offsetdumper.sh rules *.h
A::a 0
A::b 1
This is a bit fragile and won't work if your source.h
includes a main
function, so it may need some tinkering to fulfill your needs.
One possible way is to make the offset the size of an array and then pass the address of that array to a function expecting an incompatible pointer so it prints the type:
static int a[offsetof(struct A, b)];
static void foo1(int *p) { (void)p; }
static void foo2(void) { foo1(&a); }
This prints:
x1.c: In function ‘foo2’:
x1.c:13:1: warning: passing argument 1 of ‘foo1’ from incompatible pointer type [enabled by default]
static void foo2(void) { foo1(&a); }
^
x1.c:12:13: note: expected ‘int *’ but argument is of type ‘int (*)[1]’
static void foo1(int *p) { (void)p; }
^
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.