Date: Tue, 28 Apr 2026 12:26:20 +0100
On Tue, 28 Apr 2026 at 10:18, Jonathan Wakely <cxx_at_[hidden]> wrote:
>
>
> 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)...);
> };
>
Here's a corrected version without the typos, and with always_inline added:
auto const slow_print = []<typename... T> [[gnu::always_inline]] (T&&... t)
{
using namespace std::chrono;
std::this_thread::sleep_for(1s);
return std::printf(std::forward<T>(t)...);
};
This inlines completely so that the call to printf is done directly in
main, even without optimization. This generates considerably better code
than your interceptor which isn't inlined even at -O3.
>
> 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?
>
>
>
>
> 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)...);
> };
>
Here's a corrected version without the typos, and with always_inline added:
auto const slow_print = []<typename... T> [[gnu::always_inline]] (T&&... t)
{
using namespace std::chrono;
std::this_thread::sleep_for(1s);
return std::printf(std::forward<T>(t)...);
};
This inlines completely so that the call to printf is done directly in
main, even without optimization. This generates considerably better code
than your interceptor which isn't inlined even at -O3.
>
> 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?
>
>
Received on 2026-04-28 11:26:37
