Convert function pointer to std::uintptr_t at compile time

Viewed 562

I'd like to convert a constant expression function pointer to a std::uintptr_t at compile time. How can I do this?

Here's a minimal example:

#include <cstdint>
void fn() {}
int main(int argc, char** argv) {
  constexpr void* ptr = (void *) fn;
  constexpr std::uintptr_t idx = reinterpret_cast<std::uintptr_t>(fn);
  return 0;
}

GCC 7/8/9 are currently giving me the error "conversion from pointer type to arithmetic type std::uintptr_t in a constant expression." However, my understanding was that std::uintptr_t should be able to hold any pointer type, meaning this should be able to be done in a constant expression.

Background

To give a bit of background as to why I need this, I want to (1) retrieve the address of a function pointer at compile time, (2) convert it to a std::uintptr_t, then (3) pass it as a template parameter so it can be baked into a function at compile time.

This is meant to be part of an RPC engine, similar to this code, which produces a very similar error:

#include <cstdio>
#include <cstdint>

template <std::uintptr_t FnPtr, typename Fn>
void fn_handler() {
  ((Fn *) FnPtr)();
}

int main(int argc, char** argv) {
  auto lel = []() {
    printf("Hi, fam!\n");
  };
  // Note that +lel is an implement 0+lel, converting
  // the lambda to a fn ptr.
  constexpr void* ptr = reinterpret_cast<void*>(+lel);
  constexpr std::uintptr_t idx = reinterpret_cast<std::uintptr_t>(ptr);

  fn_handler<idx, decltype(lel)>();
  return 0;
}
1 Answers

However, my understanding was that std::uintptr_t should be able to hold any pointer type, meaning this should be able to be done in a constant expression.

Sure. But reinterpret_cast is never allowed in a constant expression. And since that's the only way to convert a pointer into an integer, that's not something you can do at compile time.

If you want to pass a function pointer as a template parameter, then just do that:

int func() {return 0;}

template<int (*pfn)()>
int fn_handler()
{
  return pfn();
}

...

fn_handler<&func>();

If you want to make the type of the function a template parameter, then pre-C++17, you can use the old trick like this:

template<typename Fn, Fn pfn>
decltype(auto) fn_handler()
{
  return pfn();
}

...

fn_handler<decltype(&func), &func>();

C++17 lets us just use auto:

template<auto pfn>
decltype(auto) fn_handler()
{
  return pfn();
}

...

fn_handler<&func>();
Related