- A "better" C++ - 6 Updates
- About virtual destructor for polymorhism deletion - 6 Updates
- milestone of compilation errors - 1 Update
- Deep copying - 1 Update
- A quick C++11 initializer list question (for set) - 4 Updates
BGB <cr88192@hotmail.com>: Aug 19 11:14PM -0500 On 8/19/2015 3:46 AM, Juha Nieminen wrote: > limited form of templates just for that purpose (which is, of course, > "better" than templates. Because reasons. Perhaps because they are > not called "templates", as that word is evil.) yes, 'generics'... potentially though, a more expanded form of type-inference could be another possible option (say, rather than using templates, you could have 'auto' be able to work across function and method calls, or within class members, ...). granted, this wont really buy a whole lot (more just redistributing complexity, rather than eliminating it). in such a case, internally classes and methods are specialized based on how they are used, so calling a method with a given parameter list will cause the compiler to generate a version of the method which accepts those parameters, working on a version of the class with the members using the inferred type. the compiler may still reject code for which "impossible" situations arise in the type-inference (an inferred variable being used as inconsistent types). > reasons. (And again, because it's not named "multiple inheritance", > even though it really is. That name is evil, so if we avoid it, > then we are A-ok.) I personally suspect this is more about simplifying the implementation than about simplifying use of the language. MI (as it works in C++) is a fair bit more complicated at the implementation level than SI+interfaces. class layouts are append only with SI, and a lot of hairy edge cases and funky semantics in the inheritance tree are simply not allowed. nevermind if globing together a mostly no-op base class with a whole bunch of interfaces providing default methods almost may as well be MI. > more difficult to swap the contents of the objects), but those are not > important. And we don't mind that dynamically allocated objects tend > to consume more memory. RAM is cheap, just buy more of it. yeah, this is one area where a lot of languages have fallen on their face. some languages have fudged it, like in some implementations (mostly in embedded land), there are implementations of Java which remove the GC and sort of kludge-on manual memory management. my own languages have basically just ended going in similar directions to C++ on this front (using a mix of manual memory management and RAII like patterns). but, they aren't really meant to replace C++ (rather more for use in a niche use-case). > And our "better" C++ compiling ten times faster than C++ is always > something to brag about. I suspect C and C++ really need some sort of good and standardized precompiled header mechanism here. usually compilers cripple it by either making it stupid ("stdafx.h" in MS land), or by having arbitrary limitations in the name of making it behave exactly as the non-PCH case (only works provided each compilation using has exactly the same sequence of #include directives or similar). IMO, "better" would be allowing it to be partially undefined how things like preprocessor defines work across PCH boundaries (use of the PCH mechanism in headers would be explicit and will essentially opt-out of being sensitive to preceding preprocessor definitions). the header would then contain some magic to tell the compiler ("hey, it is safe to use me as a self-contained PCH"). > of "better" C++'s during the last 20 years. A couple of them have been > moderately successful, the vast majority of them have been forgotten. > But they are still better than C++, dammit! this is because they either seriously screw something up, or try to aim their claims too high (promoting their language as the new next best thing, rather than as a language intended for a specific set of application domains). say, I have a scripting language which is aimed mostly at real-time embedded on "medium end" 32-bit targets (MB of RAM and Flash and maybe 100s of MHz), while mostly overlooking lower-end 32-bit targets (kB of RAM and Flash), or 8/16-bit targets (such as the MSP430 or AVR8). meanwhile, it also loses out to its older "sibling" if doing basically similar stuff on a PC (this older sibling basically doing things more useful for use-cases on a PC). it then exists mostly as a way to gloss over asynchronous execution on top of an event-driven cooperative scheduler with lots of latency constraints, which can otherwise get reasonably hairy in C and C++. for something like an MSP430, there is really nothing a script-language can offer over C, and ROM and RAM are enough of a premium that it doesn't really make much sense to use a more complex runtime or an interpreter. it is fairly cramped even with fairly trivial C programs, like 500 lines and you have basically already used up the 2kB of ROM space (in the MSP430G2232 or similar). on a PC (or a higher-end ARM target), one generally doesn't need to deal with tight latency constraints, and the RAM and address space aren't particularly cramped, so one can afford using a bunch of RAM and a JIT compiler. but, saying that it mostly only makes sense on a range of ARM-based controllers or similar is a lot less impressive sounding than claiming it is the new language which does good for everything and will overthrow whatever came before. other languages aim for business or desktop applications, with pretty much the same sort of issues. |
"Chris M. Thomasson" <nospam@nospam.nospam>: Aug 19 09:27PM -0700 > > It depends on what one wants to do with it. > Early C++ had destructors. If that had bee all it offered, it still > would have been a better C! lol! :^D |
jacobnavia <jacob@jacob.remcomp.fr>: Aug 20 08:39AM +0200 Le 20/08/2015 01:08, Chris Vine a écrit : > way you appear to have done is that they are completely unhygienic. > They both inject names into the user code, and accept names from the > scope in which they execute. Yes. They inject names like "ITERATOR" or "DATA_TYPE" ( as macro names) and they produce composed names like Iterator_MyStructure. The DATA_TYPE macro doesn't inject any names since it will be replaced by the type name you are using for the vector. You have to resort to trying to write > renaming conventions for macros by hand, but which in C is impossible > to do in an automated way because (to the best of my knowledge) the C > preprocessor offers no facilities to do so. ? I do not follow that. What could happen with the example I gave: struct ITERATOR(DATA_TYPE) { Iterator it; VECTOR_TYPE *L; size_t index; unsigned timestamp; unsigned long Flags; DATA_TYPE *Current; DATA_TYPE ElementBuffer; int (*VectorReplace)(struct _Iterator *,void *data,int direction); }; |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 20 10:42AM +0100 On Thu, 20 Aug 2015 08:39:39 +0200 > DATA_TYPE ElementBuffer; > int (*VectorReplace)(struct _Iterator *,void *data,int > direction); }; This is not the point. The issue is with the macros you have written for constructing, manipulating and iterating over your vectors (you said that you wrote a generic clone of the STL, using C macros). The fact that C macros are unhygienic is both unarguable and notorious. A similar issue potentially arises with (the much more capable) lisp-2 macros, but there you can 'gensym' your way out of name injection (a rebinding to temporary symbols, although that still doesn't deal with input names from the surrounding scope shadowing names used in the macro, which lisp-2 deals with by putting function names and variable names in separate namespaces and by its module/packaging systems). Chris |
BGB <cr88192@hotmail.com>: Aug 20 07:18AM -0500 On 8/20/2015 4:42 AM, Chris Vine wrote: > input names from the surrounding scope shadowing names used in the > macro, which lisp-2 deals with by putting function names and variable > names in separate namespaces and by its module/packaging systems). hmm: could add a preprocessor extension, say, __gensym__, to try to address this. each time the preprocessor sees this, it generates a new unique symbol name. a person can then hack on their own extended preprocessor into the build process (in place of, or in addition to, the one supplied by the compiler), but admittedly I haven't usually found this worthwhile (have only done this rarely, it is less common actually than running custom tools which spit out C code/headers). some other extensions had included: block macros; ability for block-macros to emit subsequent directives; computed expressions; ability to use #define's as PP variables; addition of rudimentary scoping (local variables for macros); ... in effect, looping and recursion were also possible in this preprocessor, just a bit hairy looking. some of this was more inspired by macro systems from assemblers though. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 21 01:10AM +0200 On 19.08.2015 10:46, Juha Nieminen wrote: > of "better" C++'s during the last 20 years. A couple of them have been > moderately successful, the vast majority of them have been forgotten. > But they are still better than C++, dammit! Well there is D and Rust. But really the problem with compilation time is that today's compilers use 1960 batch build model. Consider the extreme measure that some people take: including all their source code files in a single file, which they compile. There are loads of valid C++ source code sets that won't compile that way, mainly due to name collisions (no compiler firewall here!), but still, since this avoids the same header being processed in and every translation unit, it reportedly cuts so much off build times that people find it worth doing. Consider now when the COMPILER does what that very fragile and dirty technique is meant to achieve, namely compiling each header file only once, like it does it with a precompiled header, for a specified set of translation units. Since this does away with the possibility of headers generating different code due to different preprocessor symbols or options for different translation units, this is something the programmer needs to OK either by opting in or (most naturally, since you won't be fiddling with per-unit symbols when you specify a bunch of files to compile) opting out, perhaps needing to explicitly suppress a warning. Consider further, that this compiler also skips trying to keep track of recovery information, but just gives up on first detected error. Well, there you have it, decent compilation times also for C++, I believe. All it requires is ... new compilers. He he. :) Cheers & hth., - Alf |
JiiPee <no@notvalid.com>: Aug 20 10:08PM +0100 I think its Sutter who writes: http://www.gotw.ca/publications/mill18.htm // Example 3: Obvious need for virtual destructor. class Base { }; class Derived : public Base {}; Base* b = new Derived; delete b; // Base::~Base() had better be virtual! So he wants: class Base { public: virtual ~Base() {} }; class Derived : public Base {}; Base* b = new Derived; delete b; And this works. But...it seem to me that I can get it even working without a virtual destructor ! Like this: class Base { public: ~Base() {} }; class Derived : public Base {}; Base* b = new Derived; delete (Derived*)b; Its not virtual but seems to work the same as the virtual version. So, just wondering why he said "it better be virtual" as I can show here that there is a way to get it working without virtuality. Or is there a mistake in my version? I tested it... it seems to delete all objects from all classes. Yeah, I understand delete (Derived*)b; is not a good way to do stuff.... but is that the only issue here? |
Ian Collins <ian-news@hotmail.com>: Aug 21 09:11AM +1200 JiiPee wrote: > from all classes. > Yeah, I understand delete (Derived*)b; is not a good way to do stuff.... > but is that the only issue here? Do you need another? -- Ian Collins |
JiiPee <no@notvalid.com>: Aug 20 10:15PM +0100 On 20/08/2015 22:11, Ian Collins wrote: >> Yeah, I understand delete (Derived*)b; is not a good way to do stuff.... >> but is that the only issue here? > Do you need another? ok i see... we can do that trick (and its deleting everything ok), but its very bad way to program thats why he did not even mention it. ok, but am just trying to undertand.... |
Bo Persson <bop@gmb.dk>: Aug 20 11:26PM +0200 On 2015-08-20 23:08, JiiPee wrote: > from all classes. > Yeah, I understand delete (Derived*)b; is not a good way to do stuff.... > but is that the only issue here? Yes, but generally when you need to work through an interface base class, you have more than one derived type. Then you don't know which one you should cast the pointer to. Bo Persson |
Paavo Helde <myfirstname@osa.pri.ee>: Aug 20 04:37PM -0500 > class Derived : public Base {}; > Base* b = new Derived; > delete (Derived*)b; The problem is that in real life you will not have a single Derived class, but maybe tens of different classes derived directly or indirectly from Base. Note that the deletion code typically has no idea which exact class it is dealing with (otherwise it could access it via the correct pointer, not Base*). Now you will have to somehow find out which exact class you have, maybe by repeated dynamic_casts, and huge if-else blocks for executing the correct delete. The real fun starts if dynamic load libraries are involved and some derived classes might not be accessible or even not yet written when you ship the library containing the Base deletion code. You cannot write a dynamic_cast to a class which does not exist yet! HTH Paavo |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 21 12:58AM +0200 On 20.08.2015 23:08, JiiPee wrote: > class Derived : public Base {}; > Base* b = new Derived; > delete b; // Base::~Base() had better be virtual! [snip] > Its not virtual but seems to work the same as the virtual version. > So, just wondering why he said "it better be virtual" as I can show here > that there is a way to get it working without virtuality. In most code the deleting action is automated. The cast you use is automated by std::shared_ptr (provided the complete type is known at the point of construction), but e.g. std::unique_ptr does not remember the original when the smart pointer is upcasted. > Or is there a > mistake in my version? I tested it... it seems to delete all objects > from all classes. It's technically OK, just as using goto is technically OK. It's just very fragile, very error-prone. > Yeah, I understand delete (Derived*)b; is not a good way to do stuff.... > but is that the only issue here? No. For example, the destructor might be the only method available to make Base polymorphic (at least one virtual function), which it needs to be for typeid and dynamic_cast (called RTTI, Run-Time Type Information). Cheers & hth., - Alf |
legalize+jeeves@mail.xmission.com (Richard): Aug 20 08:17PM [Please do not mail me a copy of your followup] Prroffessorr Fir Kenobi <profesor.fir@gmail.com> spake the secret code >I remember when i was a newbie compile errors >scary me: i was newer sure if i will be able to >mend them, what some mean, how to ment them This came up recently as an issue with the C++ language track on exercism.io: <https://github.com/exercism/exercism.io/issues/2407#issuecomment-105746324> My intention was to add the advice I mentioned on that comment as part of the documentation for the language track. This is pretty generic advice for C/C++ programming, that I had assumed was already present, but I keep running into people who have no idea of how to cope with the messages presented to them by the compiler. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
legalize+jeeves@mail.xmission.com (Richard): Aug 20 05:57PM [Please do not mail me a copy of your followup] Paul <pepstein5@gmail.com> spake the secret code >even though I have a basic understanding of deep copying. Can anyone >help me get an answer in c++ (or help me understand the answers that are >already provided.)? In C++, whether one is doing a "shall copy" or a "deep copy" is up to the implementor of the data structure in how they implement their copy constructor/assignment operation or in other member functions that they provide. For example, here is a structure that will do a "deep copy" when you use the compiler's supplied copy constructor/assignment: #include <iostream> #include <memory> #include <string> #include <string> struct p { int i; std::string s; }; void doing_deep_copy() { p p1{10, "hello, there!"}; p p2 = p1; // deep copy p2.s = "goodbye!"; std::cout << "p1.s = '" << p1.s << "'\n"; std::cout << "p2.s = '" << p2.s << "'\n"; } struct q { int i; std::shared_ptr<std::string> s; }; void doing_shallow_copy() { q q1{10, std::make_shared<std::string>("hello, there!")}; q q2 = q1; // shallow copy *q2.s = "goodbye!"; std::cout << "q1.s = '" << *q1.s << "'\n"; std::cout << "q2.s = '" << *q2.s << "'\n"; } int main() { doing_deep_copy(); doing_shallow_copy(); return 0; } The output of this program is: p1.s = 'hello, there!' p2.s = 'goodbye!' q1.s = 'goodbye!' q2.s = 'goodbye!' doing_deep_copy is a "deep copy" because after p2 is constructed, it isn't sharing the string p2.s with p1.s -- they are distinct strings. However, doing_shallow_copy is a "shallow copy" when you use the compiler's supplied copy constructor/assignment because it just copies the members over and the member is a shared pointer to a string. If you want struct q to have deep copy semantics, you can either override the compiler-supplied copy constructor/assignment or you can write a separate member function, most often called 'clone', that explicitly implements the deep copy operation. Some folks prefer the 'clone' approach because when they see simple = style assignment they can assume the same semantics as those supplied by the compiler: memberwise copying. Unlike a language like JavaScript, you don't have reflection capabilities in C++, so you can't "dig into the object" being copied in a generic way and recursively duplicate everything that implies referenced storage. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
JiiPee <no@notvalid.com>: Aug 20 01:19AM +0100 On 19/08/2015 23:16, Ben Bacarisse wrote: > and the {2, 7} is being used to initialize the single parameter. The > supplied opertaor simple does a member-wise assignment using = for each > of the data members. ok but to be able to use the operator= the compiler must create a temporary object (like "other") out of {2, 7} , isnt it? So for example: Coordinate other{2, 7}; c = other; (this is done somewhere temporary) I mean it will create a temporary Coordinate object using {2, 7}? |
Paavo Helde <myfirstname@osa.pri.ee>: Aug 20 02:51AM -0500 >> Coordinate &Coordiante::operator=(const Coordinate &other); >> and the {2, 7} is being used to initialize the single parameter. The >> supplied opertaor simple does a member-wise assignment using = for each >> of the data members. > ok but to be able to use the operator= the compiler must create a > temporary object (like "other") out of {2, 7} , isnt it? Yes, at least conceptually a temporary is created which is then copied to c by using the copy assignment operator. If anything like the copy assignment is identifiable in the compiled machine code is another question, aggregates are pretty transparent to the compiler and can be optimized heavily (even though copy assignment cannot be elided so easily as copy constructor, AFAIK). |
Bo Persson <bop@gmb.dk>: Aug 20 05:22PM +0200 On 2015-08-20 02:19, JiiPee wrote: > c = other; > (this is done somewhere temporary) > I mean it will create a temporary Coordinate object using {2, 7}? In theory, yes. In practice, if the compiler cannot optimize the assignment of two ints, I will get another compiler. Bo Persson |
red floyd <no.spam@its.invalid>: Aug 20 10:47AM -0700 On 8/19/2015 2:21 PM, Melzzzzz wrote: >>> members to 2 and 7 in that order. I am right? >> Isn't this just C-style struct initialization? > It looks like assignment. Oops. You and JiiPee are correct. I misread it as Coordinate c = { 2, 7 }; |
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