• If you need a C-callback

    From Bonita Montero@3:633/10 to All on Sun Mar 15 15:32:03 2026
    If you have an API that requires a callback and you want to have that
    this callback can access the outer scope like a lambda through [%]
    I'm doing this:

    template<typename EnumProc>
    requires std::is_nothrow_invocable_r_v<BOOL, EnumProc, LPWSTR, DWORD,
    LPARAM>
    BOOL EnumSystemLocalesEx( EnumProc enumProc, DWORD dwFlags, LPARAM
    lParam, LPVOID lpReserved )
    {
    thread_local EnumProc *t_pCallback;
    t_pCallback = &enumProc;
    return EnumSystemLocalesEx( +[]( LPWSTR locName, DWORD dwFlags, LPARAM lParam )
    {
    return (*t_pCallback)( locName, dwFlags, lParam );
    }, dwFlags, lParam, lpReserved );
    }

    With the above concept you can use these C-APIs like a C++ API.


    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Sun Mar 15 16:41:35 2026
    Even more flexible:

    template<typename EnumProc>
    requires std::is_invocable_r_v<BOOL, EnumProc, LPWSTR, DWORD, LPARAM>
    BOOL EnumSystemLocalesEx( EnumProc &&enumProc, DWORD dwFlags, LPARAM
    lParam, LPVOID lpReserved )
    {
    thread_local EnumProc *t_pCallback;
    thread_local exception_ptr t_excPtr;
    t_pCallback = &enumProc;
    auto cEnumProc = (LOCALE_ENUMPROCEX)[]( LPWSTR locName, DWORD dwFlags, LPARAM lParam )
    {
    try
    {
    return (*t_pCallback)( locName, dwFlags, lParam );
    }
    catch( ... )
    {
    t_excPtr = current_exception();
    return FALSE;
    }
    };
    BOOL ret = EnumSystemLocalesEx( cEnumProc, dwFlags, lParam, lpReserved );
    if( exception_ptr excPtr = move( t_excPtr ); excPtr )
    rethrow_exception( excPtr );
    return ret;
    }

    Now the callback is allowed to throw exceptions across the callback
    boundary.

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Sun Mar 15 09:11:52 2026
    On Sun 3/15/2026 7:32 AM, Bonita Montero wrote:
    If you have an API that requires a callback and you want to have that
    this callback can access the outer scope like a lambda through [%]
    I'm doing this:

    template<typename EnumProc>
    requires std::is_nothrow_invocable_r_v<BOOL, EnumProc, LPWSTR, DWORD, LPARAM>
    BOOL EnumSystemLocalesEx( EnumProc enumProc, DWORD dwFlags, LPARAM
    lParam, LPVOID lpReserved )
    {
    ˙˙˙˙thread_local EnumProc *t_pCallback;
    ˙˙˙˙t_pCallback = &enumProc;
    ˙˙˙˙return EnumSystemLocalesEx( +[]( LPWSTR locName, DWORD dwFlags,
    LPARAM lParam )
    ˙˙˙˙˙˙˙ {
    ˙˙˙˙˙˙˙˙˙˙˙ return (*t_pCallback)( locName, dwFlags, lParam );
    ˙˙˙˙˙˙˙ }, dwFlags, lParam, lpReserved );
    }

    With the above concept you can use these C-APIs like a C++ API.

    Firstly, using a [pseudo-]global variable to pass additional context to "context-unfriendly" functions (i.e. a `static` or `thread_local`
    variable) is an old and trivial technique. It is ugly, so it should be
    used as a desperate last resort only. Declaring that [pseudo-]global
    variable locally and "templatizing" the whole thing does not really
    redeem it.

    Which brings us to "secondly".

    Secondly, it is not clear why you used `EnumSystemLocalesEx` in this
    example. `EnumSystemLocalesEx` happens to be a context-friendly API,
    which does not require one to use this workaround at all. The entire
    WinAPI is designed to be context-friendly from day one. So, just use
    'lParam' to pass context and achieve the same thing. Yes, it will
    require a bit of an extra effort, since the client might want to pass
    its own 'lParam' value, but it is not that difficult to handle nicely
    and is definitely worth it

    template<typename EnumProc>
    requires std::is_nothrow_invocable_r_v<BOOL, EnumProc, LPWSTR,
    DWORD, LPARAM>
    BOOL MyEnumSystemLocalesEx(EnumProc enumProc, DWORD dwFlags, LPARAM
    lParam, LPVOID lpReserved)
    {
    using Lpp = std::pair<EnumProc &, LPARAM>;
    const Lpp lp = { enumProc, lParam };
    return EnumSystemLocalesEx(+[](LPWSTR locName, DWORD dwFlags,
    LPARAM lParam)
    {
    const Lpp *lp = (Lpp *) lParam;
    return lp->first(locName, dwFlags, lp->second);
    }, dwFlags, (LPARAM) &lp, lpReserved);
    }

    Thirdly, relying on partial ordering alone to avoid infinite recursion
    is a recipe for unnecessary problems.

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Sun Mar 15 17:32:05 2026
    Am 15.03.2026 um 17:11 schrieb Andrey Tarasevich:

    It is ugly, so it should be used as a desperate last resort only.

    I don't understand why this should be ugly.
    I think it's idea is basically very elegant.

    Secondly, it is not clear why you used `EnumSystemLocalesEx` in this example. `EnumSystemLocalesEx` happens to be a context-friendly API,
    which does not require one to use this workaround at all.

    But not in a C++-sense. You can't easily use a [&] lambda with that.

    The entire WinAPI is designed to be context-friendly from day one.

    Not C++ developer friendly.

    Yes, it will require a bit of an extra effort, since the client might
    want to pass its own 'lParam' value, but it is not that difficult to
    handle nicely and is definitely worth it

    With callback'd APIs you use more often this idea becomes more
    intuitive to use.



    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Sun Mar 15 10:22:43 2026
    On Sun 3/15/2026 9:32 AM, Bonita Montero wrote:
    Am 15.03.2026 um 17:11 schrieb Andrey Tarasevich:

    It is ugly, so it should be˙ used as a desperate last resort only.

    I don't understand why this should be ugly.
    I think it's idea is basically very elegant.

    Your idea is based on introducing a "global" state, which immediately
    makes is non-reentrant. Declaring the state locally (as local static or
    tread local) does shave some layers off the that non-reentrancy, but it
    cannot fix all of its problems. It just puts more layers of lipstick on
    the same pig.

    Secondly, it is not clear why you used `EnumSystemLocalesEx` in this
    example. `EnumSystemLocalesEx` happens to be a context-friendly API,
    which does not require one to use this workaround at all.

    But not in a C++-sense. You can't easily use a [&] lambda with that.

    No, you can't use it directly.

    But if the API provides you with the opportunity to pass through a pointer-sized user-supplied parameter (`LPARAM lParam` in this case),
    the problem immediately becomes solvable - just rely on this `lParam` to
    pass external context instead of relying on a "global" state. It is
    basically the same thing as what you are doing, but without relying on a `thread_local` variable.

    Does it qualify as "easily"? Maybe not, but as my variant of the code
    clearly shows, it is pretty much equivalent in complexity to your
    variant. And without the dangers and drawbacks of non-reentrancy.

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Sun Mar 15 18:45:05 2026
    Am 15.03.2026 um 18:22 schrieb Andrey Tarasevich:

    Your idea is based on introducing a "global" state, which immediately
    makes is non-reentrant.

    Do you think _this_ function needs to be reentrant ?

    No, you can't use it directly.

    I can, through my C++-calback:

    EnumSystemLocalesEx( [&strLocs]( LPWSTR locName, DWORD )
    {
    strLocs.emplace_back( locName );
    return TRUE;
    }, 0 );


    But if the API provides you with the opportunity to pass through a pointer-sized user-supplied parameter (`LPARAM lParam` in this case),
    the problem immediately becomes solvable - just rely on this `lParam`
    to pass external context instead of relying on a "global" state.
    It is basically the same thing as what you are doing, but without
    relying on a `thread_local` variable.

    I want to show this idea also for cases where you don't have a context
    -pointer as with Posix-APIs (signal(), sigaction(), ftw(), nftw(), Posix -timers and Posix AIO).

    Does it qualify as "easily"? Maybe not, but as my variant of the code clearly shows, it is pretty much equivalent in complexity to your
    variant. And without the dangers and drawbacks of non-reentrancy.

    Do you really think that someone in my callback will use
    EnumSystemLocales() another time ? You You have a tendency
    to imagine problems where there are none.


    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tristan Wibberley@3:633/10 to All on Sun Mar 15 19:37:44 2026
    On 15/03/2026 17:45, Bonita Montero wrote:

    Do you really think that someone in my callback will use
    EnumSystemLocales() another time ? You You have a tendency
    to imagine problems where there are none.

    That's guaranteed by sod's law (the British name for Murphy's Law).

    But you can maintain a stack-like chain easily enough using a scope
    guard, and as long as thread_local works for all types of context it is
    pretty safe. Of course, I read that Windows Fibers and BSD uctx do not exemplify that candidate assumption.

    An extra layer of indirection is required to accommodate all sorts of context-locality (since "thread" clearly no longer means what it used to).

    --
    Tristan Wibberley

    The message body is Copyright (C) 2026 Tristan Wibberley except
    citations and quotations noted. All Rights Reserved except that you may,
    of course, cite it academically giving credit to me, distribute it
    verbatim as part of a usenet system or its archives, and use it to
    promote my greatness and general superiority without misrepresentation
    of my opinions other than my opinion of my greatness and general
    superiority which you _may_ misrepresent. You definitely MAY NOT train
    any production AI system with it but you may train experimental AI that
    will only be used for evaluation of the AI methods it implements.


    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Sun Mar 15 12:50:05 2026
    On Sun 3/15/2026 10:45 AM, Bonita Montero wrote:
    Your idea is based on introducing a "global" state, which immediately
    makes is non-reentrant.

    Do you think _this_ function needs to be reentrant ?

    No, you can't use it directly.

    I can, through my C++-calback:

    ˙˙˙˙EnumSystemLocalesEx( [&strLocs]( LPWSTR locName, DWORD )
    ˙˙˙˙˙˙˙ {
    ˙˙˙˙˙˙˙˙˙˙˙ strLocs.emplace_back( locName );
    ˙˙˙˙˙˙˙˙˙˙˙ return TRUE;
    ˙˙˙˙˙˙˙ }, 0 );

    Certainly. And you can do the same using my version. Except that my
    version is as simple as yours and yet perfectly reentrant.

    But if the API provides you with the opportunity to pass through a
    pointer-sized user-supplied parameter (`LPARAM lParam` in this case),
    the problem immediately becomes solvable - just rely on this `lParam`
    to pass external context instead of relying on a "global" state.
    It is basically the same thing as what you are doing, but without
    relying on a `thread_local` variable.

    I want to show this idea also for cases where you don't have a context -pointer as with Posix-APIs (signal(), sigaction(), ftw(), nftw(), Posix -timers and Posix AIO).

    No argument here. When you don't have a context pointer, then the only solution is using something like what you proposed. That's a valid
    excuse to sigh, swear and reluctantly resort to an approach based on a "global" variable.

    Does it qualify as "easily"? Maybe not, but as my variant of the code
    clearly shows, it is pretty much equivalent in complexity to your
    variant. And without the dangers and drawbacks of non-reentrancy.

    Do you really think that someone in my callback will use
    EnumSystemLocales() another time ? You You have a tendency
    to imagine problems where there are none.

    Firstly, once the problem is there, the less likely it is to occur in practice, the more difficult it is going to be to debug and the more devastating its effects are going to be.

    Secondly, it is not even about "problems". A solution that emits
    unnecessary "global" variables is something that won't sit well with a competent developer. Not because of the potential "problems", but out of
    sheer inelegance of it.

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Mon Mar 16 06:01:16 2026
    Am 15.03.2026 um 20:50 schrieb Andrey Tarasevich:

    No argument here.

    You're a real idiot.
    This is just an argument because I simply wanted to show how it works.

    Firstly, once the problem is there, the less likely it is to occur in practice, the more difficult it is going to be to debug and the more devastating its effects are going to be.

    No, that problem doesn't exist here. And the problem is not difficult
    to debug at all.
    And if, in the rare case, it should occur, you simply use a thread
    -local pointer to the state, which is then copied in the primary C
    callback and reset after the C++ callback so that the C++ callback
    can also use the primary function recursively. But overall, this is
    very rarely necessary, which is hy your objection at this point is
    simply silly.

    A solution that emits unnecessary "global" variables is something
    that won't sit well with a competent developer. Not because of
    the potential "problems", but out of sheer inelegance of it.

    As I already mentioned, you have a tendency to see problems that
    almost never occur.

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Mon Mar 16 06:30:08 2026
    Here, an example that can be called recursively:

    template<typename Callback>
    requires std::is_invocable_r_v<int, Callback, const char *, struct stat
    *, int, FTW *>
    int nftw( Callback &&callback, const char *path, int fdLimit, int flags )
    {
    struct context
    {
    Callback *pCallback;
    exception_ptr exc;
    } ctx;
    thread_local context *t_pCtx;
    t_pCtx = &ctx;
    int ret = nftw( path, +[]( const char *pth, const struct stat *stt, int
    tpFlag, FTW *ftw ) -> int
    {
    context &ctx = *t_pCtx;
    defer restore( [&] { t_pCtx = &ctx; } );
    try
    {
    return (*ctx.pCallback)( pth, stt, tpFlag, ftw );
    }
    catch( ... )
    {
    ctx.exc = current_exception();
    return 1;
    }
    }, fdLimit, flags );
    if( ctx.exc )
    rethrow_exception( ctx.exc );
    return ret;
    }

    Is that good enough for your delusional demands ?

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Mon Mar 16 06:40:34 2026
    Am 16.03.2026 um 06:30 schrieb Bonita Montero:

    template<typename Callback>
    requires std::is_invocable_r_v<int, Callback, const char *, struct stat
    *, int, FTW *>
    int nftw( Callback &&callback, const char *path, int fdLimit, int flags )
    {
    ˙˙˙˙struct context
    ˙˙˙˙{
    ˙˙˙˙˙˙˙ Callback *pCallback;
    ˙˙˙˙˙˙˙ exception_ptr exc;
    } ctx( &callback );

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)