简体   繁体   中英

C++ get struct for an entry

I encounter the following code when I am learning data structures and algorithms in C++. It is from https://github.com/xtaci/algorithms/blob/master/include/double_linked_list.h line 194 to 206.

/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 */
#ifndef _MSC_VER
#define list_entry(ptr, type, member) \
    (reinterpret_cast<type *>((char *)(ptr)-(char *)(&(reinterpret_cast<type *>(1)->member))+1))
#else
#define list_entry(ptr, ptrtype, member) \
    (reinterpret_cast<ptrtype>((char *)(ptr)-(char *)(&(reinterpret_cast<ptrtype>(1)->member))+1))
#endif

The comment block says the functionality of this Marco is to get the struct for this entry. What puzzles me is the use of

reinterpret_cast<type *>(1)->member

What does it mean by converting 1 to (type *) and access its member?

* Thanks for the help in advance. If any part is not clear, I will edit it quickly. *

* More information * :

This Marco is used in the code to define new Marco

#define list_for_each_entry_safe(pos, n, head, member)          \
    for (pos = list_entry((head)->next, typeof(*pos), member),  \
        n = list_entry(pos->member.next, typeof(*pos), member); \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))

And an example usage of the new Marco is from https://github.com/xtaci/algorithms/blob/master/include/graph_defs.h line 45 to 51 as part of the destructor.

        struct Adjacent {
            struct Vertex v;
            int32_t color;  // color for searching
            int32_t d;  // discover time
            int32_t f;  // finish time
            struct list_head v_head;  // vertex list header
            struct list_head a_node; 
            uint32_t num_neigh;     // num of neighbours

            Adjacent(uint32_t id):v(id) {
                INIT_LIST_HEAD(&v_head);
                num_neigh = 0;
            }

            ~Adjacent() {
                Vertex * v, *vn;
                list_for_each_entry_safe(v, vn, &v_head, v_node){
                    list_del(&v->v_node);
                    delete v;
                }
            }
            ......
&(reinterpret_cast<type *>(1)->member)

this statement if something like Macro offsetof , using to get the offset address of a member in the struct.

the tricky reinterpret_cast<type *>(1) tells the compiler there is a type * pointer whose address is 0x1 , then &(reinterpret_cast<type *>(1)->member) gets the offset address of the member plus the original 0x1

I've used code below to verify it.

struct list_head {
    struct list_head *next, *prev;
};

struct Vertex {
    int x;
    int y;
    list_head v_node;
};

int main()
{
    Vertex *v = (Vertex *) malloc(sizeof(Vertex));
    printf("%p", &(reinterpret_cast<Vertex *>(1)->v_node));
    return 0;
}

it prints 0x9 , exactly 2*sizeof(int) + 1

Personally I think using 1 instead of 0 can avoid compiler treats it as an invalid NULL pointer. So in the macro, the 1 is plus again in the tail.

The macro list_entry can be used like this

int main()
{
    Vertex *v = (Vertex *) malloc(sizeof(Vertex));
    Vertex *w = list_entry(&(v->v_node), Vertex, v_node);
    printf("%p\n%p\n", v, w);
    return 0;
}

when we only have the pointer to list_head we can use list_entry to get the outer struct. In the above code, v and w points to the same area.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM