To STL or not to STL, that is the question... - Page 10
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 10 of 19 FirstFirst ... 78910111213 ... LastLast
Results 136 to 150 of 280

Thread: To STL or not to STL, that is the question...

  1. #136
    Join Date
    Nov 2003
    Location
    Pasadena, CA
    Posts
    48

    Re: the souldog remove hole

    Originally posted by mclark
    Now, besides the remove family of functions - are there any others like this lurking out there to frustrate me?
    After a quick scan of the standard, the only other occurence of this hole is with 'unique'. Since the standard does not say that unique has to be implemented in terms of the remove family of functions, I'll assume that it has to be specialized seperatly.
    The views expressed are those of the author and do not reflect any position taken by the Goverment of the United States of America, National Aeronautics and Space Administration (NASA), Jet Propulsion Laboratory (JPL), or California Institute of Technology (CalTech)

  2. #137
    Join Date
    Apr 1999
    Posts
    27,427
    Originally posted by souldog
    Well this is not going to violate your size contraints, but
    you can call Remove(...) with just the iterators.
    Yes. I misssed that one also. Thanks, souldog.

    Regards,

    Paul McKenzie

  3. #138
    Join Date
    Aug 2002
    Location
    Madrid
    Posts
    4,588
    First of all, my moderator comment: Thank you all for having toned things down

    Now for mclark's problem with the bounded vector. IMHO it doesn't make sense to force things when there is no need to do so. In this case, what I mean is whether or not you really need iterator access, standard algorithms etc. for this vector. If you do, then you have to go all the way and implement things correctly, which may prove to be a pain.

    In this context, you may find it interesting that algorithm's remove(FI, FI, T) doesn't actually remove elements from the container, it just shuffles them around. See the note on SGI's STL pages:
    The meaning of "removal" is somewhat subtle. Remove does not destroy any iterators, and does not change the distance between first and last. (There's no way that it could do anything of the sort.) So, for example, if V is a vector, remove(V.begin(), V.end(), 0) does not change V.size(): V will contain just as many elements as it did before. Remove returns an iterator that points to the end of the resulting range after elements have been removed from it; it follows that the elements after that iterator are of no interest, and may be discarded. If you are removing elements from a Sequence, you may simply erase them. That is, a reasonable way of removing elements from a Sequence is S.erase(remove(S.begin(), S.end(), x), S.end()).
    If you do not need to have all this functionality, then by all means, don't bother. In my code I use STL a lot, but I also implement my own non-STL compatible containers for special purposes. For example a cache that holds only N elements and silently deletes one (or more) if a new element is added and the cache is full. Or a customized stack for Undo operations.

    Both of these still use some STL internally (the Undo stack is based on vector and deque while the cache uses a std::map for some purposes), but the external interface doesn't have to be STL-compatible and doesn't even fit the model of STL containers.

    For some other of my own containers I implemented a fully-fledged STL-compatible interface along with my own iterators. These containers fit the STL model more closely and are more generic in their uses.

    In essence, my view is that STL is a great tool to have and use. When you are designing algorithms and methods (my favourite part of my job), then STL comes in really handy. If I need to have a balanced tree that is already optimized, I can just use std::map or std::set, whichever fits best. If I need fast lookup, I can use the hash_map extension, if I need a pretty good string class for general puposes, I'll go anytime with std::string. Vector, list and deque are not among my favourites, but I still use them from time to time. vector has the pitfall of not freeing unused memory, so you are either very careful when to check if size and capacity are too much apart, or you go for a different solution. deque and list have a limited usage scenario in the types of algorithms I write.

    I really don't see a point of using MFC containers, unless you are forced to. MFC as a GUI tool is very good. The containers are in my opinon unflexible and slow in comparison to STL ones.

    As far as the discussion about OO and GP goes, I don't see a problem of using both, so when I'm writing the overall design it could very well be heavily OO oriented while some of the functionality comes from GP-based parts. Anyways, the dividing line between OO and GP is not clear-cut, as some others have pointed out.

    Yves
    Get this small utility to do basic syntax highlighting in vBulletin forums (like Codeguru) easily.
    Supports C++ and VB out of the box, but can be configured for other languages.

  4. #139
    Join Date
    Aug 2000
    Location
    West Virginia
    Posts
    7,565
    (for the language-standard monkies)
    Can I fully specialize a function template that's in the std namespace. I know I can't overload it because injecting names into namespace std is illegal (and almost all compilers enforce this).
    I think that you can (caveat: I'm not a language standard
    expert). I base this on the following section in the ISO standard:

    17.4.3.1 Reserved names

    1 It is undefined for a C++ program to add declarations or definitions to namespace std
    or namespaces within namespace std unless otherwise specified. A program may add
    template specializations for any standard library template to namespace std. Such a
    specialization (complete or partial) of a standard library template results in undefined
    behavior unless the declaration depends on a user-defined name of external linkage and
    unless the specialization meets the standard library requirements for the original
    template.
    Although the the phrase, "unless the specialization meets the standard library requirements for the original
    template." is a little worrisome - depends on how your
    remove would work.


    [lib.alg.remove] 25.2.7 Remove

    template<class ForwardIterator, class T>
    ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value);

    template<class ForwardIterator, class Predicate>
    ForwardIterator remove_if(ForwardIterator first, ForwardIterator last, Predicate pred);

    1 Requires: Type T is EqualityComparable (20.1.1).

    2 Effects: Eliminates all the elements referred to by iterator i in the
    range [ first, last) for which the following corresponding conditions
    hold: *i == value, pred(*i) != false.

    3 Returns: The end of the resulting range.

    4 Notes: Stable: the relative order of the elements that are not removed
    is the same as their relative order in the original range.

    5 Complexity: Exactly last - firstapplications of the corresponding predicate.

  5. #140
    Join Date
    Sep 2002
    Posts
    1,747
    First a little background. Although I have been programming for about 23 years, that is considering 1st grade and logo and all the other systems at schools and home since then. Professionally, I've only coded for around 2 years (give or take if you want to include simulations work for CERN which was undergrad stuff and maybe not taken that seriously). I've coded in lots of different languages along the way. And I'm the type who has opinions on just about everything, from politics to religion to music (yeah, one of "those types"). So I definitely have an opinion on the design of the standard containers.

    But I'm also the type that does not enjoy being wrong, and I am very hard on myself when I fail to see something and make errors (yeah, one of "those types"). But I find it is common for people with such a strong dislike of being wrong to build up avoidance reactions so they do not have to admit to themselves an error has been made, and consequently beat themselves up about it. Fortunately, I am not one of those types and indeed am quite the masochist.

    But I hate wading through an emotional rollercoaster to get to the facts, needing to pick apart colored phrases for the intent. I want the facts spelled out to me, up front. Its not that I don't enjoy watching a good fight, its just I want the reason spelled out clearly beforehand.

    This thread is about the standard library's containers. There have been several points mentioned against them, two prominent categories being bugs and design. Everyone who has mentioned bugs has completely avoided spelling them out when confronted. I will take this to mean: a professional developer need not worry about the stability of their standard library in professional considerations of use. Various vendors have encountered bugs off and on, and worked hard to correct them quickly, but nowadays any bugs very likely lie in the much lesser used parts of the library that have not been as stress tested in strange usage patterns (such as deep in the locales and formatting facets of the stream libraries, or something similar). I stress this because I don't think it is fair to even casually make a suggestion otherwise without evidence, as most of us here are professional programmers who need accurate information to make decisions that could potentially have a lot of money riding on them.

    About the design: let my opinion begin!:

    A large group of highly influential programmers call c++ a multi-paradigm language, including Stroustrup and Coplien among many others, and this group fairly unanimously agree that a multiparadigm design is superior to single paradigm. And they are highly influential because it is fairly easy to tell from their work that they are very intelligent, that their code is tight, and that they can accomplish very advanced tasks. But instead of focusing on them, let us focus on the arguments given by them and others that have sprungin' forth from my mind...

    First a note on OO, polymorphism, and templates. It is true that templates allow for static polymorphism, and that generics are a great way to attend to manipulations of an object without reference to the type's identity. This was the first genius of generics, and various containers and algorithms were its first major use in Ada. However, its important to realise that one of the biggest powers of templates lies not in generic use of a type but in the ability to manipulate types. Metaprogramming techniques are so powerful because one can write metafunctions which take types and a limited set of nontypes as arguments and output a completely different type or nontype. Simple uses are things like transforming a type to a pointer or reference to that type, but the capabilities extend way beyond to major code movement and transformations, detailed function type parsing, etc. So while some simple uses of templates can be seen merely as static polymorphism of a manipulated object's type, it includes capabailities that are not really OOP and much more instances of paradigms like Aspect Oriented Programming. And, being young and easily excitable by trends, I have investigated the possibilities presented by other paradigms.

    Which leads me quite naturally into the alleged design flaws of functions not associated to objects. Object Oriented programming has had its share of debates on this topic inside its paradigm, but I won't go over Sutter's or Meyer's positions on how such a design promotes reuse, protects encapsulation, etc.(which are easy enough to google for). Although I agree with them almost entirely, my approach to such considerations is much more pragmatic and less tied to a particular paradigm. I am a professional coder who gets paid to build working applications over and over again. Things that are important to my design are reuse, easy extensibility, clarity of purpose, basically all of the things that help me get my job done with the least frustration. The things that accomplish that are what I consider good design.

    Object oriented programming was probably the first paradigm I found which was able to break apart the code freeze that tends to creep into procedural code, and reuse was natural. However I have found that there are obstacles to building a framework in object oriented programming that maintains the ideals of the paradigm, particularly if one of the ideals is that all manipulators reside within classes. To extensibly interface with future objects, there is often a strong desire to continuously refactor common functionalities until the hierarchy becomes monolithic. This tends to allow extensibility at the cost of freezing in a framework's core. Although this is a manageable alternative if you happen to get all the functionality you desire into the core before it gets frozen by extensions, often what I see happening is that eventually interface bloat and violations of Liskov substitutability start arising as one reverts to "add this functionality anywhere that will work" type design. I see MFC as one of the sufferers of this affliction.

    But I also have experimented heavily with functional languages, as one of the many things I call myself is a mathematician, and the lambda calculus is a very natural language for me to express programs in. I particularly enjoy playing with the object-oriented functional languages, because they express things a bit closer to the ideal of category theory than basic use of c++. I have found, however, in my limited time as a professional software architect, that advanced c++ usage patterns and idioms can accomodate my preferred method of design. I have found that, much like the Sutter / Meyers arguments, coding independent, reusable morphisms (algorithms) is just as beneficial to the criteria I gave for good design as coding powerful, reusable objects. I make my object's interfaces "state complete" (ie. all valid states may be reached through the mutator interface) and attempt to make the interface names generic enough so that similar objects that may or may not be related through an IS-A relationship can still be manipulated robustly by the reusable morphisms. I use traits to extend my classes orthogonally to any hierarchies they may participate in, and I don't view traits as objects but as additional type-associated morphism decorators. And finally, I try to use as much metaprogramming as possible to automate the construction of my architecture. I find that such a design has allowed me to achieve all of my design ideals, and I have recently needed to focus only on refining the automation.

    All of this leads to my evaluation of the standard library's container design. Which I find to be very similar to the style of programming I have found works so well. It is extended orthogonally, preventing monolithic hierarchy lock-down. The algorithms manipulate generic containers polymorphically, through iterator interfaces. This is Aspect-Oriented Programming iin one of its first, less developed, incarnations. I like the basic separations and the powers developed. If I were to complain about the design of the standard library's containers, I would only push to having the aspects better separated, so that the data structures were more composable (giving some access to the internal construction process so that more complex types like trees and graphs were possible along the lines of boost's graph library), and I might desire a couple of extra composites (like a circular buffer in the vein of Metrowerks cdeque). But I wouldn't point to having free functions as bad design unless there were actual practical reasons why such would compromise my conditions of good design. I have yet to see such an argument in this thread.

    And finally (really, is galathaea really almost done??) I am just curious. Pre and post condition verifications have always meant wrapping to me. The inheritance method would still require wrapping, if I am not mistaken. I fail to see how inheritance would obviate the need for writing wrapping methods for verification of pre and post conditions, but my reading of that particular leaf of this thread, seemed to have this undercurrent like "well If I don't use inheritance, does that mean I have to wrap everything". I'm not trying to put words in anybody's mouth, I just wanted to express my opinion that that is pretty much the type of activity that requires wrapping, independent of the class model used, at least as far as my limited time in this field has shown.
    */*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/

    "It's hard to believe in something you don't understand." -- the sidhi X-files episode

    galathaea: prankster, fablist, magician, liar

  6. #141
    Join Date
    Nov 2003
    Location
    Pasadena, CA
    Posts
    48
    Galathaea,

    This thread has kinda run the gamut of topics (and the odd flame). Any chance you can revisit your last post and insert some quotes so I can better understand what and whose statements you are refering to?


    I've done a fair amount of defending STL in this thread, so in the interest of balance - I'll throw in a personal bash agaist it.

    STL represented the first major foray into generic programming (as a paradigm) for C++, with a large part of it transplanted from the Ada community. Since generic and policy styled designs were not yet well known or excepted in the C/C++ community (hey, even templates were a pretty tough sell at the time) the STL does have some problems with inflexibilty. Refractoring STL using a more policy-based approach would help it a lot in my opinion.

    Now let me defend this with a example...

    The standard does not specify the growth strategy for a vector - yet it is often required to understand the growth strategy when you're explicitly managing the underlying memory (shared memory, memory mapped vector, etc). Some of this can be done with Allocators which are very similar in style to a policy class. But Allocators had a very different puprose when they began life than to represent the "allocation policy". And, allocation is not growth. Imagine I need a vector of 7 elements and I have just enough memory for all 7 elements, but the vector only requests (during resizes) vectors of multiples of 5 (0, 5, 10, etc). Now, I cannot actually build my vector of 7 elements. I'm limited to a vector of 5 because that is the largest vector to fit within my memory requirements based on the vector growth pattern. Now, if it's got to be portable - your hosed because every vendor may use a different growth pattern.

    The growth pattern of a vector needs to be user specifiable. There are valid reasons for having a growable-array with O(N) growth efficency instead of O(1). There is a tradeoff between efficency and memory use.

    Yes, I know about the reserve and swap trick but this doesn't help. (I can provide an example if you really want) but the main point is it is impossible (as far as I can tell) in STL to use std::vector<T> is such a way that memory use is linear to the number of elements (capacity is never greater than high watermark size).

    Valid uses for this are when insertions are rare or when memory is constrained.

    STL while focusing mainly on algo efficency ignored an important fact in practical designs; memory is a finite, constrained, managed resource. The coupling between a vector's size and it's capacity are to fundamental to the application (the programming) to be left implementation specific. The only control the standard allows is through the Allocators. But allocators can only chose to grant the memory request or reject it. This is to inflexible for practical application designers and developers. We need the control necessary to ensure that the growth strategy of the vector is compatible with the overall memory managment, allocation, and distribution policies of the application architecture.
    Last edited by mclark; November 22nd, 2003 at 03:25 PM.
    The views expressed are those of the author and do not reflect any position taken by the Goverment of the United States of America, National Aeronautics and Space Administration (NASA), Jet Propulsion Laboratory (JPL), or California Institute of Technology (CalTech)

  7. #142
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940
    Ok, my problem with STL is readability and this is from MSDN :

    Code:
    using namespace std;
    
       // Using the constructor to declare and initialize a pair
       pair <int, double> p1 ( 10, 1.1e-2 );
    
       // Compare using the helper function to declare and initialize a pair
       pair <int, double> p2;
       p2 = make_pair ( 10, 2.22e-1 );
    
       // Making a copy of a pair
       pair <int, double> p3 ( p1 );
    
       cout.precision ( 3 );
       cout << "The pair p1 is: ( " << p1.first << ", " 
            << p1.second << " )." << endl;
       cout << "The pair p2 is: ( " << p2.first << ", " 
            << p2.second << " )." << endl;
       cout << "The pair p3 is: ( " << p3.first << ", " 
            << p3.second << " )." << endl;
    
       // Using a pair for a map element
       map <int, int> m1;
       map <int, int>::iterator m1_Iter;
    
       typedef pair <int, int> Map_Int_Pair;
    
       m1.insert ( Map_Int_Pair ( 1, 10 ) );
       m1.insert ( Map_Int_Pair ( 2, 20 ) );
       m1.insert ( Map_Int_Pair ( 3, 30 ) );
    
       cout << "The element pairs of the map m1 are:";
       for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )
          cout << " ( " << m1_Iter -> first << ", "
               << m1_Iter -> second << " )";
       cout   << "." << endl;
    
       // Using pair as a return type for a function
       pair< map<int,int>::iterator, bool > pr1, pr2;
       pr1 = m1.insert ( Map_Int_Pair ( 4, 40 ) );
       pr2 = m1.insert ( Map_Int_Pair (1, 10 ) );
    
       if( pr1.second == true )
       {
          cout << "The element (4,40) was inserted successfully in m1."
               << endl;
       }
       else   
       {
          cout << "The element with a key value of\n"
               << " ( (pr1.first) -> first ) = " << ( pr1.first ) -> first 
               << " is already in m1,\n so the insertion failed." << endl;
       }
    
       if( pr2.second == true )
       {
          cout << "The element (1,10) was inserted successfully in m1."
               << endl;
       }
       else   
       {
          cout << "The element with a key value of\n"
               << " ( (pr2.first) -> first ) = " << ( pr2.first ) -> first 
               << " is already in m1,\n so the insertion failed." << endl;
       }
    Yeach !

    Darwen.

  8. #143
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940
    Oh there are so much nicer ways of doing this !!

    Darwen.

  9. #144
    Join Date
    Apr 1999
    Posts
    27,427
    Originally posted by darwen
    Oh there are so much nicer ways of doing this !!

    Darwen.
    It isn't really that difficult to understand. If you remove the cout's here is what's left to understand:

    1) a pair:

    a template class that contains two values: first and second. Each member are of the type specified by their template arguments. It is a very minimal template class when you think about it. Also, there is nothing STL-ish about it. No iterators, nothing.

    2) The make_pair function just sets the pair's first and second members. Again, no big deal to understand.

    3) The pair has a copy constructor, so you can construct one pair with another pair. So far, so good (if you consider "good" to mean understandable).

    4) A map. Similar to the CMap class in MFC (stores key --> element) , but whose interface is different. The std::map stores pair types. The "first" of each pair is the key, and the "second" of the pair is the element. To initialize a map, you can either insert() a pair, or use operator [] to set the key.

    5) A typedef -- hopefully all competent C++ programmers understands what that does.

    6) Iterator -- analogous to a pointer (not exactly a pointer, but for this explanation, it will suffice). The for() loop uses iterators to traverse the map. In MFC, they don't have iterators, but they use a POSITION type. You initialize the POSITION member to the beginning of the CMap before traversing, for the iterator, you do the same thing. You increment the POSITION type by calling GetNextwhatever(). An iterator does the same thing, but instead of GetNextwhatever, you increment the iterator with ++ (sorry don't remember right now what the "incrementor" function is with MFC)

    So, if you know MFC and use the CMap class, the code you posted isn't that difficult to understand. As a matter of fact, it should be straight-forward once you understand the similarities between an iterator and the POSITION type in MFC.

    If you had to write the same thing in MFC, it would look very similar to what you posted, with the exception being the functions to initialize and traverse the map. This is not to say that the MFC version is inferior, just pointing out that the code to do this in MFC would not have been easier or more difficult to understand -- it would be a "push" (using gambling lingo).

    I don't count the "cout's" that you posted, since that is <iostream>, not STL (and cout is one of the first things you learn to use in a C++ course).

    Now C++ *can* get complex -- Andrei Alexandrescu's "Modern C++ Design" is an example of complex code. But complex code can be achieved without any STL whatsoever.

    You mentioned that you do not like templates, and by extension, not like STL. However, the thread (to me at least) was a comparison of MFC to STL. However MFC uses templates also (CMap, CArray, CList, etc.) So there seems to be a disconnect. Maybe you should clarify what it is that you do not like: template vs. non-template code or MFC vs. STL.

    Regards,

    Paul McKenzie

  10. #145
    Join Date
    Aug 2002
    Location
    Madrid
    Posts
    4,588
    Originally posted by mclark
    The growth pattern of a vector needs to be user specifiable. There are valid reasons for having a growable-array with O(N) growth efficency instead of O(1). There is a tradeoff between efficency and memory use.
    I agree entirely on this. The solution IMHO is just to write a my_vector class which does this and add the STL interfaces to it (i.e. iterators etc.). Which means that you can still use all the other parts in STL and if the need should arise, you can probably even change the use of my_vector back to std::vector.

    The standard containers are not perfect for all uses (which is IMHO an impossible goal anyways), but understanding them well makes you aware of potential pitfalls and then you know what you can do to fix this, i.e. change the allocator resp. rewrite your own. The advantage STL has with this is that for most types of operations, if you change the container, all the rest of the code doesn't have to be changed a single bit.

    Darwen's code:
    Code:
    m1.insert ( Map_Int_Pair ( 1, 10 ) );
    m1.insert ( Map_Int_Pair ( 2, 20 ) );
    m1.insert ( Map_Int_Pair ( 3, 30 ) );
    // is the same as (nicer):
    m1[1] = 10;
    m1[2] = 20;
    m1[3] = 30;
    The fact that map::insert returns a pair<iterator, bool> may seem complicated, but it's a major efficiency gain if you want to change/have access to the element later. It may seem complicated when you are not used to STL, but it looks pretty natural to someone who uses these things often.
    Get this small utility to do basic syntax highlighting in vBulletin forums (like Codeguru) easily.
    Supports C++ and VB out of the box, but can be configured for other languages.

  11. #146
    Join Date
    Sep 2002
    Posts
    1,747
    Originally posted by mclark
    This thread has kinda run the gamut of topics (and the odd flame). Any chance you can revisit your last post and insert some quotes so I can better understand what and whose statements you are refering to?
    Well, instead of doing that, which would violate some of the aesthetic considerations I had about the composition of that post, let me instead kind-of "executive summary" the thing.

    The first section was meant to be confrontational, not to a poster in specific (several could fit), but to an "idea". I was laying my cards out; this is where I come from, the experience I have, etc. The kinds of things being cloaked inside some of the package deals presented by the "idea". It was meant as one of those "the emperor has no clothes!" ejaculations, detailing exactly how I felt that despite smoke screens of competence and understanding, the "idea" had yet to present concrete arguments for any of the points kinda muttered in passing, which I felt was avoiding confronting the issue at hand. Which is: what about the standard containers would be a reason for not using them, and: do any of these issues hold favor for other solutions (MFC's containers, hand-rolling, etc.).

    Next, I wanted to point to the discussion earlier about the various paradigms of programming, since I felt this was a nice backgrounder to subsequent points. My main goal was to underscore that some of the design decisions in STL did not need to be analysed inside the domain of objects-orientedness. Even though I subsequently point to the opinions of the dynamic duo of ::XXX c++:: <<More XXX c++>> on how interpreting things inside the OO domain still shows the decisions made by the standard library containers and the separation of algorithms to be a Good Thing (c).

    Since design was being questioned, I wanted to point to the fact that the modern standard library was being constructed around the same time that the various ideas of Aspect-Oriented programming were first being struggled with, and even if the connection was not explicit to those in the design, the zeitgeist is definitely apparent.

    I then wanted to point out exactly some of the design flaws that I felt were avoided by the particular separation of concerns. When building an application, it is always possible to build things tabula rasa. In fact, I have found that an original project without reuse of past project structure benefits immensely from object-oriented design. The hierarchy can be kept very flat, interfaces can be kept minimal, etc. But to benefit from the reuse available by componentisation, we often need to refactor the bits, and I have personally watched while a refactoring process which excluded any templates or generic programming / metaprogramming slowly refactored hierarchies into file system roots / gui system roots / etc. which then later started weaving themselves together in ways where eventually a CObject / QObject / UberObject was desired to add that final layer of indirection to keep the system fluid. Except that the fluidity was maintained at the leaves of the now-monolithic hierarchy, the core had pretty much settled in, and interface bloat abounded. The framework now dictated large design issues that should have been application specific. I have also personally witnessed how the use of generic and preprocessor techniques have been used in refactoring subsystems out of such core-freeze, application architecture once again became fluid, and reuse suddenly grew thousands of times more powerful (subjective sense of freedom metric used here). I witnessed this myself when I was tossed into an architectural position I was originally completely unprepared for, and I feel I understand on a very intuitive level now exactly why decisions like those made in designing the standard library were made. Frameworks need orthogonal flexibility to maximise reuse, and I have this empathetic sixth-sense when looking over the design of the standard library that these guys were struggling with the same issues I know so intimately.

    There was a book I read quite a while back (which I can't for the life of me remember which one it was) which gave the suggestion that learning a new programming language every year would show benefits in design decisions. I've found this to be exceptionally true and wanted to share some of the more theoretical epiphanies I've had. I've found c++ to be very much the English of the programming languages, which because of its large, expressive vocabulary is able to formulate nearly any type dynamic I've seen in any other language. C++ sleeps around; it doesn't stay loyal to some fundamental ideal or programming paradigm; it's more pragmatic than that.

    And finally (except for a P.S. comment) I wanted to add my own two cents about where the design could be better. I think one of the first problems when approaching a problem domain is that one does not originally think "big enough". Its a natural tendency, because often the problem domain is already so huge that it takes significant amount of work to accomplish. So I wanted to point out that in my opinion, now, after having the standard library's containers available to work with, my opinion is that the idea of containment was not explored more fully. Decisions were made to get a useful utility set of linear containers. However many of the concepts of the container tools implemented by the standard could usefully undergo an additional refactoring into policies, and these components could be useful in facilitating non-linear containers such as circular buffers, deques, linked lists and trees and graphs as well. Additionally, as was subsequently suggested, decisions not specified by the standard could be factored out into alternative composable policy units as well. One further possibility that comes to mind as an example is the size policy on lists (ie, does list store size or calculate it when requested, which affects complexity orders for various members). Because I feel that the extended notion of container with more access to composability of aspects is useflu throughout programming and is the perfect thing to be standardised (parse trees in lexical analysis, visibility object graphs in games, circular buffers in streaming audio playback, just to mention a few examples).

    Now, addressing in specific darwen, let me just say that although I realise you started this thread, and mine and some other's responses very likely have sounded harsh, I don't think anyone here has a negative opinion of where you are coming from. It sounds, with your comments on template blah-blah and namespace yadda-yadda, that you have reached a comfortable position in your programming and are avoiding delving deeper into the language. I understand that, as I have reached comfort zones along the way as well (as I think most have). Each false summit in learning allows one to get certain types of tasks done easily at the expense of not addressing the needs for other tasks. But there is a reason that books like Accelerated c++ are suggested in the forums for beginners (which, among other things, avoids alot of those false summits that other teaching methods tend to promote). There is a reason why people read magazines like the c/c++ user's journal to eek out the latest metaprogramming wizardry or exception handling behind-the-back razzamatazz. In computer science, among other knowledge-based careers, there is never a time to stop learning. Yes that's a tiring thought (and even more tiring in practice), but its the reality of the field. Until or unless you find that cushy legacy maintenance job with a stable corporation, you will need to understand how others are programming, which means tackling the fears and learning the language. All of the language.
    */*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/

    "It's hard to believe in something you don't understand." -- the sidhi X-files episode

    galathaea: prankster, fablist, magician, liar

  12. #147
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940
    Actually my point was that this code is mildly unreadable. Yes you can find your way around it but it does take thought and consideration : it's not immediately obvious what its doing.

    However, I have seen far far worse than this in MSDN - I just couldn't track it down at the time.

    I think at times STL verges on the absolutely unreadable there's not really any need for it.

    I try to make my code read as much like English as possible (surely that's the job of any language otherwise why have operator overloading ?). Then when I go back to it in a year or mores time it's obvious what it's trying to do.

    STL is about as far from english as you can get in my opinion.

    The way I do this is to inherit classes as name them appropriately e.g. CApplicationNameString, CComputerNameString, CParentDirectoryString, CFilesInDirectoryArray etc etc.

    Apart from the fact that in doing this you can reduce the amount of code you write considerably it actually makes for a pretty nice thing to look it.

    Yes, you can do all of these by using a member std::string but then you lose all the functionality of the string class if you don't implement explicit functions. And implementing a 'std::string &Get()' function inside such a class is messy and detracts from encapsulation.

    I can however see some large advantages to having a library which is obviously very useful and feature rich. I just don't like the way that its naming conventions work. And I certainly don't like the lack of virtual constructors (yep, I said it again).

    Darwen.

  13. #148
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940
    Thanks galathaea for that jem of wisdom. Can we have it in english now please for us plebs who haven't swallowed a dictionary ?

    In actual fact I am constantly learning new things, and if forced to I'd learn STL too. I already know much of it, I just don't like it ! Yes, it has some extremely powerful attributes and I must admit I do like the seperation of algorithm from data structure which is inherently very powerful. I just find it cumbersome to use and hard to maintain thats all.

    I also have a sixth sense when it comes to good design (if I can be as arrogant as to say so) and yes having a flat object base (i.e. no inheritance) is very nice with no complex hierarchies to clog up the system.

    But I still don't like STL.

    It seems highly programmer unfriendly to me. Nothing is obvious, and we have lovely member functions which tell us so much about what they do like c_str().

    Do you call all your variables useful names like pLNMT or NAV ? Probably not because it would make your code completely incomprehensible (Microsoft is good at doing this especially with its DirectShow example code which I recently had the misfortune to try to make head or tail of).

    As I have said before, and will say again I have nothing against the STL if people want to use it. I just don't like it BECAUSE IT IS UNREADABLE !

    So many, many things have been added to C++ to enable people to write code resembling English, or to do things in one line which in C would take many like operator overloading etc. All of this in the name of readability.

    Sometimes I think the people who came up with STL started off saying to themselves 'right, how can we scare new programmers half to death ? Ah ! I know !'.

    All the source code I write I try to make as simple to understand as I possibly can. I can show it to my 13 year old son who doesn't even know Visual Basic and he can understand what is going on, what every function is for and how it all fits together. Show him some STL code and he just goes 'What ??'.

    So in short, yes there are advantages to using STL and in principle it's a great idea with loads of nice things. But I won't use it because I'd like to be able to maintain my code without having a nervous breakdown in the process.

    Darwen.

  14. #149
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940
    Sorry, just read another reply. Gee I'm having to fight my corner hard.

    Basically, templates are very powerful and useful things but not particularly reader friendly especially when in .h files.

    I do use templates, but prefer to use multiple inheritance and virtual functions wherever I can instead.

    True, the code I posted isn't particularly hard to understand but there are ways of making it much much nicer.

    I hate the idea of having to typedef things to make them more readable. They should be readable in the first place if you ask me.

    Again, C++ can get really messy but messy code is bad code in my opinion. If you start out with a library which isn't easy to read in the first place you're going to have problems later on.

    I don't use CArray, CList etc either since CPtrArray, CObArray, CDWordArray and CUIntArray fulfil the vast majority of my needs. I don't like CStringArray too much since it returns CStrings which is inefficient but there you go.

    However, you CAN INHERIT from these to make them more readable whereas with STL you can't.

    I seem to be running around in circles here. I honestly think that no-one is going to change my mind about STL. The fact you have to typedef things is really nasty since one programmer's bound to call a std::vector<int> something in one place in the code, and another is going to call it something different somewhere else. Yes you can hold the typedefs in a shared .h file but it's not very object-oriented is it ?

    Oh well, I'm bound to get half a million flames again. Good job I'm a masochist. Probably why I'm married (laugh).

    Isn't there anyone out there who thinks that stl is nasty to read ? Come on, be honest !

    Darwen.

  15. #150
    Join Date
    Aug 2002
    Location
    Madrid
    Posts
    4,588
    Well, STL is indeed difficult to read if you are not used to templates. If you don't know much of STL it's not making things better either. This is one of the reasons, I guess, students are not really taught to use STL at college. Then again there can be a host of other reasons.

    But, STL is part of C++ as much as templates are, so if you use STL you can be pretty sure that the next guy on the job will be able to understand your code if he is proficient in C++. Knowledge of STL is part of being a proficient C++ programmer, regardless of what your personal opinion about it is. Knowledge of templates gets the same treatment.

    Addionally, some STL constructs can be a bit more complicated than you first thought, but many times, it makes things much clearer. There are numerous examples on the Non-VC++ forum where people come up with STL-one liners for problems. When I started out with STL, I did have some problems with readability. The code just looked strange and very unnatural. But I guess I just got used to it, just in the same way as I got used to templates or involved class hierarchies.
    Get this small utility to do basic syntax highlighting in vBulletin forums (like Codeguru) easily.
    Supports C++ and VB out of the box, but can be configured for other languages.

Page 10 of 19 FirstFirst ... 78910111213 ... LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center