C++ Logo

std-proposals

Advanced search

Re: [std-proposals] interceptor functions (tested and working on x86_64)

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Tue, 28 Apr 2026 10:18:44 +0100
On Tue, 28 Apr 2026 at 07:35, Jonathan Wakely <cxx_at_[hidden]> wrote:

>
>
> On Mon, 27 Apr 2026, 22:43 Frederick Virchanza Gotham via Std-Proposals, <
> std-proposals_at_[hidden]> 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_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>

Received on 2026-04-28 09:19:03