Monday, February 6, 2023

Digest for comp.lang.c++@googlegroups.com - 22 updates in 3 topics

"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Feb 06 10:09PM +0100

On 2023-02-05 10:39 PM, Alf P. Steinbach wrote:
 
> Thanks. I don't know yet how to handle that with the RAII-based access
> scheme. Perhaps set a count of number of signals to issue then do that
> in the Access destructor.
 
Implementation of that idea, hopefully it makes sense and is technically
correct:
 
template< class Item >
class Shared_queue_
{
queue<Item> m_queue;
 
mutable mutex m_mutex;
mutable condition_variable m_condition;
 
auto length() const -> int { return int_size_of( m_queue ); }
auto is_empty() const -> bool { return m_queue.empty(); }
 
public:
class Access:
public Movable
{
friend class Shared_queue_;
using Q_ptr = variant<Shared_queue_*, const Shared_queue_*>;
 
Q_ptr m_p_queue;
Mutex_lock m_mutex_lock;
int m_n_notifications = 0;
 
Access( Shared_queue_& q ): m_p_queue( &q ), m_mutex_lock(
q.m_mutex ) {}
Access( const Shared_queue_& q ): m_p_queue( &q ),
m_mutex_lock( q.m_mutex ) {}
 
auto shared_queue() -> Shared_queue_& { return
*get<Shared_queue_*>( m_p_queue ); }
 
auto shared_queue() const
-> const Shared_queue_&
{ return *(m_p_queue.index() == 0? get<0>( m_p_queue ) :
get<1>( m_p_queue )); }
 
public:
~Access()
{
m_mutex_lock.unlock();
repeat_times( m_n_notifications, [&]{
shared_queue().m_condition.notify_one();
} );
}
 
auto length() const -> int { return
shared_queue().length(); }
auto is_empty() const -> bool { return
shared_queue().is_empty(); }
 
void wait_for_items()
{
if( not is_empty() ) { return; }
shared_queue().m_condition.wait( m_mutex_lock, [&]() ->
bool { return not is_empty(); } );
}
 
auto wait_for_items_for( const Duration duration )
-> bool
{
if( not is_empty() ) { return true; }
// TODO: handle possible overflow in
`steady_clock::now() + duration`.
const bool timed_out = not
shared_queue().m_condition.wait_for(
m_mutex_lock, duration, [&]() -> bool { return not
is_empty(); }
);
return not timed_out;
}
 
void enq( Item item, const bool notify = true )
{
shared_queue().m_queue.push( move( item ) );
m_n_notifications += notify;
}
 
auto deq() -> Item
{
wait_for_items();
hopefully( not is_empty() )
or FSM_FAIL( "Waited for items but got none;
shared_queue is empty." );
return popped_front_of( shared_queue().m_queue );
}
};
 
auto access() -> Access { return Access( *this ); }
auto access() const -> const Access { return Access( *this ); }
};
 
- Alf
scott@slp53.sl.home (Scott Lurndal): Feb 06 09:35PM

>> in the Access destructor.
 
>Implementation of that idea, hopefully it makes sense and is technically
>correct:
 
Leaving aside the odd syntactic choices, what is the function
of the class and why would it be of use? How about providing
an example of use?
 
I don't recall 'not', 'and' and 'or' being C++ keywords.
 
What's the difference between "mutex" and "Mutex_lock" types?
 
Where is the mutex actually acquired? It is released in the
destructor - does the undefined "Mutex_lock" class acquire the
mutex in some misguided attempt to apply RAII to locking?
 
IME, a critical region generally protects more just queue accesses;
it may also protect other data or make a certain code sequence atomic
with respect to other threads. That's one of the many reasons that
I dislike the application of RAII to locking.
 
Whatever agent you used to post this wrapped many of the longer lines
which made it even less readable than your nonstandard syntax choices.
 
m_mutex.lock();
qe = m_queue.pop()
m_mutex_unlock();
 
seems to be much cleaner and far more readable (and thus more maintainable).
 
The idea that locking can be solved using RAII universally is unviable.
 
/**
* Execute I/O Control Blocks queued to this DLP instance.
*
* This is the main loop of the per-DLP instance thread.
*
* Loop until the d_exit flag is set to true.
* Set d_exited flag when the thread terminates to notify
* terminator that the thread terminated successfully.
*/
void
c_dlp::run(void)
{
int diag;
c_dlist pending;
char tname[64];
 
c_processor::set_this(d_processor);
c_system::set_this(mp->get_system());
c_memory::set_this(mp->get_system()->get_mem());
 
snprintf(tname, sizeof(tname), "%04lu %s DLP",
d_channel, get_hardware_name());
set_threadname(tname);
 
pending.init();
 
lock_thread();
while (!d_exit) {
while (pending.is_empty()) {
c_dlist_iterator di(&d_iocbs);
while (di.next()) {
c_iocb *iocb = (c_iocb *)di.curr();
 
iocb->remove();
pending.insert(iocb);
}
if (!pending.is_empty()) break;
diag = pthread_cond_wait(&d_wait, &t_threadlock);
if (diag != 0) {
d_logger->log("%04lu/00 Unexpected cond-wait result: %s\n",
d_channel, strerror(diag));
}
if (d_exit) break;
}
 
if (d_exit) break;
 
unlock_thread();
 
c_dlist_iterator worklist(&pending);
while (worklist.next()) {
c_iocb * iocb = (c_iocb *)worklist.curr();
iocb->remove();
 
switch (iocb->get_op()) {
case IOD_CANCEL:
cancel(iocb);
break;
case IOD_READ:
read(iocb);
break;
case IOD_WRITE:
write(iocb);
break;
case IOD_TEST:
if (iocb->get_op_var1() == IOD_TEST_IDENTIFY) {
set_rd(iocb, IOT_COMPLETE|IOT_EXT_RD_PRESENT,
0x0000, d_testid << 8);
} else {
test(iocb);
}
break;
case IOD_ECHO:
echo(iocb);
break;
}
}
lock_thread();
pending.init();
}
d_exited = true;
 
unlock_thread();
}
 
 
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Feb 06 11:44PM +0100

On 2023-02-06 10:35 PM, Scott Lurndal wrote:
> of the class and why would it be of use? How about providing
> an example of use?
 
> I don't recall 'not', 'and' and 'or' being C++ keywords.
 
I do. ;-)
 
https://eel.is/c++draft/lex.digraph
 
 
> What's the difference between "mutex" and "Mutex_lock" types?
 
using Mutex_lock = std::unique_lock<std::mutex>;
 
That is, a `Mutex_lock` is the combination of a lock and a mutex, as the
name implies.
 
But now that you mention it, I realize the standard's terminology is not
really clear. It should be a "locker", not a "lock". Yes.
 
 
> Where is the mutex actually acquired? It is released in the
> destructor - does the undefined "Mutex_lock" class acquire the
> mutex in some misguided attempt to apply RAII to locking?
 
Yes it's RAII, and `std::unique_lock` is the standard library's RAII
based wrapper for a mutex lock: it acquires a lock in its constructor.
 
I wouldn't call the standard library's threading support misguided.
 
Not because I know enough about the technical to evaluate it, but
because I know that it's designed by some of the finest & best, and it
worked fine for me.
 
 
> with respect to other threads. That's one of the many reasons that
> I dislike the application of RAII to locking.
 
> Whatever agent you used to post this wrapped many of the longer lines
 
The agent is Mozilla Thunderbird.
 
It uses a line continuation scheme that's specified in RFC 3676 section
4.1, of putting a space at the end of a line as continuation symbol;
<url: https://www.ietf.org/rfc/rfc3676.txt>.
 
That's called a "soft line break", and the scheme is called "flowed
format", which you can see in the posting's headers.
 
Thus, looking at my posting in TB's usual view it's fine, but the raw
posted text lines are wrapped.
 
Evidently your newsreader XRN (X news reader) or perhaps your version of
that newsreader, doesn't support RFC 3676's flowed format.
 
 
> qe = m_queue.pop()
> m_mutex_unlock();
 
> seems to be much cleaner and far more readable (and thus more maintainable).
 
Yes, and in this case it would be sufficiently safe.
 
But in general it's not exception safe.
 
Letting a destructor do the unlocking is exception safe, and very
convenient. :)
 
 
> The idea that locking can be solved using RAII universally is unviable.
 
That may be, I don't know enough yet to have an opinion. Sorry for being
incompetent in this matter. :-o But it served its purpose well for the
shared queue.
 
 
[snip code example]
 
 
- Alf
Paavo Helde <eesnimi@osa.pri.ee>: Feb 07 01:02AM +0200

06.02.2023 23:35 Scott Lurndal kirjutas:
 
> The idea that locking can be solved using RAII universally is unviable.
 
Right, but in C++ we have the means and habits to encapsulate such
non-RAII things in low-level classes like
std::condition_variable::wait() et al, so the application level code can
be clean and RAII.
Paavo Helde <eesnimi@osa.pri.ee>: Feb 06 09:11AM +0200


> Ok, that makes sense. Though I wonder - given the main C compilers used now
> are also C++ compilers - why they don't just silently allow it in C? Would
> there be any wierd or unfortunate side effects in C if it was?
 
This would basically introduce a limited notion of 'reference' in the C
language, which would make the language more complex to teach and learn.
That's the opposite to the goals C currently has.
 
And yes, having a simpler language in general means the programs written
in it need to be more complex. Here it just means a couple of extra
asterisks and ampersands, so not a big deal.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Feb 06 01:01AM -0800

> 04.02.2023 18:59 Muttley@dastardlyhq.com kirjutas:
[...]
 
> And yes, having a simpler language in general means the programs
> written in it need to be more complex. Here it just means a couple of
> extra asterisks and ampersands, so not a big deal.
 
I agree it's not a big deal, and I've already commented on the idea
that most C compilers are also C++ compilers (they aren't).
 
But I don't think that allowing a conditional expression to be an
lvalue would require introducing references into the language.
 
An lvalue is defined as "an expression that potentially designated
an object" ("potentially" because *ptr is still an lvalue even if
ptr==NULL). This would just mean that `x ? y : z` would designate an
object, and the identify of that object depends on the value of `x`.
It's not all that much different from `a[i]`, where the identify
of the object it designates depends on the values of `a` and `i`
-- or `*p` for that matter.
 
If it were introduced in a future edition of the C standard, I don't
think it would cause many problems. (And the generated machine code
would probably use pointers anyway.) But I don't think there's enough
demand to justify it.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */
Muttley@dastardlyhq.com: Feb 06 09:26AM

On Sun, 5 Feb 2023 11:42:04 +0100
>On the other hand, the vast majority of C compilers are /not/ C++
 
Ok, I should have said most of the *common* C++ compilers are also C compilers.
 
>If you look at hobby projects, one-man toolchains and niche compilers, I
>have never heard of any that support C++, while a working C compiler is
>something many people have written.
 
I don't know if its true or not, but I read once that lexx and yacc are
quite capable of parsing C grammar and create the appropriate lex and parse
code but the C++ syntax is apparently beyond them because there are so many
token overloads and special cases. And that was IIRC before C++ 2011, never
mind now.
"Öö Tiib" <ootiib@hot.ee>: Feb 06 03:27AM -0800

> code but the C++ syntax is apparently beyond them because there are so many
> token overloads and special cases. And that was IIRC before C++ 2011, never
> mind now.
 
Yes, C++ has always had tendency to make same row of tokens to mean very
different things depending on context. The lookup rules have always required
to find things (the above-mentioned context) from whole class as if already
parsed while parsing that class. The "export template" of C++98 was
so tricky that no main stream compiler was capable to implement it and so it
was removed by C++11.
Richard Damon <Richard@Damon-Family.org>: Feb 06 07:42AM -0500

On 2/6/23 4:01 AM, Keith Thompson wrote:
> think it would cause many problems. (And the generated machine code
> would probably use pointers anyway.) But I don't think there's enough
> demand to justify it.
 
I think the limitation is that to parse:
 
int a, b, c, d;
...
(a ? b : c) = d;
 
The "value" used from b, for parsing the expression isn't the integral
value of b, but something like a int& that will convert to the value if
needed. The whole design phylosophy of C is that while parse the
statement, you can evalutate the pieces largly ignoring remote context.
The remembering that you have an l-value never has to survive going
through an "expression" but always (currently) used just by the
immediate operator that consumes it.
 
To implement this, b's l-valueness (if c is also an l-value) through the
conditional operator to see how that is being used. Since conditionals
can be nested, this requires a somewhat arbitrary level of delay.
 
C++ get around that by b evaluating not to the value of b, but a
reference to b, that switches to the value if used as an r-value or
stays an l-value if used as an assignment target.
 
Thus the ability comes straight out of having a "reference" type.
David Brown <david.brown@hesbynett.no>: Feb 06 02:13PM +0100

> David Brown <david.brown@hesbynett.no> wrote:
>> On the other hand, the vast majority of C compilers are /not/ C++
 
> Ok, I should have said most of the *common* C++ compilers are also C compilers.
 
No, that would still be wrong. You should have said - as I wrote - that
most C++ compilers have a C compiler as part of their toolchain packages.
 
I don't know of any C++ compiler, common or not, that does not have an
associated C compiler. (The nearest, perhaps, would be MSVC - there you
have a modern C++ compiler combined with an outdated and non-conforming
C compiler.)
Muttley@dastardlyhq.com: Feb 06 03:49PM

On Mon, 6 Feb 2023 03:27:43 -0800 (PST)
>parsed while parsing that class. The "export template" of C++98 was
>so tricky that no main stream compiler was capable to implement it and so it
>was removed by C++11.
 
That would have been very useful. Having to put templated code in a header
file and constantly recompile it its a right PITA.
 
I can see the problem though - it would need a update of object file formats
which is probably non trivial.
Muttley@dastardlyhq.com: Feb 06 03:50PM

On Mon, 6 Feb 2023 14:13:51 +0100
>compilers.
 
>No, that would still be wrong. You should have said - as I wrote - that
>most C++ compilers have a C compiler as part of their toolchain packages.
 
You're splitting hairs. "Compiler" for most people is the entire toolchain,
not one particular part of it.
David Brown <david.brown@hesbynett.no>: Feb 06 08:08PM +0100

>> most C++ compilers have a C compiler as part of their toolchain packages.
 
> You're splitting hairs. "Compiler" for most people is the entire toolchain,
> not one particular part of it.
 
Those hairs are critical when you are considering "Why doesn't C++
support this if C does?" or "Why doesn't C support this if C++ does?".
 
I agree that for most people in normal use, the details of how
toolchains work is unimportant. You use the command "gcc" or "clang",
and it will compile C and C++, do the linking, handle assembly, and
often handle Objective-C, Fortran, and maybe other languages. But we
can be a bit more advanced in these groups. You wanted to know whether
it would be simple for a C compiler to support a particular feature that
is standard in C++ - one of the reasons why it is not immediately easy
to add as an extension to C is that the compilers have independent
frond-ends.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Feb 06 12:47PM -0800

>>most C++ compilers have a C compiler as part of their toolchain packages.
 
> You're splitting hairs. "Compiler" for most people is the entire toolchain,
> not one particular part of it.
 
Perhaps, but the reason you said that most C compilers are also C++
compilers (or however you phrased it) was to support the idea that
it would be trivial to implement rvalue conditional expressions in C.
The part of a C compiler that implements conditional expressions is,
in all cases I'm aware of, distinct from the part of a C++ compiler
that implements conditional expressions. Possibly some code could
be borrowed, but it's not just a matter of turning off a restriction.
 
The "entire toolchain" is not relevant to the point you raised.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Feb 06 12:59PM -0800

> reference to b, that switches to the value if used as an r-value or
> stays an l-value if used as an assignment target.
 
> Thus the ability comes straight out of having a "reference" type.
 
You make a good point that I hadn't thought of: that a conditional
expression would have to be an lvalue or not depending whether b and c
are both lvalues (and there would have to be tighter constraints on the
types of b and c in that case). And that would violate the (probably
not quite universal) rule that every C (sub-)expression is evaluated
without regard to its context.
 
The C++ standard does define this in terms of "lvalue references".
 
But I think this could be defined in a future edition of C without using
C++-style references, probably by saying something like:
 
A conditional expression is an lvalue if and only if the second
and third operands are lvalues of compatible types.
 
(it would probably require substantially more words than that).
I don't think it would be worth the effort, but I wouldn't complain
if the C committee and implementers decided to do it.
 
(I'll note that a parenthesized lvalue is an lvalue, but of course
that's much more trival than a conditional expression.)
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Feb 06 01:03PM -0800

> David Brown <david.brown@hesbynett.no> wrote:
>>On the other hand, the vast majority of C compilers are /not/ C++
 
> Ok, I should have said most of the *common* C++ compilers are also C compilers.
 
Discussed elsewhere.
 
> code but the C++ syntax is apparently beyond them because there are so many
> token overloads and special cases. And that was IIRC before C++ 2011, never
> mind now.
 
C's grammar is not trivial to parse, due to typedefs. A typedef
effectively creates a context-specific keyword, which means the parser
has to interact with the symbol table. (Historically, this is because
typedefs were introduced relatively late in the evolution of C.)
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */
Kaz Kylheku <864-117-4973@kylheku.com>: Feb 06 10:21PM

> The key difference is that in C, the results of the conditional operator
> is always an r-value, and thus not suitable for an assignment operator.
 
Interestingly, unlike C++, ISO C doesn't use the term rvalue (only
lvalue). However: the term was used in the reference manual for the B
language, predecessor to C.
 
A footnote (I'm looking at C99, where it is note #53 on P. 46) says
"What is sometimes called ''rvalue'' is in this International Standard
described as the ''value of an expression''."
 
Every regular here knows this trivia; just dredging it up a bit here.
 
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazinator@mstdn.ca
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Feb 06 02:43PM -0800

> "What is sometimes called ''rvalue'' is in this International Standard
> described as the ''value of an expression''."
 
> Every regular here knows this trivia; just dredging it up a bit here.
 
Right, and the way C (non-normatively) defines "rvalue" is a bit odd.
An lvalue is a kind of expression, but an rvalue is the result of
evaluating an expression.
 
Historically, lvalues and rvalues were kinds of values. An expression
could be "evaluated for its lvalue" or "evaluated for its rvalue",
depending on the context in which it appears. In particular, the left
hand side of an assignment is evaluated for its lvalue (the identify of
the object it designates), and the right hand side of an assignment is
evaluated for its rvalue. Some expressions, such as 42, have rvalues
but not lvalues; thus 42 cannot appear on the LHS of an assignment.
This probably goes back to BCPL, or perhaps even CPL.
 
C mostly dropped rvalues and redefined an "lvalue" as a kind of
expression.
 
C++ has a small menagerie of kinds of rvalues and lvalues, all of which
(I think) are kinds of expressions.
 
I think I prefer the historical meanings, but we're stuck with what the
standards say now. (It took the ISO C standard three tries to define
"lvalue" consistently, so I don't propose rocking that particular boat.)
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Feb 05 10:31PM -0800

On 1/30/2023 1:28 PM, Scott Lurndal wrote:
 
>> Note - don't use counter .
 
> A static class member seems like a good starting point.
 
> Is it required to be thread-safe and/or re-entrant?
 
I thought that C++ has thread safe singletons? I know how to program one
from atomics.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Feb 05 10:33PM -0800

On 2/5/2023 10:31 PM, Chris M. Thomasson wrote:
 
>> Is it required to be thread-safe and/or re-entrant?
 
> I thought that C++ has thread safe singletons? I know how to program one
> from atomics.
 
https://www.ibm.com/docs/en/zos/2.3.0?topic=functions-pthread-once-invoke-function-once
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Feb 05 10:33PM -0800

On 2/5/2023 10:33 PM, Chris M. Thomasson wrote:
 
>> I thought that C++ has thread safe singletons? I know how to program
>> one from atomics.
 
> https://www.ibm.com/docs/en/zos/2.3.0?topic=functions-pthread-once-invoke-function-once
 
https://en.cppreference.com/w/cpp/thread/call_once
 
?
Paavo Helde <eesnimi@osa.pri.ee>: Feb 06 09:53AM +0200

06.02.2023 08:31 Chris M. Thomasson kirjutas:
 
>> Is it required to be thread-safe and/or re-entrant?
 
> I thought that C++ has thread safe singletons? I know how to program one
> from atomics.
 
C++ has thread-safe initialization and destruction of static variables.
 
Accessing them meanwhile in thread-safe manner is another topic,
depending on a lot of hairy details.
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: