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 )
{
-a-a-a-athread_local EnumProc *t_pCallback;
-a-a-a-at_pCallback = &enumProc;
-a-a-a-areturn EnumSystemLocalesEx( +[]( LPWSTR locName, DWORD dwFlags, LPARAM lParam )
-a-a-a-a-a-a-a {
-a-a-a-a-a-a-a-a-a-a-a return (*t_pCallback)( locName, dwFlags, lParam );
-a-a-a-a-a-a-a }, dwFlags, lParam, lpReserved );
}
With the above concept you can use these C-APIs like a C++ API.
It is ugly, so it should be used as a desperate last resort only.
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.
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
Am 15.03.2026 um 17:11 schrieb Andrey Tarasevich:
It is ugly, so it should be-a 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.
Your idea is based on introducing a "global" state, which immediately
makes is non-reentrant.
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.
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.
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:
-a-a-a-aEnumSystemLocalesEx( [&strLocs]( LPWSTR locName, DWORD )
-a-a-a-a-a-a-a {
-a-a-a-a-a-a-a-a-a-a-a strLocs.emplace_back( locName );
-a-a-a-a-a-a-a-a-a-a-a return TRUE;
-a-a-a-a-a-a-a }, 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.
No argument here.
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.
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.
template<typename Callback>} ctx( &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 )
{
-a-a-a-astruct context
-a-a-a-a{
-a-a-a-a-a-a-a Callback *pCallback;
-a-a-a-a-a-a-a exception_ptr exc;
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 65 |
| Nodes: | 6 (0 / 6) |
| Uptime: | 08:06:22 |
| Calls: | 862 |
| Files: | 1,311 |
| D/L today: |
1 files (1,366K bytes) |
| Messages: | 264,936 |