- Destructor elision - 4 Updates
- Some C++14 code - 5 Updates
- How exceptions are implemented? - 6 Updates
- "Jonathan Blow: "C++ is a weird mess"" - 7 Updates
- Concept for location - 2 Updates
- Fizz Buzz - 1 Update
Thiago Adams <thiago.adams@gmail.com>: Jul 19 07:06AM -0700 Something I would like to see in C++ is the idea of "destructor elision". Similarly of c++ copy elision, where the compiler is allowed to change the observed behavior, I would like to give the compiler the chance to remove the destructor call and change the observable behavior. Basically this feature gives us optimization after to move objects. std::unique_ptr<int> p1 = std::make_unique<int>(); std::unique_ptr<int> p2; p2 = std::move(p1); in this case, the compiler would be allowed to remove the destructor call of the p2. |
Thiago Adams <thiago.adams@gmail.com>: Jul 19 07:17AM -0700 On Thursday, July 19, 2018 at 11:06:17 AM UTC-3, Thiago Adams wrote: > p2 = std::move(p1); > in this case, the compiler would be allowed to remove > the destructor call of the p2. Correction: (p1 - not p2) ..In this case, the compiler would be allowed to remove the destructor call of the p1. |
Thiago Adams <thiago.adams@gmail.com>: Jul 19 07:38AM -0700 On Thursday, July 19, 2018 at 11:17:18 AM UTC-3, Thiago Adams wrote: > Correction: (p1 - not p2) > ..In this case, the compiler would be allowed to remove > the destructor call of the p1. This feature also would allow us to call destructors before the end of scope. Sample of scope created to call the destructor: //... { Lock lock; //..something } //... We could write: //... Lock lock; //..something std::destroy(lock); //move to nothing //... and them this feature give us the same control we have in manual C code, plus the guarantee of RAII. |
Bo Persson <bop@gmb.dk>: Jul 19 08:19PM +0200 On 2018-07-19 16:06, Thiago Adams wrote: > p2 = std::move(p1); > in this case, the compiler would be allowed to remove > the destructor call of the p1. You know, perhaps it already does that? Here is the result on MSVC 2017: #include <memory> int main() { 00007FF7893D1000 sub rsp,28h std::unique_ptr<int> p1 = std::make_unique<int>(); 00007FF7893D1004 mov ecx,4 00007FF7893D1009 call operator new (07FF7893D1024h) 00007FF7893D100E xor ecx,ecx 00007FF7893D1010 mov dword ptr [rax],ecx std::unique_ptr<int> p2; p2 = std::move(p1); } 00007FF7893D1012 lea edx,[rcx+4] 00007FF7893D1015 mov rcx,rax 00007FF7893D1018 call operator delete (07FF7893D1060h) 00007FF7893D101D xor eax,eax 00007FF7893D101F add rsp,28h 00007FF7893D1023 ret What destructor call do you want to remove? Bo Persson |
bitrex <user@example.net>: Jul 19 01:32PM -0400 > bothered about calling free then just use alloca() and allocate on the stack. > No free() required though there may be limits on how much memory the OS or > CPU will allow. alloca() is non-portable, and some implementations are bugged. "May be limited" in practice means "Always by default very limited on all modern PC architectures and OSes." |
bitrex <user@example.net>: Jul 19 01:42PM -0400 >> So removing something that has no cost is a sound engineering decision? > There's no such thing as no cost in creating and destructing objects no matter > how slimline they may be and smart pointers are objects. If a unique_ptr object has no persistent state other than the wrapped pointer itself, which is the same persistent state the equivalent smart-pointer-free code would have, and the rest of the object's behavior is simply no different than the effect of writing "new" and "delete" at the appropriate times (or malloc/free in C) then that is precisely what the compiler will do and that's what the compiler will generate. What sense would it make to do anything else? |
bitrex <user@example.net>: Jul 19 01:46PM -0400 >> So removing something that has no cost is a sound engineering decision? > There's no such thing as no cost in creating and destructing objects no matter > how slimline they may be and smart pointers are objects. You're laboring under the assumption that every time you write Object object; in your code the compiler is actually producing code to execute constructors (that may do nothing at all), jump into destructors (that may do nothing at all), making stack allocations, assigning-immediate class member variables, running all around doing this and that. Every time. That isn't how modern optimizing compilers work. |
bitrex <user@example.net>: Jul 19 01:56PM -0400 >> So removing something that has no cost is a sound engineering decision? > There's no such thing as no cost in creating and destructing objects no matter > how slimline they may be and smart pointers are objects. It's entirely possible to write some 250 line class or structure which compiles down to just a couple of assembly instructions, like say a load immediate and a move of something from one memory address to another. "Why in the world would I write all that code to accomplish two assembly instructions?" Simple, the point of all that code was for your benefit, not the computer's instruction set architecture. That is to say the code allows the compiler to figure out exactly what memory addresses to use for that single load and move in every context you need, automatically. Instead of you having to write it out 273 times by hand. |
Bart <bc@freeuk.com>: Jul 19 06:58PM +0100 On 19/07/2018 18:46, bitrex wrote: > may do nothing at all), making stack allocations, assigning-immediate > class member variables, running all around doing this and that. Every time. > That isn't how modern optimizing compilers work. But they /might/ do all that? Or if they don't, they might be expending effort in assembling all those operations, and then expend more effort in figuring out how to get rid of them again. And then people complain about build times. -- bart |
boltar@cylonHQ.com: Jul 19 08:41AM On Wed, 18 Jul 2018 10:13:54 -0700 (PDT) >> See above. >It was removed from the second version of the C standard, in 1999, so that >doesn't make much difference. Still valid in all C compilers. |
jameskuyper@alumni.caltech.edu: Jul 19 04:32AM -0700 > >It was removed from the second version of the C standard, in 1999, so that > >doesn't make much difference. > Still valid in all C compilers. You've checked every single C compiler ever written? Impressive - I wouldn't know how to even begin such a review. There's no point in discussing what non-conforming implementations of C or C++ do - there's no limits on the possibilities. I'm only interested in discussing the behavior of compilers when invoked in a mode that fully conforms to some version of the relevant standard, and unless otherwise specified, that's the current version. Since 1999, neither the current C standard nor the current C++ standard has supported implicit int. |
boltar@cylonHQ.com: Jul 19 01:17PM On Thu, 19 Jul 2018 04:32:41 -0700 (PDT) >> Still valid in all C compilers. >You've checked every single C compiler ever written? Impressive - I >wouldn't know how to even begin such a review. Both gcc and clang require a command line switch to strictly conform to the C99 standard and MSVC++ doesn't even support C99 properly anyway. The de facto C standard is still C89 and probably always will be. |
jameskuyper@alumni.caltech.edu: Jul 19 08:50AM -0700 > Both gcc and clang require a command line switch to strictly conform to the C99 > standard and MSVC++ doesn't even support C99 properly anyway. The de facto > C standard is still C89 and probably always will be. So "all C compilers" is equivalent to "gcc, clang, and MSVC++"? Wow - what a simple world you live in! Mine's a bit bigger and more complicated than yours. How many different countries are there in your world? How many different languages? Religions? How small is it, really? |
David Brown <david.brown@hesbynett.no>: Jul 19 07:28PM +0200 >>>>> the "landing pads" and tables state->landing pads. >>>> Yes, it trades some disk space for speed, but the size of executables >>>> does not interest most people nowadays (except some nutwits in this group). It interests embedded programmers too. If you have a fixed size of code flash on a device, and your program is nearing that limit, size begins to matter a great deal. Code size can also be relevant in terms of cache performance - less code means higher hit ratios. Of course, this does not matter for things like exception tables - only for the commonly executed code. > thinking "that's the most pointy haired thing I've every > heard of!". Come to this talk and learn how this simple > metric provides surprisingly strong counter pressure to complexity. Binary size deltas can be an interesting measure for code reviews - they say something about how much of the program has changed. That doesn't mean that keeping binary sizes small is a useful goal in itself - keeping /complexity/ low is the aim. |
Thiago Adams <thiago.adams@gmail.com>: Jul 19 10:54AM -0700 On Wednesday, July 18, 2018 at 10:05:07 AM UTC-3, Thiago Adams wrote: ... > It must have some overhead in size because it has to build > the "landing pads" and tables state->landing pads. Something interesting to compare as well, is the code generated when we have return points in the middle of the code. int F(int i) { if (i > 0) { X x; if (i < 2) return 0; Y y; if (i < 3) return 0; Z z; if (i < 4) return 0; } return 0; } Differently from exceptions, everything is static. I believe the size can also be affected having more return points in the middle of the code and the compiler will try to reuse some states. Instead of "landing pad" in this case we have "exit points" that would call some destructors and not the others. I would like to understand how compilers works to generate this code (in case someone has links about this). Maybe is something like an DFA with minimization. |
boltar@cylonHQ.com: Jul 19 08:44AM On Wed, 18 Jul 2018 19:14:18 +0200 >Templates: none >Iostream: none >Multiple inheritance: none Does multiple inheritance get used much anyway? I can count the number of times I've seen it in my 20+ year career on one hand. To me it seems like one of those features that should be useful but almost never is because composition or aggregation are almost always a better solution. |
Rosario19 <Ros@invalid.invalid>: Jul 19 12:07PM +0200 On Thu, 19 Jul 2018 08:44:24 +0000 (UTC), boltar wrote: >><quote> >>So we limited our code to "Embedded C++�€? constructs >>Exceptions: none this seems to me ok... each function return error if can, and each that call see if some error (if this is important) >>Templates: none where templates are the problem? the code that has <int> is traslated as it is one int type in what i understand >>Iostream: none than one has to write his own routines... seems ok, if one not introduce bugs (one can rewrite iostream eliminate their i presume bug (can not possible to check for sucessfull/wrong input or remember it wrong? because i rewrote getint getdouble with cin input etc)) possible now make error more than usual because some grappa >>Multiple inheritance: none i don't know the meaning... |
Rosario19 <Ros@invalid.invalid>: Jul 19 12:20PM +0200 On Thu, 19 Jul 2018 12:07:45 +0200, Rosario19 wrote: >>>Exceptions: none >this seems to me ok... each function return error if can, and each >that call see if some error (if this is important) "exceptions" could be ok in some exceptional case but i could be wrong on this (and in all the remain) this is just my opinion |
scott@slp53.sl.home (Scott Lurndal): Jul 19 02:21PM >would have expected that all the emacs afficionados would have >automated refactoring implemented in elisp, but nope. They have >essentially the same environment I had in 1988. If it works for them, why should they change? |
scott@slp53.sl.home (Scott Lurndal): Jul 19 02:29PM >I've seen it in my 20+ year career on one hand. To me it seems like one of >those features that should be useful but almost never is because composition >or aggregation are almost always a better solution. From a current project: class c_xyz: public c_device, public c_tx_sink_interface, public c_tx_src_interface, public c_rx_sink_interface, public c_rx_src_interface, public c_memory_accessors class c_zyx: public c_device, public c_thread, public c_memory_accessors, public c_pci_callback Granted in most cases the secondary base classes are pure virtual interface classes (c_thread is an exception to that - it automatically creates a thread and invokes the derived class 'run' function from that thread after the derived class constructor completes). |
boltar@cylonHQ.com: Jul 19 03:08PM On Thu, 19 Jul 2018 14:29:52 GMT > public c_rx_src_interface, > public c_memory_accessors >class c_zyx: public c_device, I hope your real class names are better than c_xyz and c_zyx! >to that - it automatically creates a thread and invokes the >derived class 'run' function from that thread after the >derived class constructor completes). And if the derived class constructor doesn't complete but throws an exception? Presumably you have to tell the thread not to bother in the exception handler. That seems a rather obtuse and error prone way to obtain a new thread. |
scott@slp53.sl.home (Scott Lurndal): Jul 19 03:43PM >> public c_memory_accessors >>class c_zyx: public c_device, >I hope your real class names are better than c_xyz and c_zyx! Yes, they were altered to avoid exposing proprietary designators. >>derived class 'run' function from that thread after the >>derived class constructor completes). >And if the derived class constructor doesn't complete but throws an exception? They cannot, by design. |
gof@somewhere.invalid (Adam Wysocki): Jul 19 08:09AM > - Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object. No idea here. > - Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack. The only solution I see is creating a "wrapper" class that would instantiate the desired class on the heap even if the wrapper is instantiated on the stack. -- [ Adam Wysocki :: Warsaw, Poland ] [ Email: a@b a=grp b=chmurka.net ] [ Web: http://www.chmurka.net/ ] |
Paavo Helde <myfirstname@osa.pri.ee>: Jul 19 04:11PM +0300 > I would like to create a class/template but want to prevent at compile time where it can live. Seems like type_traits or the upcoming concepts cannot be use for that. This is against the general idea of modularity. The idea is to provide building blocks which can be combined flexibly by the users of those building blocks. Restricting the object placement without a very good reason limits this flexibility and should be enforced by project guidelines/policies, not by the software libraries themselves. > I have two scenarios: > - Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object. What do you mean by "safe dynarray"? What is "unsafe dynarray"? One option is to define operator new and operator new[] for the class as =delete. However, the client code can easily circumvent this by placing the class inside a wrapper. Anyway, I do not see a reason why something which can be placed on the stack should not be allocated dynamically. In some very rare circumstances one might even want to allocate a mutex lock dynamically. > - Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack. I have no idea what could be such a Big object so that its size is huge and known at compile time already. A table of possible Rubic cube states? Seems more like a non-existing issue. Enforcing on heap might be useful for polymorphic types which are supposed to be accessed by pointers only. But then the issue already mostly resolves itself, if the client code wants to use an interface taking a smartpointer to object, it first needs to have one, and for that it needs to construct the object dynamically, for example by std::make_unique(). This can even be enforced a bit by defining a private destructor: #include <memory> class A { private: ~A() {} friend struct std::default_delete<A>; public: // ... }; An A can now be created with std::make_unique(). For std::make_shared it seems to become more complicated unfortunately and I haven't found a good solution, other than providing a custom smartpointer class which does not allow creation from a raw pointer, and providing static factory methods for creating the objects dynamically only, as suggested by other posts. > I guess being able to enforce/prevent being global could be usefull. Example an object you don't want global because its constructure depends on some stuff initialised in the main(). Mutable globals are indeed evil and should be avoided. In this example it seems quite easy to achieve, the object constructor should just take "the stuff initialized in the main()" as a parameter (and of course the "stuff" itself should be only created when initialized, i.e. not a global). > Does this idea make any kind of sense? Not much, to be honest. I am even doubting my own long-winded efforts to suppress stack initialization. |
gof@somewhere.invalid (Adam Wysocki): Jul 19 12:25PM > Somebody posted on comp.lang.asm.x86 an algorithm for fizz buzz. There are many language tricks and hacks to make the solution interesting, but I prefer solutions that are readable and simple enough to do the task. The cleanest (definitely not the shortest) solution I could come up with (in C++): #v+ #include <boost/format.hpp> #include <iostream> static const unsigned MIN_NUMBER = 1; static const unsigned MAX_NUMBER = 100; /** * \brief Check if value is divisible by another value * * \retval true Dividend is divisible by divisor * \retval false Value is not divisible by divisor */ static inline bool isDivisible(unsigned dividend, unsigned divisor) { return !(dividend % divisor); } /** * \brief Make word according to FizzBuzz rules * * Returns word according to FizzBuzz rules, that is: * * - if value is divisible by 3 and not by 5, returns "Fizz" * - if value is divisible by 5 and not by 3, returns "Buzz" * - if value is divisible both by 3 and 5, returns "Fizz Buzz" * - as the last resort, returns string containing the value * * \param number Number to check * \return String according to FizzBuzz rules */ static std::string makeWord(unsigned number) { std::string word; if (isDivisible(number, 3)) word += "Fizz"; if (isDivisible(number, 5)) { if (!word.empty()) word += " "; word += "Buzz"; } if (word.empty()) word = boost::str(boost::format("%u") % number); return word; } int main() { for (unsigned i(MIN_NUMBER); i <= MAX_NUMBER; ++i) { const std::string suffix(i != MAX_NUMBER ? ", " : "\n"); std::cout << makeWord(i) << suffix; } return 0; } #v- -- [ Adam Wysocki :: Warsaw, Poland ] [ Email: a@b a=grp b=chmurka.net ] [ Web: http://www.chmurka.net/ ] |
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