Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Mutually exclusive execution for threads, OK with g++ but not MSVC

96 views
Skip to first unread message

Alf P. Steinbach

unread,
Apr 10, 2019, 4:09:54 PM4/10/19
to
Code at bottom.

My questions are:
* Why is it crashing with Visual C++?
* Is there a much simpler / efficient / whatever way to do this
(except using C++20 coroutines)?

Output with MinGW g++, with logging suppressed:


Starting.
Thread 3 iteration 0.
Thread 2 iteration 0.
Thread 3 iteration 1.
Thread 2 iteration 1.
Thread 3 iteration 2.
Thread 2 iteration 2.
Thread 3 iteration 3.
Thread 2 iteration 3.
Thread 3 iteration 4.
Thread 2 iteration 4.
Thread 3 iteration 5.
Thread 2 iteration 5.
Thread 3 iteration 6.
Thread 2 iteration 6.
Thread 3 iteration 7.
Thread 2 iteration 7.
Thread 3 iteration 8.
Thread 2 iteration 8.
Thread 3 iteration 9.
Thread 2 iteration 9.
Finished.


With Visual C++ 2019 there's no ordinary output, and the log output
differs from run to run, but typically


Starting.
Thread 12828 yielding.
Thread 4076 yielding.
Thread 12828 registering unlocker.
Thread 12828 entering wait state.
Thread 4076 releasing another thread.


The process exit code is then -1073740791, which Microsoft's errlook
utility tells me is hex 0xC0000409 and that that's an unknown code.
However, mr. Google leads to me to an SO question about it, where an
answer claims that it's STATUS_STACK_BUFFER_OVERRUN, i.e. not an error
code or HRESULT code, but a low level status code.

Which means that I can possibly find out by creating a Visual Studio
project and debugging, but debugging threads is tricky, so I ask first.


-----------------------------------------------------------------------
#include <cppx-core/all.hpp> //
https://github.com/alf-p-steinbach/cppx-core

namespace my
{
$use_cppx( is_empty );
using namespace cppx::basic_string_building; // "<<"-notation.

$use_std(
clog, endl,
exception,
move,
mutex,
queue,
string,
unique_lock
);
$use_std_namespace_names( this_thread );

void log( const string& s )
{
static mutex m;
$with( unique_lock<mutex>( m ) ) { clog << s << endl; }
}

class Mutually_exclusive_execution
{
using Lock = unique_lock<mutex>;

mutex m_q_access;
queue<Lock*> m_execution_locks_q;

static auto popped( queue<Lock*>& q )
-> Lock*
{
Lock* result = q.front();
q.pop();
return result;
}

void yield( const bool do_wait )
{
mutex m;
Lock execution_lock( m );

log( "Thread "s << this_thread::get_id() << " yielding." );
$with( Lock( m_q_access ) )
{
if( not is_empty( m_execution_locks_q ) )
{
log( "Thread "s << this_thread::get_id() << "
releasing another thread." );
popped( m_execution_locks_q )->unlock(); //
Another thread runs.
log( "Thread "s << this_thread::get_id() << "
release succeeded." );
}
if( do_wait )
{
try
{
log( "Thread "s << this_thread::get_id() << "
registering unlocker." );
m_execution_locks_q.emplace( &execution_lock );
}
catch( const exception& x )
{
log( "!"s << x.what() );
}
}
}

if( do_wait )
{
log( "Thread "s << this_thread::get_id() << " entering
wait state." );
//(Lock( m )); // Waits until m is unlocked by
another thread.
$with( Lock( m ) ) {}
}
log( "Thread "s << this_thread::get_id() << " resuming." );
}

public:
void yield() { yield( true ); }
void goodbye() { yield( false ); }
};

} // namespace my

namespace app
{
$use_cppx( up_to );
$use_std( cout, endl, thread );
$use_std_namespace_names( this_thread );

void run()
{
my::Mutually_exclusive_execution mee;
const auto f = [&]()
{
for( const int i: up_to( 10 ) )
{
mee.yield();
cout << "Thread " << this_thread::get_id() << "
iteration " << i << "." << endl;
}
mee.goodbye();
};

thread t1( f );
thread t2( f );

t1.join(); t2.join();
}
} // namespace app

#include <stdexcept> // std::exception
#include <stdlib.h> // EXIT_...
$use_std( exception, cerr, cout, endl );
auto main() -> int
{
try
{
cout << "Starting." << endl;
app::run();
cout << "Finished." << endl;
return EXIT_SUCCESS;
}
catch( const exception& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
-----------------------------------------------------------------------


Cheers!,

- Alf (perplexed, baffled, confounded, bewildered & for the time being
stumped)

Chris M. Thomasson

unread,
Apr 10, 2019, 4:40:39 PM4/10/19
to
On 4/10/2019 1:09 PM, Alf P. Steinbach wrote:
> Code at bottom.
>
> My questions are:
> * Why is it crashing with Visual C++?
> * Is there a much simpler / efficient / whatever way to do this
>   (except using C++20 coroutines)?
>
> Output with MinGW g++, with logging suppressed:
[...]
> - Alf (perplexed, baffled, confounded, bewildered & for the time being
> stumped)
>

Can you possibly reduce it down to something that does not require all
of the $... stuff? Something that just uses std primitives, perhaps? I
have to learn what those $ things mean "under the covers" in order to
help. Humm...

Paavo Helde

unread,
Apr 10, 2019, 4:44:47 PM4/10/19
to
On 10.04.2019 23:09, Alf P. Steinbach wrote:
> Code at bottom.
>
> My questions are:
> * Why is it crashing with Visual C++?
> * Is there a much simpler / efficient / whatever way to do this
> (except using C++20 coroutines)?

If you could translate that to C++ and explain what it is what this is
supposed to do I might be tempted to have a look.

As for now my only advice is to not turn too much attention to the
process exit code, its informational value is extremely low. A Windows
exception code from a SEH handler or "Stopped working" box would be
better, in my experience.


Alf P. Steinbach

unread,
Apr 10, 2019, 5:08:22 PM4/10/19
to
Done. It's just that I prefer coding up things with the macros, cause
it's less to write and less to read. Thanks.


#if 1
// include <cppx-core/all.hpp> //
https://github.com/alf-p-steinbach/cppx-core
#include <iostream>
#include <stdexcept>
#include <thread>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>
#endif

namespace my
{
#if 1 // The only overload used here; the cppx::is_empty also tackles
e.g. C strings, etc.
template< class Collection >
auto is_empty( const Collection& c )
-> bool
{ return c.empty(); }
#endif

#if 1 //using namespace cppx::basic_string_building; //
"<<"-notation.
// Optimization overloads omitted here.

using std::string;
using std::ostringstream;
using namespace std::string_literals;

template< class Type >
inline auto operator<<( string& s, const Type& value )
-> string&
{
ostringstream stream;
stream << value;
s += stream.str();
return s;
}

template< class Type >
inline auto operator<<( string&& s, Type const& value )
-> string&&
{ return move( operator<<( s, value ) ); }
#endif

#if 1 // `using` declarations.
// $use_std(
// clog, endl,
// exception,
// move,
// mutex,
// queue,
// string,
// unique_lock
// );
using std::clog;
using std::endl;
using std::exception;
using std::move;
using std::mutex;
using std::queue;
using std::string;
using std::unique_lock;
#endif

#if 1 //$use_std_namespace_names( this_thread );
namespace this_thread = std::this_thread;
#endif

using Lock = unique_lock<mutex>;

mutex output_mutex;

void log( const string& s )
{
//$with( Lock( output_mutex ) ) { clog << s << endl; }
{ auto&& _ = Lock( output_mutex ); clog << s << endl; }
}

class Mutually_exclusive_execution
{

mutex m_q_access;
queue<Lock*> m_execution_locks_q;

static auto popped( queue<Lock*>& q )
-> Lock*
{
Lock* result = q.front();
q.pop();
return result;
}

void yield( const bool do_wait )
{
mutex m;
Lock execution_lock( m );

log( "Thread "s << this_thread::get_id() << " yielding." );

//$with( Lock( m_q_access ) )
{
auto&& _ = Lock( m_q_access );

if( not is_empty( m_execution_locks_q ) )
{
log( "Thread "s << this_thread::get_id() << "
releasing another thread." );
// popped( m_execution_locks_q )->unlock(); //
Another thread runs.
Lock moved_lock = move( *popped(
m_execution_locks_q ) );
log( "Thread "s << this_thread::get_id() << "
moving the lock succeeded." );
moved_lock.unlock(); // Another thread runs.
Destructor would do it anyway.
log( "Thread "s << this_thread::get_id() << "
release succeeded." );
}
if( do_wait )
{
try
{
log( "Thread "s << this_thread::get_id() << "
registering unlocker." );
m_execution_locks_q.emplace( &execution_lock );
}
catch( const exception& x )
{
log( "!"s << x.what() );
}
}
}

if( do_wait )
{
log( "Thread "s << this_thread::get_id() << " entering
wait state." );
//(Lock( m )); // Waits until m is unlocked by
another thread.
//$with( Lock( m ) ) {}
auto&& _ = Lock( m_q_access );
}
log( "Thread "s << this_thread::get_id() << " resuming." );
}

public:
void yield() { yield( true ); }
void goodbye() { yield( false ); }
};

} // namespace my

namespace app
{
#if 1 // $use_cppx( up_to );
// Range-based loop here replaced with ordinary counting for loop.
#endif

#if 1 //$use_std( cout, endl, thread );
using std::cout;
using std::endl;
using std::thread;
#endif

#if 1 //$use_std_namespace_names( this_thread );
namespace this_thread = std::this_thread;
#endif

void run()
{
my::Mutually_exclusive_execution mee;
const auto f = [&]()
{
for( int i = 0; i < 10; ++i )
{
mee.yield();
//$with( my::Lock( my::output_mutex ) )
{
auto&& _ = my::Lock( my::output_mutex );
cout << "Thread " << this_thread::get_id() << "
iteration " << i << "." << endl;
}
}
mee.goodbye();
};

thread t1( f );
thread t2( f );

t1.join(); t2.join();
}
} // namespace app

#include <stdexcept> // std::exception
#include <stdlib.h> // EXIT_...
#if 1 //$use_std( exception, cerr, cout, endl );
using std::exception;
using std::cerr;
using std::cout;
using std::endl;
#endif
auto main() -> int
{
try
{
cout << "Starting." << endl;
app::run();
cout << "Finished." << endl;
return EXIT_SUCCESS;
}
catch( const exception& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}


Cheers!,

- Alf (no change of behavior: works with g++, crashes with MSVC)



Alf P. Steinbach

unread,
Apr 10, 2019, 5:25:51 PM4/10/19
to
On 10.04.2019 23:08, Alf P. Steinbach wrote:
>
> - Alf (no change of behavior: works with g++, crashes with MSVC)

Sorry, all that editing let the editor do an unnoticed auto-completion,
changing "m" to "m_q_access" in one place.

I removed all the chaff and logging and discovered that edit-o.

Reduced code:


#include <iostream>
#include <stdexcept>
#include <thread>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>

namespace my
{
template< class Collection >
auto is_empty( const Collection& c )
-> bool
{ return c.empty(); }

using std::string;
using std::ostringstream;
using namespace std::string_literals;

template< class Type >
inline auto operator<<( string& s, const Type& value )
-> string&
{
ostringstream stream;
stream << value;
s += stream.str();
return s;
}

template< class Type >
inline auto operator<<( string&& s, Type const& value )
-> string&&
{ return move( operator<<( s, value ) ); }

using std::clog;
using std::endl;
using std::exception;
using std::move;
using std::mutex;
using std::queue;
using std::string;
using std::unique_lock;

namespace this_thread = std::this_thread;

using Lock = unique_lock<mutex>;

mutex output_mutex;

class Mutually_exclusive_execution
{

mutex m_q_access;
queue<Lock*> m_execution_locks_q;

static auto popped( queue<Lock*>& q )
-> Lock*
{
Lock* result = q.front();
q.pop();
return result;
}

void yield( const bool do_wait )
{
mutex m;
Lock execution_lock( m );


{
auto&& _ = Lock( m_q_access );

if( not is_empty( m_execution_locks_q ) )
{
Lock moved_lock = move( *popped(
m_execution_locks_q ) );
moved_lock.unlock(); // Another thread runs.
Destructor would do it anyway.
}
if( do_wait )
{
try
{
m_execution_locks_q.emplace( &execution_lock );
}
catch( const exception& x )
{
}
}
}

if( do_wait )
{
//(Lock( m )); // Waits until m is unlocked by
another thread.
auto&& _ = Lock( m );
}
}

public:
void yield() { yield( true ); }
void goodbye() { yield( false ); }
};

} // namespace my

namespace app
{
using std::cout;
using std::endl;
using std::thread;

namespace this_thread = std::this_thread;

void run()
{
my::Mutually_exclusive_execution mee;
const auto f = [&]()
{
for( int i = 0; i < 10; ++i )
{
mee.yield();
{
auto&& _ = my::Lock( my::output_mutex );
cout << "Thread " << this_thread::get_id() << "
iteration " << i << "." << endl;
}
}
mee.goodbye();
};

thread t1( f );
thread t2( f );

t1.join(); t2.join();
}
} // namespace app

#include <stdexcept> // std::exception
#include <stdlib.h> // EXIT_...
using std::exception;
using std::cerr;
using std::cout;
using std::endl;

Sam

unread,
Apr 10, 2019, 6:12:58 PM4/10/19
to
Alf P. Steinbach writes:

> void yield( const bool do_wait )
> {
> mutex m;
> Lock execution_lock( m );

A very cursory attempt to understand this: this is a std::unique_lock, that
locks this thread's mutex. A pointer to this std::unique_lock gets added to
a shared queue. Some other execution thread apparently move-constructs, via
the queued pointer to this std::unique_lock, off the queue, and into an
automatically-scoped variable, which goes out of scope and gets destroyed.

I think that's what's going on here. Not 100% certain, but that seems to be
the gist of it. There's some other code that deals with thread
synchronization, but I this looks like the key part:

Lock moved_lock = move( *popped( m_execution_locks_q ) );
log( "Thread "s << this_thread::get_id() << " moving the
lock succeeded." );
moved_lock.unlock(); // Another thread runs.
Destructor
would do it anyway.
log( "Thread "s << this_thread::get_id() << " release
succeeded." );
}

This seems to be phishing out the pointer from the queue, and std::move-s it
into "moved_lock", in a different execution thread.

The very first sentence on

https://en.cppreference.com/w/cpp/thread/mutex/unlock

specifies the following:

# The mutex must be locked by the current thread of execution, otherwise,
# the behavior is undefined.

As far as I can tell, the shown code ends up unlocking a mutex in a
different execution thread. As Mr. Spock would say: "highly illogical".

Alf P. Steinbach

unread,
Apr 10, 2019, 6:28:35 PM4/10/19
to
Thanks. I don't understand why the restriction, but given that it's
there, what can I do? Is this where condition variables are needed?

I'm possibly not quite logical because on painkillers. But.


Cheers!,

- Alf

Geoff

unread,
Apr 10, 2019, 6:34:46 PM4/10/19
to
On Wed, 10 Apr 2019 23:25:39 +0200, "Alf P. Steinbach"
<alf.p.stein...@gmail.com> wrote:

...
> if( not is_empty( m_execution_locks_q ) )
WTF????---------------^^^
> {
> Lock moved_lock = move( *popped( m_execution_locks_q ) );
> moved_lock.unlock(); // Another thread runs. Destructor would do it anyway.

Sam's analysis is correct. It's throwing at the unlock.

Sam

unread,
Apr 10, 2019, 7:40:26 PM4/10/19
to
Alf P. Steinbach writes:

>>
>> As far as I can tell, the shown code ends up unlocking a mutex in a
>> different execution thread. As Mr. Spock would say: "highly illogical".
>>
>
> Thanks. I don't understand why the restriction, but given that it's there,
> what can I do? Is this where condition variables are needed?

Yes. A mutex, a condition variable, and a bool flag can be used to implement
a logically-equivalent locking abstraction that can be unlocked by a
different execution thread.

The bool flag's initial value is false.

Your thread-independent locking operation consists of locking the
std::mutex, and waiting on the condition variable until the bool flag is
false, then setting it to true, then unlocking the mutex.

Your unlock operation consists of locking the std::mutex, setting the bool
flag to false, notify_all() the condition variable, then unlocking the mutex.

Your code is also quite vulnerable to undefined behavior due to the lifetime
of its std::mutexes which are instantiated in automatic scope. If the
std::mutex goes out of scope and gets destroyed while some other execution
thread still attempts to screw around with it, more undefined behavior.

I haven't analyzed whether the shown code guarantees that all references to
the mutex are gone, before it gets destroyed. But it should not be necessary
to rely on this rather fragile dependency. It's far more reliable to use
smart pointers to dynamically-allocated objects with your mutex, condition
variable, and the bool flag, and have a queue of smart pointers. Whichever
thread ends up having the last smart pointer to the object go out of scope
will destroy it, without worrying about making sure that it's the right
thread.


Paavo Helde

unread,
Apr 11, 2019, 2:50:21 AM4/11/19
to
A mutex is a very simple concept. You lock the mutex for doing some
modifications on a shared data state which must appear as a single
atomic operation to other threads.

If the mutex is locked and unlocked in different threads, then the
operation does not look very atomic to me any more.

Juha Nieminen

unread,
Apr 11, 2019, 6:02:22 AM4/11/19
to
Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
> $use_cppx( is_empty );

You understand that this is a C++ newsgroup, right?

> auto main() -> int
> {
...
> return EXIT_FAILURE;
> }

All things considered, I think that's actually quite fitting.

Chris Vine

unread,
Apr 11, 2019, 7:40:20 AM4/11/19
to
Semaphores with a maximum count of 1 can be used to provide mutual
exclusion and synchronization, and so to that extent provide atomicity
without having the ownership requirements of the (lighter overhead)
mutex. These may be what Alf wants, but I haven't examined his code in
detail. C++ does not have semaphores built in but they can be
synthesized with a condition variable, mutex and flag. (However, both
windows and POSIX have native semaphore objects.)

As you and others have said, in C++ mutexes have ownerhip. Only the
thread which currently holds the lock may unlock.

Paavo Helde

unread,
Apr 11, 2019, 8:13:15 AM4/11/19
to
On 11.04.2019 14:40, Chris Vine wrote:
>
> Semaphores with a maximum count of 1 can be used to provide mutual
> exclusion and synchronization, and so to that extent provide atomicity
> without having the ownership requirements of the (lighter overhead)
> mutex. These may be what Alf wants, but I haven't examined his code in
> detail. C++ does not have semaphores built in but they can be
> synthesized with a condition variable, mutex and flag. (However, both
> windows and POSIX have native semaphore objects.)

C++ does not have semaphors because there is no need for them in
multithreaded programming, one can synhronize access to any shared data
state easily with a mutex and condvar.

When using a semaphor the automatically shared data state would be just
a single flag or a single counter, and then one would need to use more
complicated logic and more error-prone code for sharing the rest of the
data.

Chris Vine

unread,
Apr 11, 2019, 8:57:47 AM4/11/19
to
I am not sure I understand you. If using a semaphore, the shared state
to which the semaphore is a gateway could be anything, depending on what
the user is trying to implement.

Perhaps you meant "if implementing a semaphore". If so, yes that is
what I wrote ("... they can be synthesized with a condition variable,
mutex and flag").

The main differences between a binary semaphore and a mutex is that a
semaphore can be "unlocked" by any thread and is more heavy weight to
implement. Non-binary semaphores are something else, where mutual
exclusion is not the main objective.

Alf P. Steinbach

unread,
Apr 12, 2019, 3:37:42 AM4/12/19
to
On 11.04.2019 12:02, Juha Nieminen wrote:
> Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
>> $use_cppx( is_empty );
>
> You understand that this is a C++ newsgroup, right?

As I understand it you indicate that in your opinion the above is not C++.

It's equivalent to

using cppx::is_empty;

and the notation really saves typing when a number of names from a
namespace are used:

$use_std( cout, cerr, endl );

instead of C++17

using std::cout, std::cerr, std::endl;

or C++14 or earlier

using std::cout; using std::cerr; using std::endl;

The `$` here saves one from the eye-soring uppercase `CPPX_USE_CPPX`.
General rule: replace `$` with prefix `CPPX_`, uppercase the rest.

But if you like all uppercase, and/prefixes, or are a stickler for
pedantic formal correctness, then by all means uglify. The library
supports that for those who want it. So you're not forced to use `$`.

And technically you'd be right: the `$` is not permitted in C++ names,
for what once was /political correctness/ issues: that ASCII was not
just very English-centric, and not just had "American" in the name, but
actually had a symbol that denoted something American! :-o In an
intended more culture-neutral replacement for ASCII the `$` was
therefore replaced with the international currency symbol `¤`, which was
proposed by Italy and subsequently used by Russia, which naturally
didn't like the `$` very much. Today that nonsense (there is yet no
common international currency) symbol that no-one uses still occupies
valuable space on our keyboards, which especially is annoying to
non-English-country programmers, who'd rather have the `$` there.

On some systems (my own direct experience was on the HP3000) the `$` is
needed for system function names. And in nearly all other aspects the
C++ standard goes to pains to not exclude any system. And as far as I
know all extant C++ compilers accept the `$`; they just say a resounding
no to political correctness, and yes to practicality.

Some time after I started experimenting with `$`, Herb Sutter did the
same, probably for the exact same reasons: that it's an unused
"namespace", providing more clean notation for those who use it first.

But part of his feedback was that some companies use preprocessing (via
other tools than the C++ preprocessor) where the `$` in ordinary code is
problematic. Just like certain characters in file names can be
problematic for some tools. So Herb decided to drop the `$` convenience,
but I, for my library aimed more at small scale programming (in large
scale almost any overhead is justifiable) and learners, don't need to
support a few big companies' preprocessing requirements.

Thank you for providing an opportunity to mention all this.

Maybe you learned something?


>> auto main() -> int
>> {
> ...
>> return EXIT_FAILURE;
>> }
>
> All things considered, I think that's actually quite fitting.

Negativity oozes out of you.

It's true, I did a stupid thing with mutexes in the code. But it's dumb
to point out that the code doesn't work, in a response to a question
about why it doesn't work. It's a kind of wanna-be-bully thing, idiocy.

So, now you got some negativity back, how does that feel? Better?


Cheers!,

- Alf

James Kuyper

unread,
Apr 15, 2019, 9:11:06 AM4/15/19
to
On 4/12/19 3:37 AM, Alf P. Steinbach wrote:
> On 11.04.2019 12:02, Juha Nieminen wrote:
>> Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
>>> $use_cppx( is_empty );
>>
>> You understand that this is a C++ newsgroup, right?
>
> As I understand it you indicate that in your opinion the above is not C++.

The meaning of your syntax is certainly not specified by the C++
standard. Any such use of '$' outside of a character or string literal
is a C++ syntax error. You should not expect people monitoring
comp.lang.c++ to know what it means in whatever context does allow you
to use that syntax. It's also unreasonable to try to teach them that
syntax. If you're posting here, you should post using standard C++
syntax, even if you normally use something else.

Alf P. Steinbach

unread,
Apr 15, 2019, 10:04:33 AM4/15/19
to
It's not a non-standard syntax (though the $ is a non-standard token
form -- syntax is one level up from that), it's an ordinary macro.

It would be quite some work to rewrite the macro calls before posting,
and ~zero work for a reader here to grok it.

One simply does not include all the relevant parts of Boost if the code
uses Boost, say. One doesn't expect to get helpful responses from people
who fail to relate to some Boost functionality and need that source code
posted in the group. The same for the above; it's sheer lunacy to
require all library source code posted, instead of referenced.

Therefore, unless there's a reasonable request for a pared down example
(as was in one response), I just provide a link to some library that
defines the macros, or other stuff used in the code.

Re "whatever context does allow you", that's quite misleading. I don't
know any environment where a compiler does not by default accept the
above. Such an environment might exist, but your statement indicates
that instead of being exceedingly rare, possibly non-existing, it's
common, and that's very misleading and can't be based on actual experience.


Cheers!,

- Alf

james...@alumni.caltech.edu

unread,
Apr 15, 2019, 11:38:45 AM4/15/19
to
On Monday, April 15, 2019 at 10:04:33 AM UTC-4, Alf P. Steinbach wrote:
> On 15.04.2019 15:10, James Kuyper wrote:
> > On 4/12/19 3:37 AM, Alf P. Steinbach wrote:
> >> On 11.04.2019 12:02, Juha Nieminen wrote:
> >>> Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
> >>>> $use_cppx( is_empty );
> >>>
> >>> You understand that this is a C++ newsgroup, right?
> >>
> >> As I understand it you indicate that in your opinion the above is not C++.
> >
> > The meaning of your syntax is certainly not specified by the C++
> > standard. Any such use of '$' outside of a character or string literal
> > is a C++ syntax error. You should not expect people monitoring
> > comp.lang.c++ to know what it means in whatever context does allow you
> > to use that syntax. It's also unreasonable to try to teach them that
> > syntax. If you're posting here, you should post using standard C++
> > syntax, even if you normally use something else.
>
> It's not a non-standard syntax (though the $ is a non-standard token
> form -- syntax is one level up from that), it's an ordinary macro.

The standard uses the notation described in 4.3 "Syntax Notation" to
describe the rules for valid identifiers (5.10p1). I feel comfortable
referring to violations of those rules as syntax errors - the standard
doesn't provide any other simple term for referring to such violations.

...
> Re "whatever context does allow you", that's quite misleading. I don't
> know any environment where a compiler does not by default accept the
> above.

Any compiler that enforces C++ rules governing the characters permitted
in identifiers should, at an absolute minimum, issue a diagnostic for
such code when invoked in a fully conforming mode. I don't normally use
any compiler in a non-conforming mode.

> ... Such an environment might exist, but your statement indicates
> that instead of being exceedingly rare, possibly non-existing, it's
> common, and that's very misleading and can't be based on actual experience.

Since I know what characters are permitted in identifiers, I never use
the ones that aren't. It's therefore entirely possible that the
compilers I've used tolerate characters that are not permitted, and
might even be non-conforming by reason of failing to issue the mandatory
diagnostic when they do so. That's not a sign of lack of experience,
merely a failure on my part to exceed the limits of what's permitted,
just for the joy of finding out what happens when I do. That's the same
reason I don't have much experience with how compilers deal with
division by 0, or how they deal with bit-wise operations on negative values.

Alf P. Steinbach

unread,
Apr 15, 2019, 11:46:12 AM4/15/19
to
A claim of in-experience!

In support of your view.

But you're just adding noise in order to drown out the earlier exchange.

You have no point, just a dislike.


Cheers!,

- Alf

Öö Tiib

unread,
Apr 15, 2019, 11:51:19 AM4/15/19
to
AFAIK $ was not allowed in identifiers until C++11. C++11 started
to allow "and other implementation-defined characters" in identifiers.
I'm in doubt that it is violation of standard not to diagnose presence of
"other implementation-defined characters" in identifiers.

james...@alumni.caltech.edu

unread,
Apr 15, 2019, 12:29:52 PM4/15/19
to
On Monday, April 15, 2019 at 11:46:12 AM UTC-4, Alf P. Steinbach wrote:
> On 15.04.2019 17:38, james...@alumni.caltech.edu wrote:
...
> > Since I know what characters are permitted in identifiers, I never use
> > the ones that aren't. It's therefore entirely possible that the
> > compilers I've used tolerate characters that are not permitted, and
> > might even be non-conforming by reason of failing to issue the mandatory
> > diagnostic when they do so. That's not a sign of lack of experience,
> > merely a failure on my part to exceed the limits of what's permitted,
> > just for the joy of finding out what happens when I do. That's the same
> > reason I don't have much experience with how compilers deal with
> > division by 0, or how they deal with bit-wise operations on negative values.
>
> A claim of in-experience!

Yes, I routinely relevant disclaim experience that others might have
expected me to have. Don't you?

> In support of your view.

Er, no? It was the C++ standard that I was using to support my view;
explaining my inexperience was merely part of an explanation of why I
cannot directly address your assertion that compilers which do this are
commonplace.

...
> You have no point, just a dislike.

It's true that I dislike non-conformance with applicable and
authoritative standards. However, my point was about the standard, not
my dislike. My point would have been equally valid (and almost
identically written), if I had been an enthusiastic supporter of this
extension.

Paavo Helde

unread,
Apr 15, 2019, 12:39:53 PM4/15/19
to
On 15.04.2019 17:04, Alf P. Steinbach wrote:
> It's not a non-standard syntax (though the $ is a non-standard token
> form -- syntax is one level up from that), it's an ordinary macro.
>
> It would be quite some work to rewrite the macro calls before posting,

If these are really macros, then a simple preprocessor run would get rid
of them.

> and ~zero work for a reader here to grok it.

You must be kidding. Zero work for downloading and studying whatever
framework/library is defining these macros? And for what purpose?


Alf P. Steinbach

unread,
Apr 15, 2019, 1:56:01 PM4/15/19
to
On 15.04.2019 18:39, Paavo Helde wrote:
> On 15.04.2019 17:04, Alf P. Steinbach wrote:
>> It's not a non-standard syntax (though the $ is a non-standard token
>> form -- syntax is one level up from that), it's an ordinary macro.
>>
>> It would be quite some work to rewrite the macro calls before posting,
>
> If these are really macros, then a simple preprocessor run would get rid
> of them

Then you get some zillions of tons of code from the standard library added.

But I guess I could write a simple name transformer. Will consider.


>> and ~zero work for a reader here to grok it.
>
> You must be kidding. Zero work for downloading and studying whatever
> framework/library is defining these macros? And for what purpose?

Well, `using` statements aren't exactly complex or rocket science.
They're just annoyingly needlessly impractically verbose in C++. And the
committee drags its feet, going by quarter-measures, where C++17 just
allows you to not redundantly repeat the word `using` for each item.


Cheers!,

- Alf

james...@alumni.caltech.edu

unread,
Apr 15, 2019, 2:14:48 PM4/15/19
to
On Monday, April 15, 2019 at 11:51:19 AM UTC-4, Öö Tiib wrote:
> On Monday, 15 April 2019 18:38:45 UTC+3, james...@alumni.caltech.edu wrote:
...
> > Since I know what characters are permitted in identifiers, I never use
> > the ones that aren't. It's therefore entirely possible that the
> > compilers I've used tolerate characters that are not permitted, and
> > might even be non-conforming by reason of failing to issue the mandatory
> > diagnostic when they do so. That's not a sign of lack of experience,
> > merely a failure on my part to exceed the limits of what's permitted,
> > just for the joy of finding out what happens when I do. That's the same
> > reason I don't have much experience with how compilers deal with
> > division by 0, or how they deal with bit-wise operations on negative values.
>
> AFAIK $ was not allowed in identifiers until C++11. C++11 started
> to allow "and other implementation-defined characters" in identifiers.

I don't currently have access to older versions of the standard, but
you're right about C++2011 and C++2014, but in C++2017 that part was
removed, and it's not present in the latest draft I have access to
(n4762.pdf, 2018-07-07), either.


> I'm in doubt that it is violation of standard not to diagnose presence of
> "other implementation-defined characters" in identifiers.

Diagnostics are sometimes mandatory, but never prohibited.

In the context of older versions of C++, I would have emphasized the
greater portability of identifiers that don't rely upon the "other ..."
option.

james...@alumni.caltech.edu

unread,
Apr 15, 2019, 2:28:18 PM4/15/19
to
On Monday, April 15, 2019 at 12:29:52 PM UTC-4, james...@alumni.caltech.edu wrote:
...
> Yes, I routinely relevant disclaim experience that others might have
> expected me to have.

Let me re-write that:

I routinely disclaim relevant experience that others might expect me to have, if I don't actually have it.

leigh.v....@googlemail.com

unread,
Apr 16, 2019, 5:48:50 AM4/16/19
to
If you ever want me to look at your code then:
1) I don't want to see any of this $ macro bollocks;
2) I don't want to see any using directives, use std:: prefix whenever something from the stdlib is used as it makes the code clearer and easier to understand;
3) I don't want to see overuse of the auto return type / arrow notation. You are deliberately trying to irritate by using it for main() and for other things that are NOT what it was designed for (it was primarily designed for a subset of function templates).

/Leigh

Alf P. Steinbach

unread,
Apr 16, 2019, 6:51:34 AM4/16/19
to
No, I don't want you looking at my code. The result of that has always
been sour commentary like the above, asserting some pretty subjective
ideas about things, with nothing useful to me or others. Which leads me
to believe that you /have/ looked at that code. :)

But I don't want you to waste time on your universal compiler either
(even if you restrict that effort to a VM, Mozilla is doing that).

Please, instead finish up a working first version of the NeoGfx library,
and let students and hobbyists, maybe also small firms, use it for free,
e.g. like Visual Studio.


Cheers!,

- Alf

leigh.v....@googlemail.com

unread,
Apr 16, 2019, 7:22:11 AM4/16/19
to
The scripting engine is an important part of neoGFX and my universal compiler is an important part of the scripting engine.

/Leigh

Öö Tiib

unread,
Apr 16, 2019, 8:56:34 AM4/16/19
to
On Tuesday, 16 April 2019 14:22:11 UTC+3, leigh.v...@googlemail.com wrote:
>
> The scripting engine is an important part of neoGFX and my universal compiler is an important part of the scripting engine.

Why you can't just embed some existing (like lua or javascript)
engine as first step? That would take only a tiny bit of time
compared to implementing and testing and getting some
"universal compiler" feature rich, efficient and defect free
enough to be useful for anything.

Mr Flibble

unread,
Apr 16, 2019, 12:00:09 PM4/16/19
to
You are underestimating both the power of "NIH" and my abilities Öö Tiib! :D

/Flibble

--
“You won’t burn in hell. But be nice anyway.” – Ricky Gervais

“I see Atheists are fighting and killing each other again, over who
doesn’t believe in any God the most. Oh, no..wait.. that never happens.” –
Ricky Gervais

"Suppose it's all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a
world that is so full of injustice and pain. That's what I would say."

Chris M. Thomasson

unread,
Apr 16, 2019, 4:32:09 PM4/16/19
to
On 4/10/2019 2:08 PM, Alf P. Steinbach wrote:
> On 10.04.2019 22:40, Chris M. Thomasson wrote:
>> On 4/10/2019 1:09 PM, Alf P. Steinbach wrote:
>>> Code at bottom.
>>>
>>> My questions are:
>>> * Why is it crashing with Visual C++?
>>> * Is there a much simpler / efficient / whatever way to do this
>>>    (except using C++20 coroutines)?
>>>
>>> Output with MinGW g++, with logging suppressed:
>> [...]
>>> - Alf (perplexed, baffled, confounded, bewildered & for the time
>>> being stumped)
>>>
>>
>> Can you possibly reduce it down to something that does not require all
>> of the $... stuff? Something that just uses std primitives, perhaps? I
>> have to learn what those $ things mean "under the covers" in order to
>> help. Humm...
>
> Done. It's just that I prefer coding up things with the macros, cause
> it's less to write and less to read. Thanks.
>
[...]
> - Alf (no change of behavior: works with g++, crashes with MSVC)

A thread that locks a mutex, MUST be same thread that unlocks it. I am
not exactly sure what you are doing here.

0 new messages