- Found a use case for `const` by value return, a shared queue - 4 Updates
- lvalue assignment difference between C and C++ - 14 Updates
- single object - 4 Updates
"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:
Post a Comment