2

I'm trying to add a socket filter to one of my sockets in C++ (Linux). In the socket filter I need to get the offset of struct fork_proc_event, which is nested within another structure. The definition looks like this (cn_proc.h):

struct proc_event {
    ...
    union {
        ...
        struct fork_proc_event {
            __kernel_pid_t parent_pid;
            ...
        } fork;
        ...
    } event_data;
    ...
};

In C I would do this:

int off = offsetof(struct fork_proc_event, parent_pid);

However I'm developing in C++. If I try to do this:

int off = offsetof(proc_event::fork_proc_event, parent_pid);

I get the following error:

error: expected type-specifier
error: expected `,'
error: expected `)' before ',' token

How should the offsetof() line look like?

Víctor Fernández
  • 1,754
  • 1
  • 16
  • 15

2 Answers2

5

It may help to think of how an implementation of an offsetof macro might go. Here's one example:

#define offsetof(TYPE, MEMBER) \
    ((uintptr_t)&(((TYPE*)0)->MEMBER))

In other words, using 0 as a pointer to the type you're interested in, and simply taking the address of the struct field...

So if you wanted the offset of parent_pid relative to fork (which is how I initially parsed your question):

((char*)&((struct proc_event*)0)->event_data.fork.parent_pid) - ((char*)&((struct proc_event*)0)->event_data.fork)

On second reading it sounds like you might just want the offset of parent_pid relative to the start of struct proc_event. Adapting the example above that would be:

((uintptr_t)&((struct proc_event*)0)->event_data.fork.parent_pid)
asveikau
  • 39,039
  • 2
  • 53
  • 68
  • Thanks, but: error: 'struct proc_event' has no member named 'fork' – Víctor Fernández Jul 26 '12 at 07:20
  • @VíctorFernández - Yeah I didn't notice `event_data` initially. Please refresh. I am editing the answer with additional stuff as well. – asveikau Jul 26 '12 at 07:21
  • Pointer sizes (apart from certain function pointers) are the same. Casting the pointer to char* or uintptr_t without the need for dereferencing it only makes the code harder to read IMO. Is there a reason for that? – Tamás Szelei Jul 26 '12 at 07:23
  • @fish - I cast to `char *` to make the subtraction work in byte units rather than units of the pointer types being taken. (Without the casts the subtraction wouldn't even have the same pointer types as operands, which is madness!) I cast to `uintptr_t` because `&foo->member` is a pointer type and not an integer - again, when you're looking for offsets, not having this cast is not sane. – asveikau Jul 26 '12 at 07:26
  • @fish: The cast to `char *` means you can reliably calculate the offset for a struct member. Without a cast you would be performing pointer arithmetic on the wrong types and getting the wrong results. – dreamlax Jul 26 '12 at 07:31
  • Ah I see, I knew I was missing something. Thanks. – Tamás Szelei Jul 26 '12 at 07:40
4

I don't exactly understand the need for all those hacks, when all you have to do is give a name to your nested union type. Any name, just to be able to refer to it in C++ code

struct proc_event {
    ...
    union whatever {
        ...
        struct fork_proc_event {
            __kernel_pid_t parent_pid;
            ...
        } fork;
        ...
    } event_data;
    ...
};

Then you'll be able to refer to it as proc_event::whatever::fork_proc_event in offsetof in C++ code

size_t off = offsetof(proc_event::whatever::fork_proc_event, parent_pid);

If you are interested in offset of parent_pid from the beginning of proc_event, you can do

size_t off = offsetof(proc_event, event_data.fork.parent_pid);

If you cannot change the declaration, you can calculate the offset of parent_pid inside fork_proc_event by doing

size_t off = 
  offsetof(proc_event, event_data.fork.parent_pid) - 
  offsetof(proc_event, event_data.fork);

(Although I can't say right away whether the last two are formally legal examples of offsetof usage, they will normally work in practice without any problems.)

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765