Downsides to the C++ STL? 1046
craybob queries: "I'm a developer for a small software group that will soon migrate from using Rouge Wave to using the C++ STL. I just left the week-long Software Developers 2002 conference, where I heard the great minds in software tell us all of the best ways to take full advantage of the STL. (I just wanted to give a quick thanks to Stephen Dewhurst and Scott Meyers) From this I came away with the feeling that this is the Holy Grail of C++. I'm sure these guys are right and that it is great, but the truth is that I'm a skeptic, so what are the downsides to the STL?"
Lots of overhead. (Score:0, Informative)
Implementation is not so 'standard' (Score:2, Informative)
Drawbacks (Score:2, Informative)
What virtual functions? (Score:1, Informative)
SGI STL reference (Score:1, Informative)
Jim [biacreations.com]
Re:Lots of overhead. (Score:4, Informative)
Not all compilers support it, god-awful comp errs (Score:5, Informative)
But the real bear is the compilation error messages, which can be pages long, and ultimately completely unreadable. This is due to template expansion, especially with STL classes (most of them) that take a large number of arguments, most of which have default values already.
Also, as with all templates in C++, there is code bloat. But it is a tradeoff between having more code or having better type checking. You have to decide what is right for you.
Not many (Score:5, Informative)
We developed a call-routing application for Solaris in C++ using ACE and the STL, and were able to meet a fairly hefty performance goal.
The biggest downsides on the STL that we encountered were a few compile issues in terms of integrating ACE into the build (not a big deal), and the larger one of somewhat poor documentation of the STL itself. We used the MSDN STL documentation, and while Microsoft's implementation may agree with that API spec, Solaris' certainly didn't. See the signature of the map::delete method for an interesting example.
Both the Solaris (actually SGI) and RogueWave implemementations DO NOT match the documented interface, even though Rogue Wave's documentation says it does! So make sure your intended usage is actually supported by the implementation of the STL that you're using.
Xentax
Re:Drawbacks (Score:1, Informative)
it seems pretty standardized to me-
vector-
std::vector v;
std::vector::iterator cur = v.begin();
while (cur != v.end()) cur = v.erase(cur);
map-
std::map m;
std::map::iterator cur = m.begin;
while (cur != m.end()) m.erase(cur++);
list looks like vector.
Well.. (Score:2, Informative)
string foo = NULL;
This always gets me, segfault.
I do a..
string foo = "";
where I define ""; as NULLSTR in some header.
I dont know if this is a real problem or no its just that i have the habbit of initializing stuff to NULL.
My bad style.
Re:Lots of overhead. (Score:2, Informative)
Re:What virtual functions? (Score:4, Informative)
ie. If you use a Vector to hold 15 different things, the compiler has to generate 15 different version of the Vector class to compile your project.
May not be that much of a problem if you've got the memory.
Re:Lots of overhead. (Score:5, Informative)
Re:Lots of overhead. (Score:3, Informative)
Re:Lots of overhead. (Score:2, Informative)
And it will cause havok if you're not aware of it.
Re:Lots of overhead. (Score:1, Informative)
There are no virtual functions in the STL. From an embedded developers stanpoint, the biggest drawback to the STL would probably be binary code size.
Cross-platform compatibility is a pain (Score:4, Informative)
Overall, I consider the STL well worth your while to learn and use.
Check out "Effective STL" by Myers (Score:5, Informative)
One thing to be wary of (as many have pointed out) is the different implementations of STL. GCC pre-3.x is pretty non-standard (although not necessarily bad). MSVC pre-6.x is absolutely horrendous (from what I gather, this is more of a legal issue than MS's fault).
Some of the wackiest things though IMHO are:
- Never use vector! It's a horrible specialization and is not even a container. Very, very bad.
- Allocators are for the most part evil. Be very wary of them.
BTW: There is a book Efficient C++ that says a lot of bad things about STL. This book absolutely sucks and is full of nothing but crap. While the examples aren't forged, they are examples of how not to use STL. Unfortunately, the book presents non-STL solutions that aren't even as fast as the proper STL solution. Long and the short of it is, make sure you (and your developers) are very familiar with STL and be aware of bad information about it.
Where the linkage? :-( (Score:2, Informative)
For the ill-informed, please see the following links concern the C++ Standard Template Library (STL):
*** Mumit's STL Newbie guide [wisc.edu]
*** Standard Template Library Online Reference Home Page [rpi.edu]
*** Another Informational Link [msoe.edu]
There, I feel much better.... and hopefully you do, as well!!!
Re:Not many (Score:5, Informative)
Pay attention to which operations are expensive for the various data structures (map vs. list vs. vector, etc.).
The fact that the operations' syntax for each of these is standardized is a double edged sword -- it makes for clean code and syntax, but it can mask poor-performing operations. Consider iterating over a map vs. over a list, for example.
So, consider carefully what operations you perform on various structures, and (of course), profile where/when appropriate. Looking at the actual implementation of the STL you choose can go a long way in revealing such troublespots, if that's an option (SGI's implementation is pretty easy to get ahold of).
Xentax
Partial List (Score:5, Informative)
Now, I don't want to get off on a rant here, but in my personal opinion, the worst thing about STL is their string support. It's great, because it's standardized, but that's about the only thing going for it, from a programmer's perspective. (Yes, it's highly optimized, but the API isn't very rich. I like rich APIs!) In other words, build your own string class, and give it a Has-A relationship to the STL string.
Also, I hate that Containers change paradigms on you, some places you can use integer indecies, sometimes you have to use iterators - and in my opinion, the line isn't very clearly drawn.
Also, the methods are written along the lines of, "if it's not optimal, you have to write the code yourself." I'm sorry - that sucks. Sometimes I need to remove an element from a Vector. Maybe I should be using another Container. Or maybe the API should allow it, but make it clear in the documentation that it's not efficient. I vote for the latter.
Get used to having your objects copied. STL Containers work by copying your objects. It's a different way of thinking for a lot of people. It has rammifications that are kind of hard to grasp, at first, if you're not used to it.
Don't use Containers of AutoPtr's! It won't work right! (Read "Effective STL" for an explanation.)
In my opinion, everyone should wrap every third-party library they use with an API that they can live with. STL is no exception. You might even expose every single member function, but you have the freedom to expand your API if you want. If you can't afford a stack push, then you probably shouldn't be using STL in the first place.
If you end up really liking STL, take a look at Boost. Some parts of Boost are really cool and well written.
Really, I think the best advice I can give is this : get to really know an API before you start to use it. Because, if you try to just use the parts of an API that you know and like, you're going to make horrible mistakes. Invest the time to get to know the library well enough to use it the right way. STL is no exception.
Of course, that's just my opinion, I could be wrong.
Bad inline optimisation. (Score:5, Informative)
Perversely enough, despite the unreadable and buggy implementation shipped with it, Visual C actually produced remarkably good object code from it. The vector class in particular was more efficient than many of the hand-rolled examples I've witnessed during my career.
...
Virtual functions, as others have noticed, are a complete red herring, as the STL doesn't use them, and any moderator with an ounce of sense would mod parent down for being uniformed karma whoring bollocks.
A couple STL issues (Score:2, Informative)
First of all, STL is not some proprietary set of binaries given to you to run to make your life easier in a black-box scenario. You're given the complete source for every bit of it. Yet for this reason, at some point in your development career, you'll feel like something in your code is written perfectly fine and that it "the STL" that has the bug. And then you step into the source while debugging. And then you curse everyone who ever had any part of coming up with this bunch of fscking nonsense. Then you completely give up, go grab a Dr Pepper, start over and skip over those STL calls instead of stepping into them and realize it was indeed your bug. I've heard various reasons in the past about why STL implementors release code that looks like an obfuscated-C contest winner (i've even heard that was the reason before), but I still don't buy any of it. There's no way they wrote it that way originally (so why did they change it), are they scared of whitespace (why?) and comments (did they ever read McConnell or Macguire?).
Second beef with STL is that although it *should* be standardized by all implementers, just like everything else that *says* it is, it is not. The STL implementation that comes with MSVC++ (a hacked up version of Dinkumware's I believe) has several subtle differences from all other implementations. And this is true for several compilers that come stock with STL. Don't immediately expect to port STL-based code from one compiler to another. Our company has to switch between two different implementations to compile between MSVC for a Win32-based build and MS Embedded C++ for a WinCE-based build. Sad but true. That's my beef.
STL Downsides? (Score:1, Informative)
My Reasons:
1. It uses templates. I know the name implies this but I can't stand the way C++ implements templates. Templates are created at compile-time which removes any advantages of generics in the first place.
2. While it may reduce developer time, it doesn't reduce code bloat. Templates are huge wasters of memory. This is because C++ creates a brand new class for *each* type of the template you use. So if memory consumption is an issue for you (like it is with me) then stay away.
3. Template are *not* portable. Each compilier has varying support for templates. Yes the *new* compiliers support *most* of the STL but if a developer wants to get to those older models on the shelves... stay away.
4. Using templates is verbose especially when you decide to throw inheritence in there. Template may look cool but they can get complicated really quickly (i.e. using the STL map template while inheriting from it)
5. Fragile Base Class. This is a C++ problem but it very much applies to the STL. If you build anything upon the STL and they add a virtual function... good-bye binary compatibility.
I fight extremely hard to not use the STL as there are other well-tested non-templated implementations of what the STL has.
These are my problems with the STL. I have other problem with C++ in general but the STL can't be faulted for those (except the STL is in C++).
The STL does have advantages but I don't think the advantages outweigh the disadvantages.
--sea
Re:Lots of overhead. (Score:3, Informative)
like those in Java, the
template-based containers of STL
do not use virtual function calls to achieve
genericity. Although this may result in an
increase in code size, there are cases
where different types can use the same
code at runtime. For example, a container
of int * and a container of char * might use
the same object code.
Some of the benefits of template-based containers
over inheritance-based containers are:
1) static type checking
2) can hold non-class type objects.
3) no virtual function call overhead.
To elaborate on item 2: If you want a
container of intgers in a Java container
(i think) you have to have a container of
"Int" rather than "int".
Re:Pitfall 1 : Lousy implementations/compilers (Score:1, Informative)
Vendor specific (Score:5, Informative)
Sure, the standard is >3 years old now, but a lot of compiler vendors are still working out bugs with either the STL, their compiler, or their linker still.
Under AIX, we've run into relatively few problems with the STL itself, but the linker is pretty bad. Between it and the compiler compiles take forever (which is why I've been surfing
This is, obviously, an AIX-specific problem. And it's pretty much an old story - every vendor has their own quirks with the compiler and/or linker.
Beyond that -- I've found a few things missing in the STL that would be really nice to have.
First, the only smart pointer is std::auto_ptr. It's pretty useless, since you can't use it in a collection, and you can't have more than one thing pointing at an object/memory block at once. This can be worked around though, since there are libraries that have better smart pointers. Check out Loki [aw.com] or Boost [boost.org] for two.
Second, there's no way to automagically ignore case on a std::string, or to upper/lower case it easily. Yes, I know, you can muck around with traits, but that's a PITA and renders your string uncopyable to other strings easily. Yes, I also know that you can use a transform() to do it. But this still isn't as nice as myString.lower().
Third, there's no date or datetime classes. You have to fall back on C time functions for them. I haven't looked for a good C++ library to handle date/time, but I'm sure there's one out there.
Fourth, there's no regular expression matching on strings. We use PCRE [pcre.org] with a C++ wrapper and it works fine for what we need though.
Both 2 and 3 are due largely to internationalization issues... in the case of 2 there's a lot of languages in which upper and lower case are non-sensical. And after having thought about the i18n issues regarding dates, I don't blame the standardization committee a bit for running away screaming from them (what date range? which calendar? how do you change between calendars? what about date weirdness with some calendars (like the missing days in the Gregorian calendar)? etc).
I used RogueWave prior to this job, so I tried to think of some of the things I was used to in RW and weren't in the STL. By and large I prefer the STL though. The container classes in particular are a lot more sane than RW's.
Re:Check out "Effective STL" by Myers (Score:2, Informative)
vector itself is very useful as a container, as well as for using C interfaces; since the storage is guaranteed to be contiguous (at least in the technical corrigendum), you can do things like:
vector<char> buffer(100);
readSomeData(&buffer[0], 100);
Advice from an STL battle-scarred veteran (Score:5, Informative)
First off: Why must you use STL? STL can be handy, but it can also be a terrible pitfall to the unwary. If your code is working and there is no compelling reason to re-write it, then don't/
Secondly: Be very, very careful about using pointers to dynamically allocated objects. If you are copying pointers around, you could very easily get into a dangling reference. A smart-pointer template (which SHOULD have been part of the STL) is a handy thing to have.
Third: Take the time to learn the Zen of STL. You must understand the rationale and mental model of the STL to get the most out of it. It doesn't take long (a week at worst).
Fourth: Get a good C++ and STL implementation. If you don't, you could wind up with compile errors that will drive you insane. <Sounds like the voice of experience, MagikSlinger!>
Fifth: Use STL sparingly. Don't go hogwild creating types made up of a dozen composited templates. When you get a run-time error or compile error, it becomes next to impossible to decipher what happened. Do not go more than two levels deep in an STL definition. map<string,MyClass> is OK, map<string,map<pair<T,X>,list< vector<int>>> is a very, very bad idea...
Sixth: Use the simplest datatype to achieve your goal. Don't resort to multimap, etc. with fancy indexing/hashing schemes unless you prove emperically that it will speed something up a lot. Not a little bit, but a lot.
Good luck, and have fun!
Re:Lots of overhead. (Score:3, Informative)
Instead, imagine what you are gaining. You get a good string class. No more worring about if the buffer is big enough or having to realloc/free memory when a string is appended or no longer required. It makes buffer overflows history.
Hash's and Trees: you can do this in C/Perl/Delphi/whatever, but STL's implementation is very easy to use and is optimized like crazy. The STL writers are very proud of their algorithms' performance. This may be one of the cases where it's impossible to write a faster C equivelent.
Portability. Anything written in ANSI C++ will compile anywhere as long as the compiler and libraries are up to date. A program I am working on will compile on C-Builder 4, Visual Studio, and GCC on Linux, without a single #ifdef or third party library.
The only downside I have experienced is that I needed to spend some money on books. STL has a learning curve and you might find yourself aging rapidly while fighting syntax errors that fill up the screen. But once you get the hang of it, STL is the easiest way.
Ozwald
STL and DLL don't mix well (Score:2, Informative)
The main problem is that when useing templates the generated code is included in each translation unit (.o or
There are ways to get a template to be an "exported" class in a DLL but this only works with vector. The other container classes are structured such that this will not work.
Sigh, there are times when I really miss my last job (Solaris all the way) but then I remember what ^%#$%& my boss was.
Object Lifetime Management (Score:3, Informative)
You have to pay very close attention to where you are storing pointers or iterators, and to when the things they reference are freed or moved. It is very easy to misuse the automatic constructors and destructors in C++, especially if you don't understand exactly what the STL is doing "under the hood" for each operation you perform with it.
"Smart pointers" help, but they have their own bugs and quirks, too. (I once did "bidirectional" smart pointers that were pretty idiotproof; all ends of each multiway link were aware of each other, but this had a lot of overhead.)
You can minimize this risk to some extent by designing the code to pass around auto-constructed copies of data instead of references or pointers, but this will tend to impact performance, sometimes so much so that Java would be faster.
Multi-threaded apps are even harder to get correct, since STL is not generally threadsafe.
Oh yeah, looking at the mangled names when you debug your code will drive you insane.
Nevertheless, IMHO, the STL is still the best thing about C++ and is just about the only reason I would use it instead of C. (Either one, though, is a last resort. I tend to develop and test all code in something like Python, and port portions to other languages only as needed.)
Re:Not all compilers support it, god-awful comp er (Score:5, Informative)
Re:STL downsides (Score:1, Informative)
Part of the reason APIs are completely illegible is, IMHO, due to all of the hack-arounds for nonstandard compilers and platforms that need to be in the headers. You normally only see this stuff inside the source code of libraries, not the headers, but in the case of STL the headers are the source code.
Re:STL downsides (Score:2, Informative)
Don't read headers to learn STL. Get a good book like Nicolai Josuttis's _The_C++_Standard_Library or visit SGI's STL Site [sgi.com]
Don't inherit from an STL container. They are not designed for inheritance (no virtual destructors). Instead, keep your current model and prefer aggregation.
Re:What virtual functions? (Score:5, Informative)
It may *appear* that way when working with STL but that is only because of the debugging information. Using templates will increase the amount of debug information in the executable (for each instance of the template) but it does not increase code size in any noticable manner.
Note: Ok, there are some methods that do get regenerated per-data type but the overhead is small and STL uses extensive inlining so this is almost meaningless.
Re:the STL is imporperly named (Score:5, Informative)
> Library is that it isn't very standard.
No, but the C++ Standard Library is in ISO14882 ("Programming Languages -- C++"), and it "superceeds" and embraces the old STL. Unfortunatly most people (and tutorials) still refer to either the STL definitions or use the term STL about the standard library.
> The support for templating, across a range of
> compilers, just isn't very consistant, which
> makes using the STL in a portable manner
> almost impossible.
Depends on what you use. Normally, it's not the containers themselves. that makes trouble, it's the functors and the algorithmic versions of operators (i.e. std::less and such), which most programmers don't use for the first few months.
BTW: GCC-3 has EXCELLENT template-YOUR_FEATURE_HERE support as well as standard library support.
> Aside from that, the STL is, to my mind, just
> another giant complex wart on top the
> mind-numbing complexity that is ANSI C++ itself.
C++ is complex because of the possibilities it offers... well, and it's heritage. To the untrained even the simplest tasks are compilcated... think how long it took to understand (not just learn) multiplication
> As with OOP itself, generic programming is a
Hmmm.... I consider generic programming perpendicular to OOP (this is a nice analogy when explaining why using GP from OOP sometimes requires "multi-dimensional" programming... if you understand?)
> Really Good Idea(TM) but its implementation in
> C++leave something to be desired for simplicity
> ans accessability.
Agreed, as well as consistence in compile-time versus runtime versions of syntax.
> Due to C++'s dominance in the marketplace, the
> STL will likely be with us for many years, but
> this is far from a desirable circumstance.
The Standard library (not STL) is the best thing that happened to C++ for years. I doubt it could have been done cleaner and more flexible in C++?
I would like to see optional garbage collection (with fitting restrictions to legal programs) introduced into C++. That's the no. 1 thing holding back (advanced/modern) OOP in C++.
--
Helge
Debugging templates is hell (Score:3, Informative)
Trying to locate a bug in a program that makes heavy use of templates, and specifically STL templates, can be infuriating. Besides having your data stored in a obscure data format that's difficult to view through normal debugging commands, you've also got type names that can be hundreds of characters long. The Sun Workshop debugger used to actually SEGV on some of those long names; apparently a buffer in the debugger overflowed. (This problem was fixed a few years ago).
Hiding the implementation of complex data structures makes for easier coding, but makes life hell when you're trying to vivisect a live process. At my work, we do use STL, but in small, measured doses.
Re:Templates increase code size? (Score:2, Informative)
Threads/Performance/Complexity (Score:2, Informative)
These aren't really downsides, just things to remember while you're using it:
* it's not inherently threadsafe - remember to lock if necessary!
* as somebody else as pointed out, choose your templates carefully for maximum performance (Meyers' book is good for this)
* if you're doing cross-platform stuff, build on the target platforms on a regular basis so you find differences early and have to change a minimum amount of code
Stroustrup's Third Edition has some STL stuff...Meyer's book is good if you know the basics and want to expand your knowledge (kinda along the lines of his C++ books).
Re:Probably slightly better than Fortran... (Score:2, Informative)
Exactly what type of crack are you smoking?
According to techies.com [techies.com], C++ is the most requested language skill, and the second skill overall (after Unix), with Java close behind it. Java is not a good laguage for building large enterprise level systems. JVMs are too slow.
More important...C++ jobs pay much better than Java jobs (and Unix pays better than windoze).
I do C++ on Solaris and Linux, so I'm happy.
As for C#. Who would ever use it? It only runs on windoze, which is rarely used in the enterprise, and it is slow, buggy, and relatively untested.
Re:Lots of overhead. (Score:5, Informative)
moded as interesting but plain wrong.
but there is a lot of overhead with using the STL.
No there is no overhead in terms of speed. The STL is designed to yield as efficient code as a VERY GOOD coder would get by hand coding. As the STL is coded with "how will the compiler work on this" in mind its often far more efficient than hand crafted code ever will be. (e.g. inlining over several function calls in depth)
Virtual functions and things of that like can make your code bigger and slower.
In the STL there are only few virtual functions. Most are non virtual.
Also a non virtual call costs you about 8 bytes asuming a 4 bytes instruction and 4 bytes adress, where as a virtual call you cost about 16 bytes, load register with adress, two times 8 bytes and jump idirect with register and offset, again 8 bytes.
However in practice the latter case is often only slightly bigger than the former(depending on the instruction set of the CPU).
If the code will be bigger than without STL is a question how your compierl and linker treat templates.
And it is a question how you would replace templates by hand.
Regards,
angel'o'sphere
Re:What virtual functions? (Score:2, Informative)
Error messages can be made readable (Score:3, Informative)
Check out BD Software's free message decryptor: "Freeware with Source Code, supporting: Comeau C++, g++, VC++6, VC++7 (Visual Studio.NET) and Metrowerks CodeWarrior"
www.bdsoft.com/tools/stlfilt.html [bdsoft.com]
The messages are still a bit odd until you browse the class which triggered the error, but it shortens them down to a readable, meaningful length.
I'm a fairly recent STL convert and I find this tool utterly invaluable. I love STL because it provides a true standard for many of the structures and algorithms that are core to any project. I'll never have to deal with another crackpot programmer's homegrown, poorly commented dynamically-sizing array class again.
STL is only as good as you... (Score:3, Informative)
Learn who owns what. Learn how to handle pointers and references in an intelligent manner. Garbage collection is neat but is no substitute for good programming.
Read Those Fine Manuals. See SGI STL Tech Pages [sgi.com] for a good online STL reference. Pay particular attention to stated efficiencies. You can use an iterator to loop through any container, but not all containers are created equal.
Get a good compiler. Template and inline code bloat can be minimized by selecting a decent compiler and flags.
You can use things like for_each, but remember you can also use a standard for with iterators.
Re:Drawbacks (Score:2, Informative)
Further, there are apparently various exciting optimisations available to sufficiently intelligent compilers (e.g. !GCC) which can't be made when using a for() loop
old compilers. need for ref. counting, threading (Score:2, Informative)
1) old compilers. Sun CC 4.2 used to have a templates DB that didn't work well with incremental builds.
2) costly to copy objects that need to be stored in containers. you can store pointers instead and manage the object lifetimes yourself or a better thing to do is make those objects ref. counted with "copy on write" semantics
3) you will need your own thread synchronization if your containers/the objects in your containers are not read-only. see http://www.sgi.com/tech/stl/thread_safety.html
prasad
Re:Drawbacks (portable remove) (Score:3, Informative)
1) any iterator will remain valid if it is untouched.
2) any iterator not directly involved in an erase will remain valid across the erase.
3) any iterator directly involved in an erase will be erased and so invalid (therefore MyList.erase(cur++) is always illegal)
4) the only value that an iterator can legally have that isn't a valid member of MyContainer is MyContainer.end()
The only/best not remove_if (e.g. NOT using the built in algorithms etc.) version of the remove on condition requires two iterators. That is:
class something {...};
// This typedef is your friend
typdef std::set SomethingSet;
void remove_matching(SomethingSet & MyContainer, something & value)
{
SomethingSet::iterator cursor = MyContainer.begin();
SomerhingSet::iterator del_mark = MyContainer.end();
while (cursor != MyContainer.end()) {
if (*cursor == value) del_mark = cursor;
++cursor;
if (del_mark != MyContainer.end()) {
MyContainer.erase(del_mark);
del_mark = MyContainer.end()
}
}
The above "seems" ugly but will execute in linear time and very efficently. It's uglyness though, is why the STL contains standard algorithms. Once you know how to write a function object correctly (and providing there is no prohibitive cost to copying your quanta) then the below which looks uglier:
MyContainer.erase(remove_if(MyContainer.begin()
actually makes a heck of a lot of sense and optimizes down to something incredibly dense (8-) and effective.
One example (Score:3, Informative)
Compare just about any use of C's qsort with C++'s std::sort. The fact that the latter is implemented as a template means that any specialised comparison functions can be inlined and optimised right in the sort algorithm, unlike the mandatory level of indirected required by qsort's call-via-pointer approach. I don't have any timings handy to give entirely objective evidence, but I've certainly done rough-and-ready timings on several compilers, and all the recent ones had std::sort way ahead. A quick glance at the generated assembler confirms the theory above.
/. is a skewed sample (Score:5, Informative)
1) Get STLPort. [stlport.org] Use STLPort. STLPort addresses many, many, STL issues. They add extra nice classes like hash tables. STLPort is thread safe. STLPort has nice extra debugging features. STLPort has readable code. STLPort is PORTABLE (thus the name!). OpenOffice uses STLPort, in case you're still dubious.
2) Get a couple of STL books. There aren't any really good ones (IMHO), but it's handy to have a printed reference with some examples.
3) You wanted downsides, so here's one. You will have to learn STL. Not the library, but the techniques--the API is easy. You have to write your own C++ classes well to take really good advantage of STL. The way you leverage the STL for absurd productivity is through generic programming and STL's pluggable component architecture. Still, though, even you all you ever use is map, string, and streams (or some other subset), you'll probably become a convert.
4) STL will keep getting better to use. Other people have mentioned it, but look at Boost [boost.org] for some ideas about where STL is headed. Also, the compiler people are aware of and are working on the error message and debugging problems. Both VC++.Net and gcc 3.x are making progress here.
Performance & size tests, c, gcc & stlport (Score:4, Informative)
g++ is just what i needed for a fun and relaxing weekend of random hacks.
I was goofing off with a trivial c++ hack that provides a base64 iostream iterator. I've heard for a while about the relative bloat and performance hits on the IO side of c++ and wanted a peek for myself.. what I found was halting.. 50x performance increase from a simple C hack, all of it IO. Well, not to be defeated so easily, I plugged in STLPort with its spiffy optimized IO and gave a whirl. What resulted was not 1:1 with C but reasonably in the realm of hand tunable for IO buffering thereafter... this was after all, code that reads and writes a byte at a time vs. a c program that uses buffers...
the relative results of this effort were as follows: test were conducted streaming 1 meg of data to >dev/null on an athlon 1800xp under debian sid dist.
buildtimesize
gnu uuencode, from gnu sharutils, compiled c code. ~0.12 seconds/meg binary 9k
base64.h: using g++-3.0.4 -O6 -static
~3.5 seconds
binary size 895k
base64.h: using g++-3.0.4 -O6 (shared)
~4.8 seconds
binary size 6k
base64.h: using g++ and stlport -O6
~0.45 seconds
binary size 10k&7k, static and shared.
Solving C++ template code bloat with Squeeze++ (Score:4, Informative)
http://www.elis.rug.ac.be/~brdsutte/squeeze++
you can see that we are able to reduce the code size of real-life programs (e.g. LyX) using a lot of templates by 60%. This is done by compacting the programs after they are linked, applying aggressive whole-program optimization and code abstraction techniques. There is a specific manuscript on the page as well, on how to reduce the code bloat coming from the use of templates. An important technique is whole-procedure reuse: if several identical procedures are found at the assembly level, no matter what the source code was, the duplicates are eliminated from the program. If similar procedures are found (e.g. in sorting routines where only the called compare method is different), they are merged using a new parameter.
Cheers,
Bjorn De Sutter
Ghent University
Re:One Downside (Score:3, Informative)
By [at least one logical] definition, a high level language is one that uses constructs which do not map directly to those supported by the hardware on which it is running.
C is considered [by many] to be a low level language because it only uses constructs which are available on the majority of modern hardware platforms. However, C relies heavily on the construct of accessing the heap. On a purely stack-based machine which has no heap (some embedded systems, for example), you would have to emulate a heap in terms of stacks. C would therefore be a high level language on that platform, while Forth, a language based around the use of stacks, would be low level.
Sometimes the situation is reversed ... people design the hardware to match the constructs used by a particular language. This was so in the case of the old Lisp Machines [lisp.org], or Sun's picoJava [sun.com] chips.
Few languages these days are strictly interpreted, as in parsing each line of source code just before executing it. Many are compiled into an intermediate form, sometimes called bytecode. This bytecode, in turn, may or may not be a high level language for a particular machine, depending on how closely its constructs match the underlying hardware ones.
Even in a purely compiled language without an explicit "bytecode" stage, the further the language's constructs are from those of the hardware, the more instructions it will take to process each statement.
In short, there are plenty of high level languages which can compile to native code. But this does not mean they will run as fast as carefully crafted assembly!
Re:er... "horseshit" (Score:5, Informative)
(Summary first) Everything was put in or left out for a reason and that reason (per thing) is documented if you take the time to look. Since it will be impossible in this forum to address the entirity of the issue I will stick to the elementes you named. Specifically the four casting operators (actually there are five) and the constructor invocation as a sixth "cast", which you incorrectly classify as an ambiguity.
First you must consider the base language construct of the unconditional cast.
struct A {
struct B {
A * ptr_to_A = new A();
B * ptr_to_B = (B*)A;
In the base language "C" the above is legal, will compile just fine, and is totaly wrong. This program would likely fail catostrophically. This is the generic version of an actual problem we (at my company) just found in a comercial "C" product.
The thing is, the C-style cast has some core functionality that is occasionally indespensible. (The discussion of when and how this is indespensible is ommited as whole chapters of books cover this topic.)
More importantly the C-style cast is a "gloves off" operation. When the programmer performs this basic operations it is with the understanding that if it is wrong it is to be done anyway. Complaining about its existence is like compalining to a surgon that a scalpel is dangerous because it could cut something...
The four "lesser casts" (my term) are "gloves on" operations. The programmer, in using the spesific casts is describing a desireable transformation on the data and the compiler and/or runtime checks the viability of the operations to ensure that they are completely legal.
Consider first a language that will do silent uncasted transformations (C++ will do these in some spesific and well defined cases which I will get back to later)
if you have "let SomeString = SomeInt" and the language allows this as a transformation (see awk, possibly Perl etc) it will "just do it." but the "it" is only vaguely defined. It is worse if "let SomeInt = SomeString" where there is no obvious guarantee that "SomeString" contains a useful representation of a candidate integer.
Ok, so we can agree that the transformation of a datum from one intrinsic type to another is problematic. Composite types make this a composite problem. Now back to the casts...
All the casts represent expressions in the true sense. The opperations are transformative just as unary minus or square_root are transformative. The original object and representation are unchanged but the address or constantness or the "invariant" are manipulated. And like any expression there may be temporary objects involved. Listed from most-checked and safe to least, the compile-time resolvable casts are:
const_cast(existing_object) => the existing_object may only vary from the new_type by the addition or removal [usually removal] of the constantness.
static_cast(existing_object) => the existing_object must have a defined pathway to becomming an object of the new_type. Usually this is done by one of two methods. The most common transforms the address of the object into the address of the part of the object that is of the new_type. The second method creates a temporary object of the new_type using the relevant information from the existing_object.
reintrepret_cast(existing_object) => almost always a transition from pointer-to-existing_object to pointer-to-void or vice versa. The reintrepret cast is used to release the expression from the constraints of the invariant of an object. Usually in order to pass the object through some external interface (e.g. passing it to the OS etc).
Notice that these three casts each have a spesific guarantee of function for form. To take the safe root through a transformation you sometimes need to use two casts together. Most commonly you will un-constant-ize something and then static cast it if you are doing these kinds of casts in a way that requires composition. I'll skip the example for now. The important thing is that you can, once you know how to use your tools, know in turn exactly the transformations that your existing_object or reference or pointer there-to will undergo.
There is no uncertanty in the three compile_time casts.
The fourth "limited cast" is:
dynamic_cast(existing_object)
This one is trickeir as it has an implicit "if" statement within it at compile time and another "if" statement in it at runtime.
IF new_type is an obvious part of existing_object the dynamic_cast is identical to the static_cast and you should have used that. The compiler will use that static_cast in place of the dynamic_cast because it knwos you are being dumb. 8-)
IF however new_type is not clearly in existing_object(s) ancestory (this is the "else" of the above case) then the compiler generates code to deal wiht the cast at runtime instead of compile time.
At runtime, IF the new_type object that is "part of" the "whole_object" that existing_object might also just be "part of" then the whole thing goes off without a hitch and the expression works. If the request is impossible then either a zero is returned (remember "transformative expression" 8-) or an exception is thrown. (the causes and cases are again ommited, go read the book).
SO BACK TO THE CORE QUESTION: Why is the above a "good language design"?
Answer: Because an unconditional cast "(new_type)existing_object" could do any combination of the above, but the above only happen explicitly if you use them explicitly.
Hua?
Well, in the first three you *ALWAYS* get a thing of the type new_type so there is no testing to be done. The compiler will not let the activity go wrong. You don't have to test anything in the code, the compiler makes you a warrent.
The fourth, more dangerous and occasionally indispenseable and often quite desireable, cast needs support code.
In a lesser language, I would either have to inclde the support code for every cast *OR* "work without a net".
In C++ I can code to the spesific requirements.
A language that lets the programmer code to the spesific requrements without having to put in lots of dead code just to be safe is "well designed".
Some languages "seem" to be better designed because they put in the general case safety-net for you, but languages that generate "general case" code "for you" arn't well designed. The fact that some environments/compilers then try to take the safety-net code back out durring optimization etc are arguable. The work for the slapdash at the cost of allowing the programmer to express himself explicitly. When the programmer isn't really in charge of the code that is generally a "bad design" where effeciency is a factor. But for the people who need this net or don't know how to work without it, this kind of code is "good".
But I digress.
So what about the so-called "ambiguities" over constructor invocation as a casting operation.
First, construction of a new object isn't, strictly speaking, a cast operation. It is "so like a cast operation" that in practical terms the two operations are considered synonymous.
In actual fact, construction is construction. Casting is casting. Construction is the building of a new object using the information from zero or more old objects. Casting is the reconsideration of an existing object "in place".
The confustion arrises because a sloppy thinker often cannot separate the classification of an object from the application of the value of that object.
In the expression "A = B;" A is being transformed. In the special case "A_Type A = B;" the transformation of "starts from nothing". The construction of paramter objects durring fuction call is the latter case.
The language allows us to define the transformation of A with respect to many types. If we create a transformation of A based on a B then this is obvious. Sometimes things are not that simple.
The programmer is allowd to make a chain of transformations implicit in ther code space because the language design allows for one "free and silent" step to be added. That is, if B can become X and X may be used to transform A, then B will transform A by way of a temporary X.
The user is spesifically warned that this feature must be used wiht due dilligence. In particular if there is more than one possible X intermediary the compiler will pick the "best" one. The rules for finding this "best X" are explicit (there are exactly four such rules).
Additionally, under no circumstances will B => Y => X => A considered.
If there is more than one "X" and they score the same on the "betsness" scale the compiler will demand the programmer clean up his mess. Many less-well designed languages will silently pick one wihtout a peep, which one may change from compile to compile. This would be an inferior design because any "X" may have larger-scale implications.
The educated user is capible of making the transformations work seamlessly.
The educated user is capible of explicitly disallowing some transformations by use of the keyword "explicit".
The uneducated user is going to make a mess if he fails to understand and use the feature.
There is absolutely no ambiguity.
The fact of the matter is that a poor design will net poor code. Some languages will glop out corrective code for the programmer. Where the programmer is willing to pay this expense, and the application can afford this expense the issue is a giant "don't care".
Where the cost should not be paid, the "looser" languages don't give the programmer the ability to remove these expenses. To that end these "looser" languages have an intrinsic limit to their functionality.
Every language does actually.
However proficency in the tighter (and more demanding) language translates directly into the skillset needed for a looser language. The converse isn't true.
There is an old aphorisim: "If all you have is a hammer every problem looks like a nail." The secret obverse is "the more tools you have the easier it is to find the right tool for the job."
Think nailgun. Heavy, dangerous, effective as hell. If you are competent to use one, it will serve you well. If you never learned to use anthing more invasive than wood-glue then don't pelase-god try to use the nailgun.
The fact that you are uncomfortable using a nailgun doesn't intrinisicly mean the nailgun is poorly designed.
It's a poor craftsman that blames the tools.
RWTools vs. STL (Score:2, Informative)
The STL is vastly superior to the old Tools.h++
template collection classes. The STL has a
set of powerful algorithms, useful iterators,
and is type-safe and const-correct. Old Tools.h++
doesn't and isn't.
Newer Tools.h++ classes are compatible with
the STL, but I can see little reason to use
them beyond backward compatibility. RW does
provide hash tables, but you can get an STL-compatible hash table for free.
Re:er... "horseshit" (http-ed again) (Score:2, Informative)
{open angle} new_type {close angle}
between the cast and the parenthisized expression. Those things look like browser tags and get eaten...
so
const_cast <new_type>(existing_object)
static_cast <new_type>(existing_object)
reinterpret_cast <new_type>(existing_object)
dynamic_cast <new_type>(existing_object)
Sorry about that... 8-)
Re:Check out "Effective STL" by Myers (Score:3, Informative)
The C++ commitee thought that a proxy class could be used to emulate a primative data type. They didn't know how to do this, but they believed someone would figure it out. To encourage this, they forced vector<bool> to be specialized to return a proxy object and to store the bits internally as a bitset (which is more efficent since it uses 1 bit per element instead of 1 byte).
Problem is that proxy classes cannot emulate primative types and the experiment failed. As it is, the vector bool class is still part of the standard and currently violates the rules for the behavior of a container class. (Namely that &v[0] is invalid).
Failed experiment that somehow made its way into the standard.
a couple of pointers (Score:1, Informative)
http://lnxatd01.cern.ch/Atlas/DaqSoft/rw2stl
Enjoy!
Re:One Downside (Score:5, Informative)
It always amazes me at how people who acknowledge they don't know C++/STL (as you said, "I have never used it") know so much more about it than those of us who've been using it since the mid-90s, and who still discover more neat things about it on a continuing basis.
That said: C++ makes it easier to produce bloatware.
Two answers:
Name me one advance in computer science that doesn't also carry with it the possibility of using the advance stupidly. Yes, if you deliberately do stupid things with C++, you'll get code bloat. But if you make the language impossible to do stupid things in, you'll also make it impossible to do clever hacks in the language. If I want a language like that, I'll use Java, thanks.
Most of the time when people blame code bloat they're really blaming templates. Tell you what: look at the vector template and find out just how brilliantly sweet it is. Now hand-code it in C, such that it gives you the exact same level of sweetness, speed, and safety. Dollars to donuts says your C code is more bloated.
Spoken like someone who never made it past the introduction of Stroustrup's The C++ Programming Language. Repeat after me: C++ is not an object-oriented language. C++ never was an object-oriented language. C++ never will be an object-oriented language. C++ supports OOP, but that doesn't make it an OOPL. C++ is, more precisely, a multiparadigm language. You want generic programming? C++ has the tools. You want functional programming? C++ has the tools (awkwardly, but they're there). You want OOP? C++ has the tools. You want procedural/imperative programming? C++ has the tools.
Whatever you want, C++ has the tools.
Hey, at least its not Java which forces OOP on you instead of giving you an option
My harsh words about Java (above) were, as I hope the Java community will understand, meant as a gentle jab from one diehard C++ hacker--not as a misinformed flame like you're spewing here. Of course Java gives you an option. If you don't want OOP, don't use Java. Use Ada95 or Python instead, both of which support non-OOP paradigms and which can compile down to Java bytecodes. Java, like Smalltalk, is a purely OO language. Saying that Java sucks because it forces you to write OO code is... well, really foolish. Java doesn't force you to write OO code; you force yourself to write OO code by committing to Java as a platform. Java is a tool in the toolbox. A hammer doesn't force you to treat everything like a nail; but if you choose to pick up the hammer, the only person to blame is you if you needed to pick up the screwdriver instead.
I can't really talk about the STL
... Why not? Your utter ignorance didn't stop you from talking about C++ or Java.
... And are these coders competent craftsmen, skilled in the ways of the STL? Or are they incompetent two-bit fly-by-nighters?
it is a sad sad mechanism for making your code slower and harder to debug and your executables larger
Bullshit. Look at the following code:
int compare(const void* first, const void *second)
{
int *x = (int*) first;
int *y = (int*) second;
return x < y;
}
int main(void)
{
int array[1048576];
qsort(array, 1048576, sizeof(int), compare);
return 0;
}
int main(void)
{
int array[1048576];
sort(array, array+ 1048576);
return 0;
}
[rjhansen@numbers cpp]$ time
real 0m1.034s
user 0m0.960s
sys 0m0.070s
[rjhansen@numbers cpp]$ time
real 0m0.719s
user 0m0.720s
sys 0m0.010s
... Want to repeat that bit again about how STL causes your code to run slower and be harder to debug?
Generic Algorithms are the Downside (Score:2, Informative)
Generic algorithms in STL aren't as useful (yet) as their equivalents in Java, Smalltalk, & Lisp (& probably a bunch of other languages).
Overall, the STL is a great timesaver when programming in C++.
gene