Is there a way to create qualifier-preserving functions in C?

101 Views Asked by At

I have a function that accepts a pointer but does not modify the pointed data, but returns the same pointer or a derivative thereof. I could make the pointer const:

const void *some_read_only_operation(const void *ptr) {
    // Do work with ptr
    return ptr;
}

However, this means if I call the function, I will not be able to manipulate the result without a cast, even if the pointer I passed was not const. On the contrary, if I make the pointers and return type in the function non const then I will need a cast to pass any pointer to the function that is const. And I will get a non const pointer back so I have broken const safety.

Is there any way to create a function in C that repects const correctness (const in means const out and non const in means non const out) without having to write 2 separate functions? If not, how close can I get?

Note: This seems to be the type of question that is likely to have been asked before. I tried to search for duplicates using keywords like const function return or qualifier preserving function but I could not find any.

2

There are 2 best solutions below

6
On BEST ANSWER

You can use _Generic for this:

/**
 * Cast a pointer to `void *` while keeping qualifiers, e.g.
 *
 *     struct foo *   => void *
 *     const char *   => const void *
 *     volatile int * => volatile void *
 */
#define VOID_CONVERT(ptr) (1 ? (ptr) : (void *)(ptr))

const void *const_fn(const void *ptr) {
    return ptr;
}

void *non_const_fn(void *ptr) {
    // Re-use the const implementation
    return (void *)const_fn(ptr);
}

#define fn(ptr) _Generic(VOID_CONVERT(ptr), \
    void *: non_const_fn((void *)(ptr)), \
    const void *: const_fn((const void *)(ptr)))
2
On

You can return an offset instead of a pointer:

#include <stddef.h>


//  Function definition:
ptrdiff_t SomeReadOnlyOperation(const void * const Original)
{
    const void *Derived = Original;
    // Work with Derived.
    return (const char *) Derived - (const char *) Original;
}


…

    //  Function use:
    char *MyPointer = …;
    MyPointer += SomeReadOnlyOperation(MyPointer);

You can use units other than char for the offset if that is suitable for your application.