2

I have a function which has a uint32_t * as its argument which actually points to a 64-bit value. I want to have a macro around it which accepts uint64_t as input.

Can the macro expand differently based on its input argument type?

The macro below works fine for my case; however, it's not efficient:

void func(uint32_t* a);

#define func_wrapper(a) do { \
    uint64_t aa = a; func((uint32_t*) &aa); \
} while(0)

For example, case 1 is not as efficient as case 3 below.

uint64_t x = 12;
func_wrapper(x)        // case 1
func_wrapper(12)       // case 2
func((uint32_t*) &x);  // case 3

Is there a way to define a macro which expands to

#define func_wrapper(a) { \
    func((uint32_t*) &(a)); \
}

when the argument is not a literal and expands to

#define func_wrapper(a) { \
    uint64_t aa = a; func((uint32_t*) &(aa)); \
}

when it is?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Nima
  • 23
  • 5
  • I think you are asking for polymorphism. – Yunnosch Nov 10 '22 at 16:44
  • Standard C doesn't have a way to process types dynamically. GCC has a `typeof` extension, see https://gcc.gnu.org/onlinedocs/gcc/extensions-to-the-c-language-family/referring-to-a-type-with-typeof.html. You may also be able to use generic functions. – Barmar Nov 10 '22 at 16:44
  • 4
    “I have a function which has a uint32_t* as it's argument which actually points to a 64 bit value” is irrelevant to the question and is almost certainly bad design. It is irrelevant because the problem you ask to solve is passing `&x` for `foo_wrapper(x)` and `& (uint64_t) {12}` for `foo_wrapper(12)`, and this is unrelated to the function call. The C standard does not provide any support for doing that as part of macro replacement. The `_Generic` operator might be used to do what you want, if the macro argument is always either a `uint64_t` variable or an `int` literal. – Eric Postpischil Nov 10 '22 at 16:48
  • 5
    It is bad design because accessing a `uint64_t` through a `uint32_t` is not defined by the C standard, and there is almost certainly a better and defined way to do whatever `func` is doing. – Eric Postpischil Nov 10 '22 at 16:49

1 Answers1

1

C macros are handled entirely during a preprocessing stage that lacks type information, and cannot expand differently based on language concepts like types. The C language provides very few tools for polymorphism, which seems to be what you are actually asking for.

It's also worth saying that accessing a value with effective type of uint64_t through a pointer of type uint32_t* breaks C's type-aliasing rules. This is a very bad idea and can lead to all sorts of undefined behavior, especially in the presence of high levels of compiler optimization.

Now on to the underlying assumption in your question, I'd like to address this statement:

For example, case 1 is not as efficient as case 3

Here is your code after macro expansion, where I've also fixed the type-aliasing violation:

void func(uint64_t* a);

uint64_t x = 12;
uint64_t aa = x; func((uint64_t*) &aa); // case 1
func((uint64_t*) &x);                   // case 3

You should not assume that case 3 is more efficient at runtime than case 1. C optimizers are capable of some very aggressive optimization, and the number of instructions/cycles in the final optimized executable has only a weak correlation with the number of statements in the C language. In this example, if the compiler can prove that func does not modify its argument, then it can apply copy propagation and remove the aa temporary variable entirely. Assuming the property is true, you can potentially help the analysis do this by declaring func with a const qualifier, ie:

void func(const uint64_t* a);

However even in the absence of any optimization whatsoever, it's unlikely you'd be able to measure a real performance difference between any of these on a modern superscalar processor.

Dan Bonachea
  • 2,408
  • 5
  • 16
  • 31
  • 1
    Thanks. I'm using O2 when compiling and getting slightly different output assembly (code size becomes 2 words bigger in case 1). Adding the const in the declaration didn't change it. But as you said it is very negligible, I'm going to ignore it. – Nima Nov 10 '22 at 18:19