- threading problems - 5 Updates
- A "better" C++ - 12 Updates
- Copy and Direct Initialization - 1 Update
- A "better" C++ - 1 Update
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 25 01:17AM +0100 On Mon, 24 Aug 2015 17:13:46 -0500 > So you cannot return a boost::thread from a method? > I am not sure why not? Why can't we? > It seems to work if I change it to return a pointer... Pointers are built in types and always copiable. However, your design is wrong. You attempt to export the thread class member object of SomeClass by copying it - the reason given in the comments being: "Return the thread we started, so that the caller may wait on it to join along with any other threads the started from other instances of this class." This shows that you have got your ownership semantics wrong, presumably to get around the rule (with POSIX threads and which probably therefore applies to boost threads) that you cannot have two different threads joining on the same thread object: the SUS states that "the results of multiple simultaneous calls to pthread_join() specifying the same target thread are undefined". However, your copying doesn't work because thread objects are not copyable (copying is semantically meaningless and impossible to achieve with POSIX threads and probably also windows threads); and returning a pointer won't work because it subverts your original intention of making a copy to avoid violating the aforementioned requirement. Usually, if you want multiple threads to wait for a condition such as thread termination you need to use a condition variable. (Although you do not use C++ threads, the same rule that applies to POSIX threads appears to apply to std::thread, because it is a precondition of calling join() that the thread should be joinable(), and a thread ceases to be joinable as soon as join() or detach() returns in a thread: this is not surprising because the standard states, albeit non-normatively, that "These threads [std::thread threads] are intended to map one-to-one with operating system threads".) More generally, your approach seems like an unnecessarily convoluted way of starting a thread. Chris |
Christopher Pisz <nospam@notanaddress.com>: Aug 25 10:30AM -0500 On 8/24/2015 7:17 PM, Chris Vine wrote: > different threads joining on the same thread object: the SUS states > that "the results of multiple simultaneous calls to pthread_join() > specifying the same target thread are undefined". I don't understand the why behind it being semantically wrong. I am not trying have more than one thread in which the joins are called. I am trying to join all the child threads in the main thread. > copying doesn't work because thread objects are not copyable (copying is > semantically meaningless and impossible to achieve with POSIX threads > and probably also windows threads); I am not trying to "create a new thread by copying the thread object. I am trying to "pass some thing the main thread can use to join on" > and returning a pointer won't work > because it subverts your original intention of making a copy to avoid > violating the aforementioned requirement. But it does work in my test. I'll post my edited concept code at the bottom. > Usually, if you want multiple > threads to wait for a condition such as thread termination you need to > use a condition variable. Just the main thread. > (Although you do not use C++ threads, the same rule that applies > to POSIX threads appears to apply to std::thread, because it is a > precondition of calling join() that the thread should be joinable(), It should be > More generally, your approach seems like an unnecessarily convoluted way > of starting a thread. > Chris Maybe so. Garnering suggestions on how to edit it are my goal, but I have a feeling people are misinterpreting what I am trying to do. I just want to start multiple threads from one parent thread and have that parent wait on them all to terminate, unless an signal is obtained, in which case I want the main thread to signal the child threads to terminate. I am trying to also encapsulate all the activity/functionality that takes place on the child threads, in a class. Maybe this concept code does a better job of it: ________ #pragma once // Boost Includes #include <boost/shared_ptr.hpp> #include <boost/thread.hpp> // Standard Includes #include <atomic> //-------------------------------------------------------------------------------------------------- class SomeClass { public: typedef boost::shared_ptr<SomeClass> Shared_Ptr; static void Start(SomeClass & someClass); SomeClass(); ~SomeClass(); void Stop(); void Join(); protected: void ThreadProcedure(); static size_t m_count; size_t m_id; boost::thread m_thread; // Worker thread this object will run on boost::function<void()> m_threadProcedure; // Function object that is the main loop for the thread std::atomic<bool> m_stopping; // Flag to exit loop in thread }; __________ #include "SomeClass.h" // Boost Includes #include <boost/thread.hpp> // Standard Includes #include <iostream> //-------------------------------------------------------------------------------------------------- size_t SomeClass::m_count = 0; //-------------------------------------------------------------------------------------------------- void SomeClass::Start(SomeClass & someClass) { // Start the thread someClass.m_thread.swap(boost::thread(someClass.m_threadProcedure)); } //-------------------------------------------------------------------------------------------------- void SomeClass::Stop() { // Flags the loop in the thread procedure to exit m_stopping = true; } //-------------------------------------------------------------------------------------------------- void SomeClass::Join() { m_thread.join(); } //-------------------------------------------------------------------------------------------------- SomeClass::SomeClass() : m_id (m_count++), m_thread (), // Derived classes should override this behavior by binding their own thread procedure in their constructor m_threadProcedure(boost::bind(&SomeClass::ThreadProcedure, this)), m_stopping (false) { std::cout << "SomeClass #" << m_id << " constructor has been called." << std::endl; } //-------------------------------------------------------------------------------------------------- SomeClass::~SomeClass() { std::cout << "SomeClass #" << m_id << " deconstructor has been called." << std::endl; // If the thread is still running, interupt it and wait for it to join m_thread.interrupt(); m_thread.join(); } //-------------------------------------------------------------------------------------------------- void SomeClass::ThreadProcedure() { std::cout << "SomeClass #" << m_id << " thread procedure has been called." << std::endl; // Will go forever until interupted try { while(!m_stopping.load()) { boost::this_thread::sleep_for(boost::chrono::seconds(60)); std::cout << "SomeClass #" << m_id << " tick..." << std::endl; } } catch(boost::thread_interrupted) { std::cout << "SomeClass #" << m_id << " interupted. Exiting thread." << std::endl; } std::cout << "SomeClass #" << m_id << " stopped. Exiting thread." << std::endl; } ____________ // Project Includes #include "SomeClass.h" // Shared Library #include "Exception.h" #include "PerformanceTimer.h" // Boost Includes #include <boost/thread/thread.hpp> // Standard Library #include <cstdio> #include <iostream> #include <string> #include <vector> #include <cmath> #include <signal.h> //-------------------------------------------------------------------------------------------------- // TODO - Only global for concept testing, later we'll wrap all this in a class std::vector<SomeClass::Shared_Ptr> g_workerObjects; //-------------------------------------------------------------------------------------------------- void SignalHandler(int signal) { // Signal worker thread to exit std::cout << "Halt signal caught. Exiting all threads..." << std::endl; for(std::vector<SomeClass::Shared_Ptr>::const_iterator itWorker = g_workerObjects.begin(); itWorker != g_workerObjects.end(); ++itWorker) { (*itWorker)->Stop(); } } //-------------------------------------------------------------------------------------------------- int main() { // Register a handler for the ctrl-z signal signal(SIGINT, SignalHandler); // Create the workers and start thier threads for(size_t index = 0; index < 10; ++index) { SomeClass::Shared_Ptr worker(new SomeClass()); SomeClass::Start(*worker); g_workerObjects.push_back(worker); } // Wait for the threads to exit std::vector<SomeClass::Shared_Ptr>::const_iterator itWorker = g_workerObjects.begin(); while(itWorker != g_workerObjects.end()) { (*itWorker)->Join(); itWorker = g_workerObjects.erase(itWorker); } // Done return 0; } -- I have chosen to troll filter/ignore all subthreads containing the words: "Rick C. Hodgins", "Flibble", and "Islam" So, I won't be able to see or respond to any such messages --- |
scott@slp53.sl.home (Scott Lurndal): Aug 25 06:16PM >> threads to wait for a condition such as thread termination you need to >> use a condition variable. >Just the main thread. My generic (pthread-based) thread class has the following method that handles the join. When the destructor for the class that inherits from c_thread is called, the thread will be joined. bool in_context(void) { return t_thread == pthread_self(); } /* * Terminate the thread. If called in-context, it will clear the * running flag and exit. If called out of context, it will cancel * the thread and join with it. */ void c_thread::terminate(void) { int diag; if (in_context()) { t_running = false; pthread_exit(NULL); } else { diag = pthread_cancel(t_thread); if (diag == ESRCH) { /* Thread already gone, nothing to do */ } else if (diag != 0) { t_logger->log("%s Unable to cancel thread: %s\n", t_thread_name, strerror(diag)); } void *rval; diag = pthread_join(t_thread, &rval); if (diag == ESRCH) { /* Thread already gone, nothing to do */ } else if (diag != 0) { t_logger->log("%s Thread join failed: %s\n", t_thread_name, strerror(diag)); } t_running = false; } } A class that needs a thread will inherit from c_thread and will implement the virtual c_thread::run() method for the body of the thread. An example ::run method: /** * SSP Thread. This thread will coordinate ownership of a lock between * the hosts which are connected to the SSP instance. */ void c_ssp::run(void) { s_lock.lock(); s_running = true; while (s_running) { s_wait_for_work.wait(&s_lock); if (!s_running) continue; while (s_file->sf_waiter[s_sysnum].sf_waitlist != SSP_TABLE_SIZE) { s_lock_notify *lnp = s_file->sf_notifiers; lnp += s_file->sf_waiter[s_sysnum].sf_waitlist; s_file->sf_waiter[s_sysnum].sf_waitlist = lnp->ln_offset; c_dlist_iterator di (&s_lock_list); while (di.next()) { s_lock_op *lop = (s_lock_op *)di.curr(); if (lop->lo_address == lnp->ln_address) { lop->remove(); s_client->lock_acquired(lop->lo_handle, false); delete lop; lnp->ln_offset = s_file->sf_freelist; s_file->sf_freelist = lnp->ln_this; } } } } s_terminate_wait.signal(); s_lock.unlock(); } /** * Destroy an SSP implementation. */ c_ssp::~c_ssp(void) { s_lock.lock(); s_running = false; // Signal ::run method to terminate s_wait_for_work.signal(); // Awaken ::run method s_terminate_wait.wait(&s_lock); // Wait for ::run method to exit terminate(); // Join with thread if (s_file != NULL) { munmap(s_file, SSP_MAILBOX_SIZE); s_file = NULL; } s_lock.unlock(); } s_running member data item is declared volatile, of course. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 25 09:34PM +0100 On Tue, 25 Aug 2015 10:30:14 -0500 Christopher Pisz <nospam@notanaddress.com> wrote: [snip] > > to POSIX threads appears to apply to std::thread, because it is a > > precondition of calling join() that the thread should be joinable(), > It should be Not in your usage (see below). A thread ceases to be joinable as soon as detach() is called on it, or otherwise at some unspecified time after the thread has terminated between a call to join() being made and that call returning. See in particular §30.3.1.5/2 and §30.3.1.5/6 of C++11. [snip] > threads to terminate. I am trying to also encapsulate all the > activity/functionality that takes place on the child threads, in a > class. Maybe this concept code does a better job of it: OK I can see where you are going. Having a thread handle (which is basically what SomeClass now is) is not a bad idea, given that std::thread is not exception safe for a joinable thread (but boost::thread is). Having protected data members would also normally be regarded as unusual, but presumably it is to make your m_stopping flag visible to a worker thread function provided by a derived class, but in that case I cannot see the point of having the polymorphic function object m_threadProcedure - you might just as well use inheritance with a virtual thread function, much as I try to avoid them except for type erasure purposes. However, on the technical level you still have undefined behaviour. I mentioned that you cannot have more than one thread joining on another according to the C++ and POSIX standards (and, having looked at the boost documentation, I see also according to boost). In your code the destructor of SomeClass calls join() on m_thread and any thread calling your SomeClass::Join() method will do the same. By the time the destructor of SomeClass is entered and calls join() any thread blocking on SomeClass::join() will probably have returned leaving the thread in already detached state and the destructor with undefined behaviour. You say "But it does work in my test". If having non-conforming code which happens to work on a particular compiler version with a particular OS version is enough for the purposes of the contract with your clients, so be it. If you are concerned with portability and standard compliance you would use a condition variable, or remove your SomeClass::Join() function. (As an aside, you will get away with it on any implementation where std::vector iterators are pointers, but manipulating iterators in a signal handler is technically undefined behaviour because no standard library functions are guaranteed to be asynchronous-safe except loads and stores on lock-free atomic objects - §1.9/6 of C++11. std::atomic<bool> is going to be lock free in practice. You might as well use relaxed memory ordering for that usage though.) Chris |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 25 09:59PM +0100 On Tue, 25 Aug 2015 21:34:56 +0100 Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote: [snip] > signal handler is technically undefined behaviour because no standard > library functions are guaranteed to be asynchronous-safe except loads > and stores on lock-free atomic objects - §1.9/6 of C++11. Actually, because your main() function and your signal handler both manipulate g_workerObjects, in your test case you will only get away with it if the thread in which the signal handler runs is the thread in which main() runs. To do that you would need to block signals in all but the main thread using sigprocmask()/pthread_sigmask() or whatever equivalent your OS offers. Otherwise you would need a mutex to protect g_workerObjects, and mutexes are definitely not asynchronous safe. In multi-threaded code I always use sigwait() to deal with asynchronous signals safely. Chris |
"Öö Tiib" <ootiib@hot.ee>: Aug 24 05:50PM -0700 On Monday, 24 August 2015 15:27:58 UTC+3, David Brown wrote: > small difference. For example, if a function bar() contains a call to > an external function foo(), the code generated for bar() must take into > account the possibility that foo() throws an exception. Exceptions are expensive only when used wrongly to signal about quite common situations. If foo() throws exceptions, but rarely, (lets say once from 20000 calls) then exception is actually measurably cheaper (and also results with cleaner code) than to check return values and propagate those up stack. It is because exception is cheaper than return value checking in good case situation. One issue with exception is when stack unwinding takes unacceptably long time but we we need both to succeed and to fail fast. It is rare case, some critical real time processing for example. For fixing that we can still use return values in C++ like we anyway do with non-exceptional edge cases. > This can mean > different requirements for stack layout or restrictions to optimisations > and re-organisations that would not apply in the case of plain C. That only applies when we are sure that there can't be any exceptional failures. If foo() can not throw exceptions then we have to declare foo() 'noexcept' in C++ to gain same benefit. > could have been written better or faster using C++ rather than C. > (I haven't tried look at this in detail myself - maybe I will do so if I > get the time.) For whatever braindead reasons even the destructors and extern "C" functions are not made 'noexcept' by default. One could indicate with 'noexcept(false)' if there really is throwing destructor or extern "C" function but standard is backward there. IOW we have to use 'noexcept' massively if we want to get really rid of that one percent as well. |
Richard Damon <Richard@Damon-Family.org>: Aug 24 10:19PM -0400 On 8/24/15 8:50 PM, Öö Tiib wrote: > 'noexcept(false)' if there really is throwing destructor or extern "C" > function but standard is backward there. IOW we have to use 'noexcept' > massively if we want to get really rid of that one percent as well. The issue is that there is nothing in the language to say that destructors or extern "C" functions naturally can't throw. We normally try not to have destructors throw, as a destructor throwings in the midst of processing another exception can cause problems. extern "C" functions may well be written in C++ or call C++ functions which might throw, so we can't presume them not to throw. The biggest issue is that the first version of C++ defaulted to anything could throw (and I think it would be a mistake to require all functions that might throw to indicate this), backwards compatibility make it hard to change that default. |
"Öö Tiib" <ootiib@hot.ee>: Aug 25 12:07AM -0700 On Tuesday, 25 August 2015 05:19:25 UTC+3, Richard Damon wrote: > > massively if we want to get really rid of that one percent as well. > The issue is that there is nothing in the language to say that > destructors or extern "C" functions naturally can't throw. There are no things natural. Like that members or bases of class (but not struct) are private by default. Why not protected? Because standard say private, nothing inherently natural. Authors thought that in such a way it is more convenient. So the question only is: what is more convenient default 'noexcept(false)' or 'noexcept(true)'? I would say that for destructors and extern "C" functions the more natural default is 'noexcept(true)' for all the rest 'noexcept(false)'. > We normally try not to have destructors throw, as a destructor throwings > in the midst of processing another exception can cause problems. So normal and usual (IOW default) is 'noexcept(true)'? > extern "C" functions may well be written in C++ or call C++ functions > which might throw, so we can't presume them not to throw. However the normal and usual (IOW default) is 'noexcept(true)'? > could throw (and I think it would be a mistake to require all functions > that might throw to indicate this), backwards compatibility make it hard > to change that default. On such obvious cases it is the bad of backwards guys if they miss an unit test that checks the test case where their throwing destructor or extern "C" function does actually throw. It is typically causing nuisance if someone throws C++ exceptions over C interface or out of destructor so if it really is intended then it should be explicitly marked that way as well IMHO. |
Juha Nieminen <nospam@thanks.invalid>: Aug 25 08:16AM > number of structs, it is typical to have another struct as a "vtable". > then you would access it as: > obj->vt->Whatever(obj, ...); And then they say that C is "faster" than C++... (Sure, that resembles virtual functions in C++, but a) in C++ you are not forced to have every member function being virtual, which means that the compiler elides the indirection, and may even be able to inline the function, and b) even with virtual functions the compiler may be able to perform optimizations that it can't do with an explicit "virtual table" like that.) --- news://freenews.netfront.net/ - complaints: news@netfront.net --- |
jacobnavia <jacob@jacob.remcomp.fr>: Aug 25 10:28AM +0200 Le 24/08/2015 14:27, Juha Nieminen a écrit : > This requirement is "inherited" by anything that uses such data containers. > For example, if you put such a data container in a struct, the struct > automatically "inherits" the need for manual construction and destruction. Yes, you have to call the destructors explicitely. It is done using container.Finalize // release all memory and the container itself container.Clear // release all memory and reset the container > With complex data structures, it can become really complicated to track > which structs require manual construction and destruction, and which don't. It is very simple. When you are finished with a container you should call its Finalize method. > And we all know how error-prone (and tedious) non-RAII manual destruction > can be. It's one of the biggest sources of bugs in C programs out there. Yes but this is the price to pay to avoid C++ complexity that is also a source of many bugs. > some dynamic data structure, and you need to create another dynamic > data structure containing elements of that struct type.) The data > structure needs to know how to destroy those elements appropriately. No. Data is always copied into the container and the container manages its own data space so you just have to call its Finalize/Clear method. Tere are NO shared data between containers. > The classical way of doing that is to add "member functions" to structs > for construction and destruction... as function pointers. Which increase > the size of the struct, thus adding overhead. No. The way it is done in the CCL is that you have a SINGLE function pointer table (the *interface* object for each container) and you call the functions in there. Note that you can at any moment substitute the given functions by your own. > they are not virtual. Even if they are virtual, the penalty is that > of one single pointer regardless of the number of virtual member > functions.) In the CCL too. No size penalty for your structures that do NOT store any pointers at all. > And debugging macro-based dynamic data containers must be a joy. It is. The source code is available and easy to debug. No templates, no problems debugging template code. It is funny how you ignore the difficulty of debugging any STL container or even JUST UNDERSTANDING the error messages whe necessary. For instance here is an example of the C macros used: 102 static int Sort(VECTOR_TYPE *AL) 103 { 104 CompareInfo ci; 105 106 if (AL == NULL) { 107 return NullPtrError("Sort"); 108 } 109 ci.ContainerLeft = AL; 110 ci.ExtraArgs = NULL; 111 qsortEx(AL->contents, AL->count, AL->ElementSize, AL->CompareFn, &ci); 112 return 1; 113 } The only generic argument is the type name of the elements stored in the vector: "VECTOR_TYPE". Is this too much for you? |
jacobnavia <jacob@jacob.remcomp.fr>: Aug 25 10:31AM +0200 Le 25/08/2015 10:16, Juha Nieminen a écrit : >> then you would access it as: >> obj->vt->Whatever(obj, ...); > And then they say that C is "faster" than C++... It is. > not forced to have every member function being virtual, which means > that the compiler elides the indirection, and may even be able to > inline the function, Of course the C compilers could do exactly the same, nothing prevents a C compiler for doing that optimization. They do not do it now because there is not much usage in C for that. The first C++ compilers did not have that optimization either. and b) even with virtual functions the compiler > may be able to perform optimizations that it can't do with an explicit > "virtual table" like that.) The C compilers could do that too. |
Ian Collins <ian-news@hotmail.com>: Aug 25 08:36PM +1200 jacobnavia wrote: > problems debugging template code. It is funny how you ignore the > difficulty of debugging any STL container or even JUST UNDERSTANDING the > error messages whe necessary. There is no problem debugging template code, it's just code. If you want to avoid cryptic error messages, use a compiler other than gcc :) -- Ian Collins |
David Brown <david.brown@hesbynett.no>: Aug 25 11:09AM +0200 On 25/08/15 02:50, Öö Tiib wrote: > (and also results with cleaner code) than to check return values and > propagate those up stack. It is because exception is cheaper than return > value checking in good case situation. That's all true. You can think of the exception and catching as being a bit like checking for error return values, but heavily optimised for the "no error" case. This makes exceptions faster than traditional C-style error checking. My point here is in comparison to /no/ error checking - because you know there won't be an error, or because you don't care about what happens if there is an error. In this case, the code generated for C++ that runs when there is no exception may not be as optimal as if the compiler knew that no exception could possibly happen. > That only applies when we are sure that there can't be any exceptional > failures. If foo() can not throw exceptions then we have to declare > foo() 'noexcept' in C++ to gain same benefit. Yes, using "noexcept" should (I think) give these benefits. But you would have to use it extensively, and AFAIUI it can cause overhead in functions that are declared "noexcept" (since they have to check if there are exceptions in functions that they call, and then call std::terminate()). > 'noexcept(false)' if there really is throwing destructor or extern "C" > function but standard is backward there. IOW we have to use 'noexcept' > massively if we want to get really rid of that one percent as well. I have read that destructors are implicitly "noexcept" in C++11, but I can't say I follow all the details: <https://akrzemi1.wordpress.com/2013/08/20/noexcept-destructors/> The trouble with extern "C" functions being default "noexcept" is that C++ code can call a C function which calls a C++ function which throws - the exception should make its way back. For an extern "C" function to be "noexcept", you have to be sure that any unhandled exceptions result in an std::terminate() - and that will not be the case for all extern "C" functions. |
Thomas Richter <thor@math.tu-berlin.de>: Aug 25 04:07PM +0200 On 25.08.2015 15:50, Stefan Ram wrote: > I have little experience with this. > When can I use the »noexcept« specification and not > be lying to the implementation? Whenever it is acceptable that a thrown exception terminates the program. > In my C++ course, at the beginning, I use > - arithmetic operators, can they throw? If these are arithmetic operators on the built-in types, then no. > - »::std::cout << ...«, can this throw? Well. Here we already have something like a corner case. No, they don't throw on I/O exceptions because std::cio predates exceptions, so a different mechanism is used to indicate I/O errors. *However* it does not sound too implausible that these functions are library implementations that, as part of the implementation, use the global new operator in one place or another, and *this* operator can throw. I wish C++ would be more clear which library functions can throw which exception, *including* errors from memory allocation. I'm also unclear whether a C++ library is required to mark functions as noexcept if it can guarantee that they are noexcept. Probably not. > . Another approach would be to mark /every/ function > »noexcept« in some programs, /even if/ it can throw. This is usually a bad idea as makes any exception processing impossible. > If I read 15.4p10 correctly, the worst thing that can > happen is that »::std::unexpected()« or »::std::terminate()« Actually, the latter, not the former. The former happens if an exception tries to propagate outside of a function marked as throw() (which is deprecated). > But if the exception in this case still /is/ "handled" > in this sense, will it really be faster with the > »noexcept« specification? That I cannot answer. One of the reasons for deprecating throw() was that it was essentially equivalent to try { ... function body ... } catch() { std::unexpected(); } so the compiler had to generate *more* code to process throw() instead of less code, so it is usually of no advantage (speed-wise) to mark a function as throw(). And what can you actually do in std:unexpected() except printing an error and terminating the program anyhow? Why that changes if std::unexpected() is replaced by std::terminate(), as it happens if throw() is replaced by noexcept, is another question. Probably because it does not require the compiler to unwind the stack, i.e. the program may terminate right away. Whether that is any better I do not know. Actually, I believe that it might have probably been better just to say that noexcept might have caused UB in case an exception tries to propagate out of such a function, including behavior such as "works and just propagates the exception", "does not destroy objects on the stack" to "just crashes". It would be closer to the "you do not need to pay what you do not need" approach C++ typically takes in such cases. Greetings, Thomas |
BGB <cr88192@hotmail.com>: Aug 25 09:14AM -0500 On 8/25/2015 3:16 AM, Juha Nieminen wrote: > inline the function, and b) even with virtual functions the compiler > may be able to perform optimizations that it can't do with an explicit > "virtual table" like that.) the non-virtual case is: Foo_Whatever(obj, ...); however, what one may note though, is that if an indirect call from a certain location tends to always land on the same target, then the CPU catches this case and makes it a bit faster. otherwise, one is still faced with a similar cost for pretty much every call for targets which use ELF (pretty much every non-inline call tending to be implemented as an indirect call through the GOT). |
"Öö Tiib" <ootiib@hot.ee>: Aug 25 11:49AM -0700 On Tuesday, 25 August 2015 12:09:29 UTC+3, David Brown wrote: > functions that are declared "noexcept" (since they have to check if > there are exceptions in functions that they call, and then call > std::terminate()). That check is most likely made by implementation of 'throw' not by functions. We assume that 'throw' can't happen. If it happens then it finds a noexcept function in stack and says "Phew, lucky me! I do not need to unwind anything today. :) Terminate!". > I have read that destructors are implicitly "noexcept" in C++11, but I > can't say I follow all the details: > <https://akrzemi1.wordpress.com/2013/08/20/noexcept-destructors/> You are correct! I was wrong. Opposite behavior is bug in old version of gcc that I had to use recently and it has been fixed since gcc 4.8. So on conforming compiler the destructors are already 'noexcept(true)' and I can take back that part of my complaint. > be "noexcept", you have to be sure that any unhandled exceptions result > in an std::terminate() - and that will not be the case for all extern > "C" functions. I have heard that example before but it does not make sense to me. C function should be capable of calling only extern "C" functions. If such are 'nothrow(true)' then how those can throw into C? If C code is somehow calling 'nothrow(false)' function and that throws then C++ exception crosses language boundaries and that is undefined behavior AFAIK. C function does have no ways to release (or close or unlock) any resources it acquired since it knows nothing of C++ exceptions. If the exception passes it somehow and further is even caught by C++ that called the C function then we have alive nasal daemons instead of 'std::terminate'. |
David Brown <david.brown@hesbynett.no>: Aug 25 09:32PM +0200 On 25/08/15 20:49, 嘱 Tiib wrote: > exceptions. If the exception passes it somehow and further is even > caught by C++ that called the C function then we have alive nasal > daemons instead of 'std::terminate'. This is getting /way/ outside my level of C++ (I don't actually do much C++ at the moment, and when I do it is mostly on embedded systems - and exceptions are disabled entirely). But my understanding is that an extern "C" function is not treated any differently with regard to exceptions - the function can throw, and it can pass on thrown exceptions down the chain. The extern "C" merely means there is no name mangling (thus no overloading or default parameters), and may conceivably alter calling conventions. But as said, I am far from sure on this - hopefully someone more knowledgeable will make things clear. |
"A. Bolmarcich" <aggedor@earl-grey.cloud9.net>: Aug 25 01:51PM -0500 > reason anyone would ever want to use copy initialization > when writing new code? Are there use cases where copy > initialization is much better than direct initialization? In "A Tour of C++", Bjarne Stroustrup gave the following advice: "Prefer the = syntax for the initialization in declarations using auto." |
ram@zedat.fu-berlin.de (Stefan Ram): Aug 25 01:50PM >function but standard is backward there. IOW we have to use 'noexcept' >massively if we want to get really rid of that one percent as well. I have little experience with this. When can I use the »noexcept« specification and not be lying to the implementation? In my C++ course, at the beginning, I use - arithmetic operators, can they throw? - »::std::cout << ...«, can this throw? - can one use the 5.3.7 »noexcept« operator in the above cases to find answers to the above questions? (If so, could this be done by C++ automatically, marking a function as »noexcept« when the »noexcept« operator yields »true« for every expression in the function?) - library functions, they should be marked appropriately? . Another approach would be to mark /every/ function »noexcept« in some programs, /even if/ it can throw. If I read 15.4p10 correctly, the worst thing that can happen is that »::std::unexpected()« or »::std::terminate()« is called, and for certain programs this might be ok. But if the exception in this case still /is/ "handled" in this sense, will it really be faster with the »noexcept« specification? |
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