On Tue, 28 Apr 2026 at 07:35, Jonathan Wakely <cxx@kayari.org> wrote:


On Mon, 27 Apr 2026, 22:43 Frederick Virchanza Gotham via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:
Consider intercepting a call to a function such as 'printf'. Instead
of parsing all the percentage signs individually, you just want to
forward all the arguments on. So the interceptor should be totally
agnostic to the signature of the target function -- totally oblivious
to its return type and parameter types.

https://wg21.link/p2826 seems better.

Or not ... I didn't read past the first paragraph of your mail.

I read the rest of it now, and unsurprisingly, I don't like it




Like this:

#include <cstdio>    // printf
#include <chrono>    // seconds
#include <thread>    // sleep_for

[[interceptor]] void MyInterceptor(void) noexcept
{
  // Sleep for 1 second before jumping to 'printf'

  std::this_thread::sleep_for( std::chrono::seconds(1) );
  goto -> std::printf;

The interceptor is declared as void, but it actually returns whatever the 'goto' statement returns? Why not declare it as auto or decltype(auto)?
 
}

int main(int const argc, char **const argv)
{
  auto const fp = (decltype(std::printf)*)&MyInterceptor;

This is horrible. Why would I ever want to write this? You have to name std::printf inside the interceptor, *and* cast its type to std::printf when you use it? That's horrible.

How do you use that for an overloaded function like std::pow instead of one like std::printf?

Why is this horribleness better than just wrapping std::printf in a lambda?

auto const slow_print = []<typename... T>(T&&... t) {
  std::this_thread::sleep(1s);
  return std::printf(std::forward<T>(args)...);
};

OK, the syntax for writing the lambda is a bit ugly, but at the call site there's no cast to decltype(std::printf)* and it handles overloading properly, and you can put [[gnu::format(printf, 1, 2)]] which won't work on your interceptor because it has no parameters.

What's the benefit of your interceptor?

 

  fp("My favourite number is %i,\nand my favourite word is %s.\n",
     argc,
     argv[ argc - 1 ]);
}

And well here it is now working up on GodBolt:

    https://godbolt.org/z/6qqTrq6Wj

I want to explain to compiler vendors just how easy it is to implement
this new feature. Here's what you do:

Step 1 - When the compile encounters a function with the attribute
[[interceptor]], create a second function declaration with the same
name but prepended with "__core_", and change the second declaration's
return type from 'void' to 'void(*)(void)'. Compile the body of the
function into the second declaration. (The original declaration will
become a thunk).

Step 2 - Inside the body of the interceptor function, the 'return'
statement should instead be written as 'goto ->'.

Step 3 - Emit a thunk with the name of the original function
declaration and give it assembler as follows:

    push_all_arguments
    call __core_Func
    pop_all_arguments
    jmp target_function

It really is that simple. And because the original function name is
mangled before "__core_" is prepended, it also works with templates:

    https://godbolt.org/z/8bYja9zfn

In the above GodBolt, the original function name is mangled as
"_Z13MyInterceptorILi0EEvv", and so then the core becomes
"__core__Z13MyInterceptorILi0EEvv". You can have multiple template
instantiations:

    https://godbolt.org/z/exxz5vcGo

Easy peasy. Here's what I've got so far for the GNU compiler patch:

    https://github.com/healytpk/gcc-thomas-healy/commit/tag_interceptor

It's not perfect but it's a really good proof of concept.

Note that for any given target CPU instruction set, such as x86_64,
the interceptor will be able to intercept all calling conventions and
all CPU configurations. So for example on x86_64, it can intercept
both System V and MSabi, and also it can intercept SSE processors as
well as AVX-512 processors (it checks at runtime to see what the CPU
is capable of).

Next I will code it for x86_32, and it will work for stdcall, cdecl,
thiscall and so on. After that will be aarch64, followed by 32-Bit
ARM.
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals