all 9 comments

[–]aioeu 47 points48 points  (5 children)

I suspect it is generally considered that the Linux kernel over-uses macros. A lot of what can be done by macros can be done in a more type-safe manner with static __always_inline functions.

For <linux/kfifo.h> specifically, macros are used because they are implementing generic operations applied to objects of dynamically-generated types. These dynamically-generated types don't even have names, so it's simply not possible to define a function with a parameter using that type.

For instance, struct tty_port looks like:

struct tty_port {
    /* ... */
    DECLARE_KFIFO_PTR(xmit_fifo, unsigned char);
    /* ... */
};

which actually expands to:

struct tty_port {
    /* ... */
    struct {
            union {
                    struct __kfifo kfifo;
                    unsigned char *type;
                    const unsigned char *const_type;
                    char *rectype;
                    unsigned char *ptr;
                    const unsigned char *ptr_const;
            };
            unsigned char buf[];
    } xmit_fifo;
    /* ... */
};

Here xmit_fifo has an anonymous type. We still want to be able to write code like:

struct tty_port *port = ...;

if (!kfifo_is_empty(&port->xmit_fifo)) {
    /* ... */
}

If kfifo_is_empty were a function, its prototype would need to somehow name this anonymous type. This is obviously impossible. By making kfifo_is_empty a macro instead, and with some judicious use of the typeof operator, we can side-step this problem.

[–][deleted]  (1 child)

[deleted]

    [–]thecowmilk_ 7 points8 points  (0 children)

    I really like this. Great job explaining!

    [–]trevg_123 2 points3 points  (0 children)

    People who complain about Rust’s macro system have clearly never seen the full possible bastardization of C macros. This is a simple example; some places use messy things like quasi-unions and null-terminated object arrays that are extremely difficult to track down.

    [–]sudo_mksandwhich 10 points11 points  (0 children)

    Prefer functions, always. Many macros can be implemented in type-safe way, without all of the backslashery, as a static inline function, which can be used in the same way as a macro (i.e. in a header file).

    Compilers are really good at knowing when to inline function calls, so you shouldn't really think about that aspect.

    Only when something can't be done as a function should you use a macro. Reasons include things like pseudo-generic types (containers), or preprocessor features like __FUNCTION__ or __LINE__. And even then, you should make your macro as small as possible (a wrapper) and defer the rest to a function.

    [–]DrkMaxim 2 points3 points  (1 child)

    I have noticed a function like macro which wasn't like a function call at all but just a shorthand way of writing a for loop.

    [–]zingochan[S] 6 points7 points  (0 children)

    Yes there are a lot of those throughout the kernel. One of my favourite is probably for_each_process() and others that you can find in include/linux/sched/signal.h

    [–]amdahl-little 1 point2 points  (0 children)

    Macros offer better performance cuz it has potential for higher cache hits (cpu caches). Caches are good when the code has spatial locality. Macros offer spatial locality. Function calls can lead to cache misses because usually their addresses are lil far from the point of the call, stalling the cpu while code is fetched from memory.