• std::map advocacy

    From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Sun Apr 7 18:38:54 2024
    From Newsgroup: comp.lang.c++

    On Sun, 7 Apr 2024 16:06:02 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Different people have different needs from C++, and use different
    features. If I were programming on a PC, I'd happily use std::map<>
    and the like. But I don't program on PC's, and I don't use
    std::map<>. If I needed something like that, I'd make my own class
    that fitted my requirements tightly - a factor of 10 inefficiency is
    often fine on big systems, but not on the devices I work with.

    But that's all fine - use whatever you find useful from the language.



    Factor of 5-10 (mentioned in my other post) is quite rare and mostly
    applies not to std::map vs custom implementation of RB or AVL tree, but
    to std::map vs some form of hash, esp. when the nature of the key is
    such that relatively simple hash function happens to have good
    collision avoidance properties and when the database is huge, so O(1)
    vs O(logN) starts to be significant.

    Also pay attention that in situations where you do a lot of insertions
    and deletions, and especially of deletions followed by insertions, std::map::extract() could be of great help in reducing a cost of memory allocation/delallocation to necessary minimum. It's relatively new
    addition to std::map (C++17) and I'd guess that many C++ programmers
    are not aware of its existence.

    In my recent experience (I was implementing median filter over window
    of few 1000s values), a use of extract() made clean and simple
    std::map-based solution (well, actually std::multiset in this
    particular case, but the same principles applies to std::set/multiset/amp/multymap) faster than relatively more complicated
    code based on boost:intrusive:map.

    In this case I happened to have enough time to rewrite it in C with
    my own implementation of AVL tree. My own code was only some 10 or
    15% faster than std container's. And it took more than a day (may be
    more than two days? I don't remember) to write and to debug. And that's
    for me, who knows the algorithm (from TAoCP) for more than 40 years.
    For somebody else it could easily take a weak.
    In short, std::map and relatives is quite good piece of work.
    Highly recommended. Consider me fan.

    At the end in particular case of my unusually long median filter, it
    turned out that AVL or RB tree is not the best structure for the job. A
    couple of other structures (heap and tournament tree) work better. But
    they don't work much better, only by factor of ~2.5. However both heap
    and tournament tree are far less generically applicable data structures
    than AVL/RB tree. They just happened to be better in this quite
    special case.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Sun Apr 7 18:06:29 2024
    From Newsgroup: comp.lang.c++

    On 07/04/2024 17:38, Michael S wrote:
    On Sun, 7 Apr 2024 16:06:02 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Different people have different needs from C++, and use different
    features. If I were programming on a PC, I'd happily use std::map<>
    and the like. But I don't program on PC's, and I don't use
    std::map<>. If I needed something like that, I'd make my own class
    that fitted my requirements tightly - a factor of 10 inefficiency is
    often fine on big systems, but not on the devices I work with.

    But that's all fine - use whatever you find useful from the language.



    Factor of 5-10 (mentioned in my other post) is quite rare and mostly
    applies not to std::map vs custom implementation of RB or AVL tree, but
    to std::map vs some form of hash, esp. when the nature of the key is
    such that relatively simple hash function happens to have good
    collision avoidance properties and when the database is huge, so O(1)
    vs O(logN) starts to be significant.


    Sure.

    But remember, for small embedded systems you don't have dynamic memory -
    or if you do, you use it in limited and tightly controlled manner. And
    in a real-time system, it is the worst-case time that matters, unlike
    PC's where you are generally looking for average or amortized times. So
    a data structure which allocates and deallocates memory unpredictably,
    can use complex tree structures with little control of their worst-case
    times - no thanks!

    (And again, that does not mean these containers are not great choices
    for all kinds of other uses.)

    Also pay attention that in situations where you do a lot of insertions
    and deletions, and especially of deletions followed by insertions, std::map::extract() could be of great help in reducing a cost of memory allocation/delallocation to necessary minimum. It's relatively new
    addition to std::map (C++17) and I'd guess that many C++ programmers
    are not aware of its existence.

    In my recent experience (I was implementing median filter over window
    of few 1000s values), a use of extract() made clean and simple
    std::map-based solution (well, actually std::multiset in this
    particular case, but the same principles applies to std::set/multiset/amp/multymap) faster than relatively more complicated
    code based on boost:intrusive:map.

    In this case I happened to have enough time to rewrite it in C with
    my own implementation of AVL tree. My own code was only some 10 or
    15% faster than std container's. And it took more than a day (may be
    more than two days? I don't remember) to write and to debug. And that's
    for me, who knows the algorithm (from TAoCP) for more than 40 years.
    For somebody else it could easily take a weak.
    In short, std::map and relatives is quite good piece of work.
    Highly recommended. Consider me fan.

    At the end in particular case of my unusually long median filter, it
    turned out that AVL or RB tree is not the best structure for the job. A couple of other structures (heap and tournament tree) work better. But
    they don't work much better, only by factor of ~2.5. However both heap
    and tournament tree are far less generically applicable data structures
    than AVL/RB tree. They just happened to be better in this quite
    special case.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Sun Apr 7 16:09:04 2024
    From Newsgroup: comp.lang.c++

    Michael S <already5chosen@yahoo.com> writes:
    On Sun, 7 Apr 2024 16:06:02 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Different people have different needs from C++, and use different
    features. If I were programming on a PC, I'd happily use std::map<>
    and the like. But I don't program on PC's, and I don't use
    std::map<>. If I needed something like that, I'd make my own class
    that fitted my requirements tightly - a factor of 10 inefficiency is
    often fine on big systems, but not on the devices I work with.

    But that's all fine - use whatever you find useful from the language.



    Factor of 5-10 (mentioned in my other post) is quite rare and mostly
    applies not to std::map vs custom implementation of RB or AVL tree, but
    to std::map vs some form of hash, esp. when the nature of the key is
    such that relatively simple hash function happens to have good
    collision avoidance properties and when the database is huge, so O(1)
    vs O(logN) starts to be significant.

    The biggest problem I have with std::map is that you cannot
    define a compile time map. For the application where we use
    map, the maps can be quite large (hundreds or thousands of entries)
    and initialization of those maps impacts application startup time
    significantly (several hundred distinct maps).

    We're planning on replacing std::map with a custom class optimized
    to efficiently handle the data (mmap'd disk file generated by
    a python script). A binary search on the primary key is the likely
    search algorithm. The maps don't change during application execution.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Sun Apr 7 19:35:05 2024
    From Newsgroup: comp.lang.c++

    On Sun, 7 Apr 2024 18:06:29 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 17:38, Michael S wrote:
    On Sun, 7 Apr 2024 16:06:02 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Different people have different needs from C++, and use different
    features. If I were programming on a PC, I'd happily use
    std::map<> and the like. But I don't program on PC's, and I don't
    use std::map<>. If I needed something like that, I'd make my own
    class that fitted my requirements tightly - a factor of 10
    inefficiency is often fine on big systems, but not on the devices
    I work with.

    But that's all fine - use whatever you find useful from the
    language.



    Factor of 5-10 (mentioned in my other post) is quite rare and mostly applies not to std::map vs custom implementation of RB or AVL tree,
    but to std::map vs some form of hash, esp. when the nature of the
    key is such that relatively simple hash function happens to have
    good collision avoidance properties and when the database is huge,
    so O(1) vs O(logN) starts to be significant.


    Sure.

    But remember, for small embedded systems you don't have dynamic
    memory - or if you do, you use it in limited and tightly controlled
    manner. And in a real-time system, it is the worst-case time that
    matters, unlike PC's where you are generally looking for average or
    amortized times. So a data structure which allocates and deallocates
    memory unpredictably, can use complex tree structures with little
    control of their worst-case times - no thanks!

    As said in post above, in C++17 when the max size of map is known
    during initialization, allocation and de-allocation can be solved by std::map:extract in the same manner you do it for simpler containers:
    you push the required number of nodes into std::map then immediately
    extract and store in the simple structure, typically managed like a
    stack, but probably not using std::stack. Then during main runtime,
    in very typical "embedded" manner, you pop nodes that you want to
    insert from stack and push extracted nodes back to stack. No malloc, no
    free.
    It was possibly to achieve similar effect before C++17 as well, but it
    required use of custom allocators which are, IMHO, too hard to use.


    (And again, that does not mean these containers are not great choices
    for all kinds of other uses.)


    Partially balanced binary trees, like those used by std::map and by its relatives, are among the most predictable data structures out here.
    Certainly they are far more predictable than generic hash that in the
    worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Sun Apr 7 19:48:45 2024
    From Newsgroup: comp.lang.c++

    On Sun, 07 Apr 2024 16:09:04 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Sun, 7 Apr 2024 16:06:02 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Different people have different needs from C++, and use different
    features. If I were programming on a PC, I'd happily use
    std::map<> and the like. But I don't program on PC's, and I don't
    use std::map<>. If I needed something like that, I'd make my own
    class that fitted my requirements tightly - a factor of 10
    inefficiency is often fine on big systems, but not on the devices
    I work with.

    But that's all fine - use whatever you find useful from the
    language.



    Factor of 5-10 (mentioned in my other post) is quite rare and mostly >applies not to std::map vs custom implementation of RB or AVL tree,
    but to std::map vs some form of hash, esp. when the nature of the
    key is such that relatively simple hash function happens to have good >collision avoidance properties and when the database is huge, so O(1)
    vs O(logN) starts to be significant.

    The biggest problem I have with std::map is that you cannot
    define a compile time map. For the application where we use
    map, the maps can be quite large (hundreds or thousands of entries)
    and initialization of those maps impacts application startup time significantly (several hundred distinct maps).

    We're planning on replacing std::map with a custom class optimized
    to efficiently handle the data (mmap'd disk file generated by
    a python script). A binary search on the primary key is the likely
    search algorithm. The maps don't change during application
    execution.


    std::map is known to not be the best data structure for static search.
    Not too bad, but not the best.

    If you have the case where good hash function does not exist (which is extremely rare) then in your scenario simple sorted vector + binary
    search will likely be 1.5-2 times faster than std::map. Also when both
    key and payload are small, vector can be non-trivially more
    compact than map.

    Nowadays the best structures probably have to take into account cache hierarchy, so fat nodes are somewhat better than simple binary search,
    but that level of advancement should be used only when speed is VERY
    important.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Sun Apr 7 19:44:18 2024
    From Newsgroup: comp.lang.c++

    Am 07.04.2024 um 17:38 schrieb Michael S:

    Factor of 5-10 (mentioned in my other post) is quite rare and mostly
    applies not to std::map vs custom implementation of RB or AVL tree, ...

    That's nonsense. If the tree type is the same the performance is the
    same. The high overhead of a tree comes from the pointer chasing while
    looking up a certain node. The issue is the same for manual implemen-
    tations.
    What could make a map slow is the memory allocation part, but you could
    either chose a fast allocator like mimalloc or use a C++23 flat_map,
    where insertion or removal is as fast as possible.

    In this case I happened to have enough time to rewrite it in C with
    my own implementation of AVL tree. My own code was only some 10 or
    15% faster than std container's. ...

    I also prefer AVL-trees over RB-trees for the faster lookup, but if
    the type of tree is the same and the memory allocation part is the
    same there should be no performance difference between a manual C -implementation and a std::map.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Sun Apr 7 19:45:45 2024
    From Newsgroup: comp.lang.c++

    Am 07.04.2024 um 18:48 schrieb Michael S:

    If you have the case where good hash function does not exist (which
    is extremely rare) then in your scenario simple sorted vector + binary
    search will likely be 1.5-2 times faster than std::map. Also when both
    key and payload are small, vector can be non-trivially more
    compact than map.

    Use a flat_map and you'll get near to the lookup performance of a sorted
    vector but you'll have superior insertion or removal performance.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Mon Apr 8 11:15:35 2024
    From Newsgroup: comp.lang.c++

    On Sun, 7 Apr 2024 19:44:18 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 07.04.2024 um 17:38 schrieb Michael S:

    Factor of 5-10 (mentioned in my other post) is quite rare and mostly applies not to std::map vs custom implementation of RB or AVL tree,
    ...

    That's nonsense. If the tree type is the same the performance is the
    same. The high overhead of a tree comes from the pointer chasing while looking up a certain node. The issue is the same for manual implemen- tations.

    What exactly is nonsense?
    You are saying approximately the same that I said above.

    What could make a map slow is the memory allocation part, but you
    could either chose a fast allocator like mimalloc or use a C++23
    flat_map, where insertion or removal is as fast as possible.


    I didn't look at flat_map yet. I hate to leave leading edge of the
    language.
    In the case that I described in the other post allocation was solved by extract() and pool of nodes. Worked great because in this particular application there were a lot of insertions and deletions, but they
    perfectly balanced each other and total size of multiset remained
    constant (+-1) for majority of its life time.

    In this case I happened to have enough time to rewrite it in C with
    my own implementation of AVL tree. My own code was only some 10 or
    15% faster than std container's. ...

    I also prefer AVL-trees over RB-trees for the faster lookup,

    Since my application was very modification-intensive, from algorithmic perspective AVL is probably slightly worse than RB. I did AVL not
    because of algorithmic superiority, but because I understand it better.

    but if
    the type of tree is the same and the memory allocation part is the
    same there should be no performance difference between a manual C -implementation and a std::map.


    In my particular case of 2K to 10K nodes it was indeed almost the
    same. But for bigger size, e.g. 1M to 10M, manual implementation could
    be significantly faster, because nodes could be made significantly
    smaller, improving cache hit rates.
    That applies to set/multiset more than to map/multimap and to small
    keys more than to big keys.

    On x86-64 with MSVC/gcc/clang each std::set/multiset node occupies 32
    bytes even when key is only 32 bits. With manual code+custom allocator+ knowledge that size of set never exceeds 4G nodes, the size of node
    could be reduced to 16 bytes, still with 2 or 3 bytes to spare. Whether
    it makes significant difference in speed or not, depends on exact size
    of your set, on your hardware, on your access pattern, etc... But it
    surely *could* make a difference.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Mon Apr 8 11:29:11 2024
    From Newsgroup: comp.lang.c++

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by its relatives, are among the most predictable data structures out here.
    Certainly they are far more predictable than generic hash that in the
    worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not guaranteed by
    the standards.

    Different types of systems have different needs. C++ supports a huge
    range of uses, and for some of them, many parts of the standard library
    are simply out of the question. The point was just that claims such as Bonita's that "it is impossible to use C++ without exception[s]", or
    that if you don't use the standard library you are "missing most of
    C++", are just ignorant nonsense. People can choose what they need
    based on a range of requirements and preferences, and that's exactly
    what we want from a general-purpose language.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Mon Apr 8 13:34:57 2024
    From Newsgroup: comp.lang.c++

    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by
    its relatives, are among the most predictable data structures out
    here. Certainly they are far more predictable than generic hash
    that in the worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not guaranteed
    by the standards.

    Different types of systems have different needs. C++ supports a huge
    range of uses, and for some of them, many parts of the standard
    library are simply out of the question. The point was just that
    claims such as Bonita's that "it is impossible to use C++ without exception[s]", or that if you don't use the standard library you are
    "missing most of C++", are just ignorant nonsense. People can choose
    what they need based on a range of requirements and preferences, and
    that's exactly what we want from a general-purpose language.


    My opinion about standard library is very close to that Bonita, even if possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library or of
    the other key feature of the language - virtual functions, then it is
    wiser to avoid C++ altogether.
    BTW, I *don't* use C++ on small embedded targets. More so, I don't use
    it on moderately-sized embedded targets, say, 8-16 MB of RAM, 100-200
    MHz CPU, but without protected memory and without full-featured OS.
    My reasons are more psychological/managerial than technical - to keep
    away developers with wrong mindsets and to prevent those that I can't
    keep away from turning wrongest parts of their mindsets to full volume.




    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Richard Damon@richard@damon-family.org to comp.lang.c++ on Mon Apr 8 07:10:26 2024
    From Newsgroup: comp.lang.c++

    On 4/8/24 5:29 AM, David Brown wrote:
    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by its
    relatives, are among the most predictable data structures out here.
    Certainly they are far more predictable than generic hash that in the
    worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not guaranteed by
    the standards.

    Different types of systems have different needs.  C++ supports a huge
    range of uses, and for some of them, many parts of the standard library
    are simply out of the question.  The point was just that claims such as Bonita's that "it is impossible to use C++ without exception[s]", or
    that if you don't use the standard library you are "missing most of
    C++", are just ignorant nonsense.  People can choose what they need
    based on a range of requirements and preferences, and that's exactly
    what we want from a general-purpose language.


    The Standard gives complexity guarantees for some operations on most containers which largely limit what sort of method can be used by each
    of them.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From wij@wyniijj5@gmail.com to comp.lang.c++ on Mon Apr 8 19:18:06 2024
    From Newsgroup: comp.lang.c++

    On Mon, 2024-04-08 at 13:34 +0300, Michael S wrote:
    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by
    its relatives, are among the most predictable data structures out
    here. Certainly they are far more predictable than generic hash
    that in the worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.
     

    As far as I know, details of how containers such as std::map are implemented are left to the implementation - they are not guaranteed
    by the standards.

    Different types of systems have different needs.  C++ supports a huge range of uses, and for some of them, many parts of the standard
    library are simply out of the question.  The point was just that
    claims such as Bonita's that "it is impossible to use C++ without exception[s]", or that if you don't use the standard library you are "missing most of C++", are just ignorant nonsense.  People can choose
    what they need based on a range of requirements and preferences, and
    that's exactly what we want from a general-purpose language.


    My opinion about standard library is very close to that Bonita, even if possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library or of
    the other key feature of the language - virtual functions, then it is
    wiser to avoid C++ altogether.
    I would say that maybe you don't know how to use C++ effectively.
    Ex1: I use my own C++ library (based on Clib), burden (learning, remembering,...) is super low.
    all that user need to know are very reusable (c++ std-library has lots of things to learn that
    are only meaningful in that library). E.g. my 'filesystem' is based on file descriptor, I don't
    think what in the std c++ lib can beat mine (except it considers lots more usage environments).
    Ex2: Qt is the software I admired since learn C++. It does not use std c++ library.
    BTW, I *don't* use C++ on small embedded targets. More so, I don't use
    it on moderately-sized embedded targets, say, 8-16 MB of RAM, 100-200
    MHz CPU, but without protected memory and without full-featured OS.
    My reasons are more psychological/managerial than technical - to keep
    away developers with wrong mindsets and to prevent those that I can't
    keep away from turning wrongest parts of their mindsets to full volume.




    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Mon Apr 8 14:09:32 2024
    From Newsgroup: comp.lang.c++

    Am 08.04.2024 um 10:15 schrieb Michael S:

    What exactly is nonsense?
    You are saying approximately the same that I said above.

    You said that a cusom implementation of a RB- or AVL-tree is multiple
    times faster. I said that this depends on the tree's properties because
    you've got pointer chasing with trees; and you can't improve that with
    a custom implementation.

    On x86-64 with MSVC/gcc/clang each std::set/multiset node occupies 32
    bytes even when key is only 32 bits. ...

    There ar three pointers: to the parent and to the children and if a
    RB-tree is used there's a flag that determines the colour of the node.
    There's nothing to get around that.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Mon Apr 8 16:26:34 2024
    From Newsgroup: comp.lang.c++

    On Mon, 8 Apr 2024 14:09:32 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 08.04.2024 um 10:15 schrieb Michael S:

    What exactly is nonsense?
    You are saying approximately the same that I said above.

    You said that a cusom implementation of a RB- or AVL-tree is multiple
    times faster. I said that this depends on the tree's properties
    because you've got pointer chasing with trees; and you can't improve
    that with a custom implementation.


    I never said what you think I said.
    In fact, I said an opposite - that bigger factors could happen only when competing structure/algorithm is *not* a balanced binary tree of any
    sort.

    On x86-64 with MSVC/gcc/clang each std::set/multiset node occupies
    32 bytes even when key is only 32 bits. ...

    There ar three pointers: to the parent and to the children and if a
    RB-tree is used there's a flag that determines the colour of the node. There's nothing to get around that.

    There is little (not nothing, see below) to do about it under
    restrictions of std API. In the absence of restrictions several thing
    possible.

    Even under restrictions of std API, parent link is not strictly
    necessary. In absence of knowledge about expected usage, it's probably
    wise to have it. But when the usage is known there are cases where one
    would do better without it. Such cases are not even too rare.

    And if you wonder, no, I don't think that taken in isolation removal
    of parent link could provide measurable gain. It's just one example of
    what could be done when you are determined to shrink the size of the
    node.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Mon Apr 8 15:30:03 2024
    From Newsgroup: comp.lang.c++

    On 08/04/2024 12:34, Michael S wrote:
    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by
    its relatives, are among the most predictable data structures out
    here. Certainly they are far more predictable than generic hash
    that in the worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not guaranteed
    by the standards.

    Different types of systems have different needs. C++ supports a huge
    range of uses, and for some of them, many parts of the standard
    library are simply out of the question. The point was just that
    claims such as Bonita's that "it is impossible to use C++ without
    exception[s]", or that if you don't use the standard library you are
    "missing most of C++", are just ignorant nonsense. People can choose
    what they need based on a range of requirements and preferences, and
    that's exactly what we want from a general-purpose language.


    My opinion about standard library is very close to that Bonita, even if possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library or of
    the other key feature of the language - virtual functions, then it is
    wiser to avoid C++ altogether.

    Sorry to say, but I think that is an absurd attitude.

    There is a cost to using C++, rather than C. You lose a couple of
    features (such as compound literals), and there are few very minor cases
    where the same code is valid as C and C++ but has slightly different semantics. You need a C++ compiler (rarely an issue these days). You
    lose the clear match between HLL identifiers and generated code identifiers.

    Perhaps the biggest cost risk is that it opens doors for some people in
    a team to write code that others in the team don't understand. But that
    is a development process issue that can apply regardless of language choice.

    So often, the cost of using C++ rather than C is small. That means you
    only need minor benefits before it is worth making the change. And
    there are a /lot/ of features of C++ that can be used to benefit the
    code, even when the style is basically C.

    This means it can be wise to move to C++ merely to get better type
    checking for enumerated types, nicer "const", and fewer uses of "typedef".

    In my last embedded C++ project, some of the C++ features I have made
    heavy use of include :

    * classes
    * template classes (on classes and non-class template parameters)
    * CRTP (compile-time polymorphism)
    * class inheritance, including multiple inheritance for methods, but
    without run-time polymorphism
    * namespaces
    * strong enumerations
    * RAII
    * std::optional<> and std::variant<>
    * references
    * constexpr functions
    * inline variables
    * function overloads
    * std::array<>
    * lambdas
    * auto

    Some things I have not used include :

    * virtual functions
    * "big" standard library features such as iostreams or containers (other
    than std::array)
    * exceptions
    * rtti
    * new, delete, or other allocations


    Should I not have used C++ ? Should I have stuck to C because some
    people think that feature X is the be-all and end-all of C++ ?

    BTW, I *don't* use C++ on small embedded targets. More so, I don't use
    it on moderately-sized embedded targets, say, 8-16 MB of RAM, 100-200
    MHz CPU, but without protected memory and without full-featured OS.

    I don't have a lower bound for use of C++.

    It certainly used to be the case that C++ was unsuitable for very small systems - indeed, there was a time when C was unsuitable for very small systems. But C++ and C++ tools have improved greatly, and the power of
    "very small" embedded systems has gone up. In the past decade at least,
    I've only used C for continuing work on existing programs or working
    with other people's C projects (though of course that's a large part of
    the job).

    My reasons are more psychological/managerial than technical - to keep
    away developers with wrong mindsets and to prevent those that I can't
    keep away from turning wrongest parts of their mindsets to full volume.


    Those reasons I can understand. Developers with the wrong mindset can
    do harm faster with C++ than with C.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Mon Apr 8 16:32:40 2024
    From Newsgroup: comp.lang.c++

    On Mon, 08 Apr 2024 19:18:06 +0800
    wij <wyniijj5@gmail.com> wrote:


    I would say that maybe you don't know how to use C++ effectively.


    It is possible.
    But more likely that relatively to you I have longer experience in C++
    and better understanding of both advantages and disadvantages.




    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Mon Apr 8 16:37:24 2024
    From Newsgroup: comp.lang.c++

    07.04.2024 19:09 Scott Lurndal kirjutas:

    The biggest problem I have with std::map is that you cannot
    define a compile time map. For the application where we use
    map, the maps can be quite large (hundreds or thousands of entries)
    and initialization of those maps impacts application startup time significantly (several hundred distinct maps).

    We're planning on replacing std::map with a custom class optimized
    to efficiently handle the data (mmap'd disk file generated by
    a python script). A binary search on the primary key is the likely
    search algorithm. The maps don't change during application execution.

    If the map does not change during run, then there is no need to use any map-like data structures or mmap files, a pre-sorted static array should
    be fine for most cases. I am having multiple such "compile-time maps",
    as they are not so large (tens and hundreds of entries) and do not
    change often, I am maintaining them just by hand, no pre-processing complications needed. I do not really trust my sorting abilities, so I
    always also have a small function which is checking the array order (in
    the Debug build, or in a unit test). A bare-bone demo:

    struct A {
    int x;
    double y;
    };

    struct Entry {
    std::string_view key;
    A a;
    };

    static constexpr const Entry gLookup[] = {
    { "alpha", {1, 10.0}},
    { "beta", {2, 20.0}},
    { "gamma", {3, 30.0}}
    };

    #ifdef _DEBUG
    // Check that I have pre-sorted my array correctly.
    static struct CheckLookupTable {
    CheckLookupTable() {
    for (size_t i = 1; i < std::size(gLookup); ++i) {
    assert(gLookup[i].key > gLookup[i - 1].key);
    }
    }
    } gCheck;
    #endif

    const A* Lookup(std::string_view searchKey) {
    auto it = std::lower_bound(
    std::begin(gLookup),
    std::end(gLookup),
    searchKey,
    [](const Entry& a, std::string_view b) {return a.key < b; });

    return ((it != std::end(gLookup) && it->key == searchKey) ?
    &(it->a):
    nullptr);
    }






    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Mon Apr 8 15:45:09 2024
    From Newsgroup: comp.lang.c++

    On 08/04/2024 13:10, Richard Damon wrote:
    On 4/8/24 5:29 AM, David Brown wrote:
    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by its
    relatives, are among the most predictable data structures out here.
    Certainly they are far more predictable than generic hash that in the
    worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not guaranteed
    by the standards.

    Different types of systems have different needs.  C++ supports a huge
    range of uses, and for some of them, many parts of the standard
    library are simply out of the question.  The point was just that
    claims such as Bonita's that "it is impossible to use C++ without
    exception[s]", or that if you don't use the standard library you are
    "missing most of C++", are just ignorant nonsense.  People can choose
    what they need based on a range of requirements and preferences, and
    that's exactly what we want from a general-purpose language.


    The Standard gives complexity guarantees for some operations on most containers which largely limit what sort of method can be used by each
    of them.

    The complexity guarantees in the standard are vague - they are given as
    an indication of "time and/or space complexity", and I assume they mean
    the average or amortized complexity. That's fine for general use, but
    not for real-time systems. (Some people distinguish between "hard
    real-time" and "soft real-time" - the C++ standard guarantees /might/ be acceptable for some "soft real-time" uses.)

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Mon Apr 8 16:54:02 2024
    From Newsgroup: comp.lang.c++

    Am 08.04.2024 um 15:26 schrieb Michael S:

    Even under restrictions of std API, parent link is not strictly
    necessary.

    This would make iterators large since they's have to carry a stack
    ov parent nodes.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Mon Apr 8 17:19:19 2024
    From Newsgroup: comp.lang.c++

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and by
    its relatives, are among the most predictable data structures out
    here. Certainly they are far more predictable than generic hash
    that in the worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access time
    between perfectly balanced case and most unbalanced case is only
    2x. And that applies not just for lookup, but for insertion and
    deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not guaranteed
    by the standards.

    Different types of systems have different needs. C++ supports a huge
    range of uses, and for some of them, many parts of the standard
    library are simply out of the question. The point was just that
    claims such as Bonita's that "it is impossible to use C++ without
    exception[s]", or that if you don't use the standard library you are
    "missing most of C++", are just ignorant nonsense. People can choose
    what they need based on a range of requirements and preferences, and
    that's exactly what we want from a general-purpose language.


    My opinion about standard library is very close to that Bonita, even if >possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library or of
    the other key feature of the language - virtual functions, then it is
    wiser to avoid C++ altogether.

    Now that's just as silly as Bonita's position

    BTW, I *don't* use C++ on small embedded targets.

    I do, and have. Two operating commercial operating
    systems, a hypervisor, and SoC microcode; all in C++ (with some legacy
    C device drivers loaded dynamically). Using a rational
    subset of C++ (with little run-time overhead) makes perfect
    sense for such an application given the data hiding and
    interface advantages of C++ over say, C.


    Because _you_ don't do something doesn't generalize.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Mon Apr 8 20:52:53 2024
    From Newsgroup: comp.lang.c++

    On Mon, 8 Apr 2024 16:54:02 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 08.04.2024 um 15:26 schrieb Michael S:

    Even under restrictions of std API, parent link is not strictly
    necessary.

    This would make iterators large since they's have to carry a stack
    ov parent nodes.



    For modifications, yes. But that's still only LogN, but in time and in
    size of temporary storage. And search from root which in majority of
    workloads is the most common operation by far, is not affected.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 00:17:28 2024
    From Newsgroup: comp.lang.c++

    On Mon, 8 Apr 2024 20:52:53 +0300
    Michael S <already5chosen@yahoo.com> wrote:

    On Mon, 8 Apr 2024 16:54:02 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 08.04.2024 um 15:26 schrieb Michael S:

    Even under restrictions of std API, parent link is not strictly necessary.

    This would make iterators large since they's have to carry a stack
    ov parent nodes.



    For modifications, yes. But that's still only LogN, but in time and in
    size of temporary storage. And search from root which in majority of workloads is the most common operation by far, is not affected.


    I meant to write "both in time and etc..."

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 00:47:50 2024
    From Newsgroup: comp.lang.c++

    On Mon, 08 Apr 2024 17:19:19 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and
    by its relatives, are among the most predictable data structures
    out here. Certainly they are far more predictable than generic
    hash that in the worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access
    time between perfectly balanced case and most unbalanced case is
    only 2x. And that applies not just for lookup, but for insertion
    and deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not
    guaranteed by the standards.

    Different types of systems have different needs. C++ supports a
    huge range of uses, and for some of them, many parts of the
    standard library are simply out of the question. The point was
    just that claims such as Bonita's that "it is impossible to use
    C++ without exception[s]", or that if you don't use the standard
    library you are "missing most of C++", are just ignorant nonsense.
    People can choose what they need based on a range of requirements
    and preferences, and that's exactly what we want from a
    general-purpose language.

    My opinion about standard library is very close to that Bonita, even
    if possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library or of
    the other key feature of the language - virtual functions, then it is
    wiser to avoid C++ altogether.

    Now that's just as silly as Bonita's position

    BTW, I *don't* use C++ on small embedded targets.

    I do, and have. Two operating commercial operating
    systems, a hypervisor, and SoC microcode; all in C++ (with some legacy
    C device drivers loaded dynamically). Using a rational
    subset of C++ (with little run-time overhead) makes perfect
    sense for such an application given the data hiding and
    interface advantages of C++ over say, C.


    What proof do you have that C++ was an advantage for your projects
    rather than handicap?
    Unlike standard library, the things you mention don't sound like
    something that directly improves productivity.
    In particular, is there really a productivity gain from sort of data
    hiding available in C++ vs disciplined C with systematic documentation
    and project-wide naming conventions? I am pretty sure that even if gain
    exists it is below noise.

    One "free" C++ feature that on paper could improve productivity in the
    sort of project that you mention is constant expressions. I have
    no experience with it, so can't say whether the gain is real or its
    just my wrong impression.
    This feature is relatively new, at least in form that no longer looks
    like a toy, so I would guess that you didn't use it in at least 2 out of
    3 project that you mention as success stories.


    Because _you_ don't do something doesn't generalize.

    Didn't I wrote IMHO ? I think I did.

    BTW, in 90s and early 00s I held opinion similar to yours. Since than I certainly gained weight and hopefully gained wisdom.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Mon Apr 8 23:00:47 2024
    From Newsgroup: comp.lang.c++

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 08 Apr 2024 17:19:19 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map and
    by its relatives, are among the most predictable data structures
    out here. Certainly they are far more predictable than generic
    hash that in the worst case degenerates into O(N).
    OTOH, in std::map of given size the difference in worst access
    time between perfectly balanced case and most unbalanced case is
    only 2x. And that applies not just for lookup, but for insertion
    and deletion as well.


    As far as I know, details of how containers such as std::map are
    implemented are left to the implementation - they are not
    guaranteed by the standards.

    Different types of systems have different needs. C++ supports a
    huge range of uses, and for some of them, many parts of the
    standard library are simply out of the question. The point was
    just that claims such as Bonita's that "it is impossible to use
    C++ without exception[s]", or that if you don't use the standard
    library you are "missing most of C++", are just ignorant nonsense.
    People can choose what they need based on a range of requirements
    and preferences, and that's exactly what we want from a
    general-purpose language.

    My opinion about standard library is very close to that Bonita, even
    if possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library or of
    the other key feature of the language - virtual functions, then it is
    wiser to avoid C++ altogether.

    Now that's just as silly as Bonita's position

    BTW, I *don't* use C++ on small embedded targets.

    I do, and have. Two operating commercial operating
    systems, a hypervisor, and SoC microcode; all in C++ (with some legacy
    C device drivers loaded dynamically). Using a rational
    subset of C++ (with little run-time overhead) makes perfect
    sense for such an application given the data hiding and
    interface advantages of C++ over say, C.


    What proof do you have that C++ was an advantage for your projects
    rather than handicap?

    What proof would you except? What makes you think proof is necessary?

    Unlike standard library, the things you mention don't sound like
    something that directly improves productivity.

    Inheritance, pure-virtual (interface) classes leading to clean
    interfaces, improved type safety, destructors, for starters. There's nothing preventing one from developing equivalent facililties as one might
    find in the soi disant standard library as part of the project and
    re-use those "classes" throughout multiple projects.

    In any case, the first C++ OS project I worked on ran from 1989 to 1997,
    and there was no standard C++ library available. The first hypervisor
    project was 1998-1999 (skunk works at SGI - coincidentally, an early standardish C++ library originated at SGI) and the second hypervisor project was from 2004-2010.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 02:20:03 2024
    From Newsgroup: comp.lang.c++

    On Mon, 08 Apr 2024 23:00:47 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 08 Apr 2024 17:19:19 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 8 Apr 2024 11:29:11 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 07/04/2024 18:35, Michael S wrote:

    Partially balanced binary trees, like those used by std::map
    and by its relatives, are among the most predictable data
    structures out here. Certainly they are far more predictable
    than generic hash that in the worst case degenerates into
    O(N). OTOH, in std::map of given size the difference in worst
    access time between perfectly balanced case and most
    unbalanced case is only 2x. And that applies not just for
    lookup, but for insertion and deletion as well.


    As far as I know, details of how containers such as std::map
    are implemented are left to the implementation - they are not
    guaranteed by the standards.

    Different types of systems have different needs. C++ supports a
    huge range of uses, and for some of them, many parts of the
    standard library are simply out of the question. The point was
    just that claims such as Bonita's that "it is impossible to use
    C++ without exception[s]", or that if you don't use the standard
    library you are "missing most of C++", are just ignorant
    nonsense. People can choose what they need based on a range of
    requirements and preferences, and that's exactly what we want
    from a general-purpose language.

    My opinion about standard library is very close to that Bonita,
    even if possibly possibly for the opposite reasons.
    IMHO, if you can't make good use of either C++ Standard library
    or of the other key feature of the language - virtual functions,
    then it is wiser to avoid C++ altogether.

    Now that's just as silly as Bonita's position

    BTW, I *don't* use C++ on small embedded targets.

    I do, and have. Two operating commercial operating
    systems, a hypervisor, and SoC microcode; all in C++ (with some
    legacy C device drivers loaded dynamically). Using a rational
    subset of C++ (with little run-time overhead) makes perfect
    sense for such an application given the data hiding and
    interface advantages of C++ over say, C.


    What proof do you have that C++ was an advantage for your projects
    rather than handicap?

    What proof would you except? What makes you think proof is necessary?

    Unlike standard library, the things you mention don't sound like
    something that directly improves productivity.

    Inheritance, pure-virtual (interface) classes leading to clean
    interfaces, improved type safety,

    Sounds like you misread my original post.
    I wrote that use of virtual function is a sufficient justification for
    choice of C++ over C.
    What I didn't wrote, but had in mind was "when inheritence used to
    implement interfaces, preferably with abstract base classes and *not*
    used for anything else."

    destructors, for starters.

    I'd rather avoid destructors in that types of system.

    There's nothing preventing one from developing equivalent facililties
    as one might find in the soi disant standard library as part of the
    project and re-use those "classes" throughout multiple projects.


    Everything can be developed from scratch. Not doing it improves
    productivity. At least most of the time.

    In any case, the first C++ OS project I worked on ran from 1989 to
    1997, and there was no standard C++ library available. The first
    hypervisor project was 1998-1999 (skunk works at SGI -
    coincidentally, an early standardish C++ library originated at SGI)
    and the second hypervisor project was from 2004-2010.



    Used until scrapped or scrapped before used?








    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c++ on Mon Apr 8 19:42:15 2024
    From Newsgroup: comp.lang.c++

    On 4/8/24 09:45, David Brown wrote:
    ...
    The complexity guarantees in the standard are vague - they are given as
    an indication of "time and/or space complexity", and I assume they mean
    the average or amortized complexity.

    I don't think they are that vague. Some of them are explicitly described
    as amortized - virtually all of which are "amortized constant time", I
    would presume that any complexity guarantee not explicitly described as amortized, isn't.

    That's fine for general use, but
    not for real-time systems. (Some people distinguish between "hard real-time" and "soft real-time" - the C++ standard guarantees /might/ be acceptable for some "soft real-time" uses.)

    The standard's guarantees about complexity are meant to be what
    developers can portably rely upon. Code that doesn't need to be portable
    can rely upon more precise specifications of the complexity provided by
    a particular implementation of C.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From wij@wyniijj5@gmail.com to comp.lang.c++ on Tue Apr 9 08:40:34 2024
    From Newsgroup: comp.lang.c++

    On Mon, 2024-04-08 at 16:32 +0300, Michael S wrote:
    On Mon, 08 Apr 2024 19:18:06 +0800
    wij <wyniijj5@gmail.com> wrote:


    I would say that maybe you don't know how to use C++ effectively.


    It is possible.
    But more likely that relatively to you I have longer experience in C++
    and better understanding of both advantages and disadvantages.




    Any works of yours on the internet that I can understand better what you mean? --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Tue Apr 9 07:34:59 2024
    From Newsgroup: comp.lang.c++

    Am 08.04.2024 um 19:52 schrieb Michael S:

    For modifications, yes. But that's still only LogN, but in time and
    in size of temporary storage. And search from root which in majority
    of workloads is the most common operation by far, is not affected.

    If you've got a large number of nodes this only would be a "problem"
    if an additional cacheline would be loaded for a node.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Tue Apr 9 09:11:16 2024
    From Newsgroup: comp.lang.c++

    On 08/04/2024 23:47, Michael S wrote:

    (I am speaking for myself, and my own projects - and my uses of C++
    might differ a lot from those of Scott.)


    What proof do you have that C++ was an advantage for your projects
    rather than handicap?
    Unlike standard library, the things you mention don't sound like
    something that directly improves productivity.
    In particular, is there really a productivity gain from sort of data
    hiding available in C++ vs disciplined C with systematic documentation
    and project-wide naming conventions? I am pretty sure that even if gain exists it is below noise.

    Seriously? Are you suggesting that documentation of requirements, restrictions and features is just as good as expressing them in code? I assume that your own coding and documentation is flawless, but have you
    never seen code written by anyone else where the source code and the
    comments or documentation are out of sync or directly contradictory?

    The more you can express your requirements and the features of the
    program in code itself, the better. It gives vastly better checking
    than asking someone to read through the documentation. It keeps
    everything in sync. It gives the compiler more knowledge - for better checking, and for more efficient code generation (which often matters a
    lot in embedded systems).

    Tell me, is it better to document that users must call "flubar_init()"
    on their "flubar" objects before calling "flubar_bar()" on them, or is
    it better to make a "flubar" class with a constructor to do the "init" automatically and guaranteed by the language? Is it more productive to
    have developers check each other's code for accidental mixups between
    two enum types, or to have the compiler throw a hard compile-time error?


    One "free" C++ feature that on paper could improve productivity in the
    sort of project that you mention is constant expressions. I have
    no experience with it, so can't say whether the gain is real or its
    just my wrong impression. > This feature is relatively new, at least in form that no longer looks
    like a toy, so I would guess that you didn't use it in at least 2 out of
    3 project that you mention as success stories.


    It's only been around for a decade or so, and thus is relatively young.
    And it certainly has improved since its introduction in C++11. Yes, it
    can be extremely useful.

    But it's only one of the dozen or more features of C++ that I use to be
    more productive and write safer and more efficient code for small
    embedded systems.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Tue Apr 9 09:45:08 2024
    From Newsgroup: comp.lang.c++

    On 09/04/2024 01:42, James Kuyper wrote:
    On 4/8/24 09:45, David Brown wrote:
    ...
    The complexity guarantees in the standard are vague - they are given as
    an indication of "time and/or space complexity", and I assume they mean
    the average or amortized complexity.

    I don't think they are that vague. Some of them are explicitly described
    as amortized - virtually all of which are "amortized constant time", I
    would presume that any complexity guarantee not explicitly described as amortized, isn't.

    I only saw "amortized" used in the context of "amortized constant"
    complexity. (I have not read the standards here very thoroughly, so I
    might have missed something.) I think it is fair to assume that
    something marked "constant" complexity, without "amortized", really is constant - and that will obviously also apply to the worst case.

    Everything else is vague. That's good enough for most uses, and
    stricter guarantees would limit implementations too much. But it is not suitable for real-time systems or systems with tight constraints on time
    or space.


    That's fine for general use, but
    not for real-time systems. (Some people distinguish between "hard
    real-time" and "soft real-time" - the C++ standard guarantees /might/ be
    acceptable for some "soft real-time" uses.)

    The standard's guarantees about complexity are meant to be what
    developers can portably rely upon. Code that doesn't need to be portable
    can rely upon more precise specifications of the complexity provided by
    a particular implementation of C.

    Sure. However, I don't think any of the big C++ standard library implementations provide tighter guarantees. Other implementations of
    standard library containers, such as the EASTL, might have worst-case guarantees - or they might at least have simple enough source code that
    it is practical to determine the complexity by reading the code.

    Typically, however, small embedded systems and hard real-time systems
    have little need for things like std::map<> or std::vector<>, so it is
    not much of an issue in practice. But it is one of the reasons why I
    rule out such containers for use in the code I write.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 12:48:10 2024
    From Newsgroup: comp.lang.c++

    On Tue, 09 Apr 2024 08:40:34 +0800
    wij <wyniijj5@gmail.com> wrote:

    On Mon, 2024-04-08 at 16:32 +0300, Michael S wrote:
    On Mon, 08 Apr 2024 19:18:06 +0800
    wij <wyniijj5@gmail.com> wrote:


    I would say that maybe you don't know how to use C++ effectively.


    It is possible.
    But more likely that relatively to you I have longer experience in
    C++ and better understanding of both advantages and disadvantages.





    Any works of yours on the internet that I can understand better what
    you mean?


    All my code on Internet is for fun and either unrelated or very
    remotely related to my professional work.
    The conservative approach I am preaching in this thread is all about
    pro and is not necessarily applicable to hobby coding.
    Besides, all my code that you can seen on Internet is written by a
    single person whose judgment I tend to trust, possibly mistakenly,
    i.e. by me.
    That is very different even from small work group, yet more different
    from medium-sized group. Tight control is far less important in one
    man shop.
    However even looking at my public github repository you probably can
    see gradual shift from C++ to C, with remaining uses of C++ almost
    exclusively due to productivity gains provided by C++ Standard Library.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 13:12:04 2024
    From Newsgroup: comp.lang.c++

    On Tue, 9 Apr 2024 09:11:16 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 08/04/2024 23:47, Michael S wrote:

    (I am speaking for myself, and my own projects - and my uses of C++
    might differ a lot from those of Scott.)


    What proof do you have that C++ was an advantage for your projects
    rather than handicap?
    Unlike standard library, the things you mention don't sound like
    something that directly improves productivity.
    In particular, is there really a productivity gain from sort of data
    hiding available in C++ vs disciplined C with systematic
    documentation and project-wide naming conventions? I am pretty sure
    that even if gain exists it is below noise.

    Seriously? Are you suggesting that documentation of requirements, restrictions and features is just as good as expressing them in code?

    Naming conventions are expressed in code.
    E.g. if you systematically prefix all you private data members then I
    would think that it's not just as good for productivity as C++ access
    control, but better than it.

    I assume that your own coding and documentation is flawless, but
    have you never seen code written by anyone else where the source code
    and the comments or documentation are out of sync or directly
    contradictory?


    There is no language solution to problem of undisciplined co-worker.

    The more you can express your requirements and the features of the
    program in code itself, the better. It gives vastly better checking
    than asking someone to read through the documentation. It keeps
    everything in sync. It gives the compiler more knowledge - for
    better checking, and for more efficient code generation (which often
    matters a lot in embedded systems).


    I can't see of any C++ feature except templates that can lead to more
    efficient code generation vs C. And I probably wouldn't want templates
    written by ourselves (as opposed to std and saner parts of boost) in my embedded project for reasons mostly unrelated to efficiency.

    Tell me, is it better to document that users must call
    "flubar_init()" on their "flubar" objects before calling
    "flubar_bar()" on them, or is it better to make a "flubar" class with
    a constructor to do the "init" automatically and guaranteed by the
    language?

    It depends.
    For trivial initialization constructor is a little better. For
    non-trivial initialization init function is better, sometimes
    significantly.
    Overall, gain from sane use of constructors is smaller than harm caused
    even by relatively minor misuse of constructors.

    Is it more productive to have developers check each
    other's code for accidental mixups between two enum types, or to have
    the compiler throw a hard compile-time error?


    It's not like C++ enum classes are convenient. It's not like people
    that use them tend to cast left, right and center.
    And its not like even in ideal scenario they can improve productivity.


    One "free" C++ feature that on paper could improve productivity in
    the sort of project that you mention is constant expressions. I have
    no experience with it, so can't say whether the gain is real or its
    just my wrong impression. > This feature is relatively new, at
    least in form that no longer looks like a toy, so I would guess
    that you didn't use it in at least 2 out of 3 project that you
    mention as success stories.

    It's only been around for a decade or so, and thus is relatively
    young. And it certainly has improved since its introduction in C++11.
    Yes, it can be extremely useful.


    In theory, or you have positive first hand experience?
    I mean, something that warrants an epithet "extremely"?

    But it's only one of the dozen or more features of C++ that I use to
    be more productive and write safer and more efficient code for small embedded systems.


    Try one day to start your next project in C. You will likely find out
    that productivity is the same if not improved.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 13:27:22 2024
    From Newsgroup: comp.lang.c++

    On Tue, 9 Apr 2024 09:45:08 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 09/04/2024 01:42, James Kuyper wrote:
    On 4/8/24 09:45, David Brown wrote:
    ...
    The complexity guarantees in the standard are vague - they are
    given as an indication of "time and/or space complexity", and I
    assume they mean the average or amortized complexity.

    I don't think they are that vague. Some of them are explicitly
    described as amortized - virtually all of which are "amortized
    constant time", I would presume that any complexity guarantee not explicitly described as amortized, isn't.

    I only saw "amortized" used in the context of "amortized constant" complexity. (I have not read the standards here very thoroughly, so
    I might have missed something.) I think it is fair to assume that something marked "constant" complexity, without "amortized", really
    is constant - and that will obviously also apply to the worst case.

    Everything else is vague. That's good enough for most uses, and
    stricter guarantees would limit implementations too much. But it is
    not suitable for real-time systems or systems with tight constraints
    on time or space.


    That's fine for general use, but
    not for real-time systems. (Some people distinguish between "hard
    real-time" and "soft real-time" - the C++ standard guarantees
    /might/ be acceptable for some "soft real-time" uses.)

    The standard's guarantees about complexity are meant to be what
    developers can portably rely upon. Code that doesn't need to be
    portable can rely upon more precise specifications of the
    complexity provided by a particular implementation of C.

    Sure. However, I don't think any of the big C++ standard library implementations provide tighter guarantees. Other implementations of standard library containers, such as the EASTL, might have worst-case guarantees - or they might at least have simple enough source code
    that it is practical to determine the complexity by reading the code.

    Typically, however, small embedded systems and hard real-time systems
    have little need for things like std::map<> or std::vector<>, so it
    is not much of an issue in practice. But it is one of the reasons
    why I rule out such containers for use in the code I write.


    You're arguing about unimportant theoretical possibilities.

    In practice 95% of std::map implementations that you will ever
    encounter will be Red-Black Tree and the remaining 5% will be AVL Tree.
    All of them will have lookup, insertion and deletion in O(logN), not
    amortized, but honest worst case. If you want all three major ops and
    if your keys have no special properties than nothing more real-time
    friendly than partially balanced binary tree does not exist. Or at least
    I never heard about it and I tend to keep my ears open to that sort of
    gossips.

    All above not considering memory allocation part, but for robustness of
    the later there exist several solutions mentioned by several posters in
    this sub-thread.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Tue Apr 9 13:16:53 2024
    From Newsgroup: comp.lang.c++

    Am 09.04.2024 um 11:48 schrieb Michael S:

    However even looking at my public github repository you probably can
    see gradual shift from C++ to C, with remaining uses of C++ almost exclusively due to productivity gains provided by C++ Standard Library.

    With C you've got at least five times the code as in C++.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Tue Apr 9 15:01:56 2024
    From Newsgroup: comp.lang.c++

    On 09/04/2024 12:12, Michael S wrote:
    On Tue, 9 Apr 2024 09:11:16 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 08/04/2024 23:47, Michael S wrote:

    (I am speaking for myself, and my own projects - and my uses of C++
    might differ a lot from those of Scott.)


    What proof do you have that C++ was an advantage for your projects
    rather than handicap?
    Unlike standard library, the things you mention don't sound like
    something that directly improves productivity.
    In particular, is there really a productivity gain from sort of data
    hiding available in C++ vs disciplined C with systematic
    documentation and project-wide naming conventions? I am pretty sure
    that even if gain exists it is below noise.

    Seriously? Are you suggesting that documentation of requirements,
    restrictions and features is just as good as expressing them in code?

    Naming conventions are expressed in code.
    E.g. if you systematically prefix all you private data members then I
    would think that it's not just as good for productivity as C++ access control, but better than it.

    I would strongly disagree. If you prefer having your private data
    members named in a particular way, that's fine - do that in /addition/
    to making them "private" class/struct members. But express it in code
    first, documentation or comments as a secondary point.

    I think, as a general point, there is nothing that you can do with your
    coding conventions, discipline and documentation in C that you cannot
    also equally well with C++. Using C++ features to promote safe and
    correct coding, and turn at least some kinds of mistakes into
    compile-time errors, does not in any way preclude non-coding procedures.



    I assume that your own coding and documentation is flawless, but
    have you never seen code written by anyone else where the source code
    and the comments or documentation are out of sync or directly
    contradictory?


    There is no language solution to problem of undisciplined co-worker.

    No, indeed - no language can protect you from a determined fool. But
    the strength of a programming language comes not just from the features
    it provides, but from the limitations it enforces. C++ is more powerful
    than C partly because it provides more ways to restrict incorrect code
    by accident.

    Computer time is free - developer time is not, and the further down the development process a mistake gets, the more costly it is. Any tool
    that makes it easier to find errors earlier, and in a more automated
    fashion, is likely to be a useful choice. So you use a language and
    language features that help here - references instead of pointers, const
    where possible (within reason, of course), small scopes, strong typing,
    strong enums, etc. Mistakes found by your editor/IDE are the cheapest
    and easiest to fix. You run your compiler (or linter) with strong
    warnings, preferably flagged as errors rather than warnings, to help
    enforce coding rules and styles as well as violations of the language's
    type system. You use all the safety-related tools your language and
    your compiler give you - including compiler extensions, to the extent
    possible within portability requirements.

    You can't stop all mistakes through choice of language and use of tools.
    But you can certainly stop many types of error, and it would be silly
    not to do that.


    The more you can express your requirements and the features of the
    program in code itself, the better. It gives vastly better checking
    than asking someone to read through the documentation. It keeps
    everything in sync. It gives the compiler more knowledge - for
    better checking, and for more efficient code generation (which often
    matters a lot in embedded systems).


    I can't see of any C++ feature except templates that can lead to more efficient code generation vs C.

    I agree that templates can often be important in improving efficiency. constexpr can save run-time effort. References can occasionally be more efficient because the compiler knows more about them than pointers - and
    more compiler knowledge can lead to better error checking and better optimisations. inline variables have made it easier to put more stuff
    in headers, leading to more compiler optimisations (LTO is an
    alternative here). Once modules mature, they will help keep this all
    neat and modular.

    But I was being more general than the language choice here - the more
    you write in code, the better. You don't write a comment saying the
    size of struct X is 32 bytes - you write static_assert(sizeof(X) == 32).
    You don't write a comment saying "we can assume x is not negative",
    you write [[assume(x >= 0)]];". You don't write "x /should/ be none-negative", you use some kind of assertion - perhaps with run-time checking during testing and debugging, and an assume attribute when it
    is thoroughly confirmed correct.

    And I probably wouldn't want templates
    written by ourselves (as opposed to std and saner parts of boost) in my embedded project for reasons mostly unrelated to efficiency.

    I use templates regularly. Now that concepts are part of the language I
    have available (embedded toolchains are usually a version or two behind
    the mainstream releases), I have started to use them to make templates
    neater.

    I don't know why people worry so much about using templates. They can
    make some kinds of debugging a lot harder. But they can make things
    safer and more efficient by carrying around details as part of their
    type, rather than as run-time information. I use templates for GPIO pin instances, for example - every GPIO pin is an inline variable of a
    unique type that encapsulates things like the port and pin number,
    active high or low, interrupt support, and so on, as part of the type.
    These types inherit from base template classes with methods for reading
    or setting pins, and other features - a GPIO_In_Out class will inherit
    from both GPIO_In and GPIO_Out template classes. And it all cooks down
    to absolute minimal run-time code with the neatest possible usage in the source code.


    Tell me, is it better to document that users must call
    "flubar_init()" on their "flubar" objects before calling
    "flubar_bar()" on them, or is it better to make a "flubar" class with
    a constructor to do the "init" automatically and guaranteed by the
    language?

    It depends.
    For trivial initialization constructor is a little better. For
    non-trivial initialization init function is better, sometimes
    significantly.
    Overall, gain from sane use of constructors is smaller than harm caused
    even by relatively minor misuse of constructors.


    You believe you can make safe code in C by telling developers to be disciplined and follow rules. Tell them not to misuse constructors, and
    now your C++ is safe for trivial and non-trivial cases.

    Is it more productive to have developers check each
    other's code for accidental mixups between two enum types, or to have
    the compiler throw a hard compile-time error?


    It's not like C++ enum classes are convenient. It's not like people
    that use them tend to cast left, right and center.
    And its not like even in ideal scenario they can improve productivity.


    They are not /quite/ as convenient as I'd like (though "using enum" in
    C++20 helps). But they are pretty good for many purposes. They
    increase productivity by being harder to get wrong than non-scoped
    enums. (And enums in general, combined with compiler checks, are much
    nicer and safer than arbitrary lists of constants.)


    One "free" C++ feature that on paper could improve productivity in
    the sort of project that you mention is constant expressions. I have
    no experience with it, so can't say whether the gain is real or its
    just my wrong impression. > This feature is relatively new, at
    least in form that no longer looks like a toy, so I would guess
    that you didn't use it in at least 2 out of 3 project that you
    mention as success stories.

    It's only been around for a decade or so, and thus is relatively
    young. And it certainly has improved since its introduction in C++11.
    Yes, it can be extremely useful.


    In theory, or you have positive first hand experience?
    I mean, something that warrants an epithet "extremely"?

    I use constexpr constants all the time, though in many (or even most)
    cases a "const" would work too. But probably the most advanced uses of constexpr I have had would be compile-time calculation of a CRC
    acceleration table, and a case where I needed a reversible mapping
    between byte orderings for a data package that my code received and transmitted. I had one constexpr std::array initiated with the mapping
    in one direction (based on external documentation), then the reverse
    constexpr std::array was calculated at compile time. That gave me top efficiency in use, and only one array to change if the ordering changed.

    The alternative for this kind of thing is scripts or programs to
    pre-generate data tables or headers. I have used such scripts many
    times for different programs - C++ constexpr functions don't cover all
    uses, but they do cover a number of them.


    But it's only one of the dozen or more features of C++ that I use to
    be more productive and write safer and more efficient code for small
    embedded systems.


    Try one day to start your next project in C. You will likely find out
    that productivity is the same if not improved.


    I have more than enough experience with C and C++ to know what I am doing.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Tue Apr 9 15:13:54 2024
    From Newsgroup: comp.lang.c++

    On 09/04/2024 12:27, Michael S wrote:
    On Tue, 9 Apr 2024 09:45:08 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Typically, however, small embedded systems and hard real-time systems
    have little need for things like std::map<> or std::vector<>, so it
    is not much of an issue in practice. But it is one of the reasons
    why I rule out such containers for use in the code I write.


    You're arguing about unimportant theoretical possibilities.


    Perhaps you don't understand about what "real-time" means. Real-time development does not care about theoretical possibilities of different implementations, because you fix your system at /one/ implementation.
    But it /does/ care about theoretical possibilities of what could happen
    in that one system. I don't care if some implementation uses Pink-Green
    trees to get O(1) average speed, or another one uses Purple-Brown trees
    to get O(n ^ 27) space complexity. I only care about the implementation
    I am using. And I don't care about its average speed, I only care about
    its worst-case spaces and times.

    I don't care if it does 99.999% of its operations within 1µs, I care if
    I can /guarantee/ that /every/ case is completed with 1 second.

    Sometimes in real-time systems you will prefer a linear search through a
    list, because it is easier to be sure of your limits and the correctness
    of the code in all cases - with the simpler code, it's likely that your
    worst case times are better than many kinds of tree.

    And the same applies to space considerations - the worst case is the
    only relevant case.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Tue Apr 9 18:12:52 2024
    From Newsgroup: comp.lang.c++

    On Tue, 9 Apr 2024 15:13:54 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 09/04/2024 12:27, Michael S wrote:
    On Tue, 9 Apr 2024 09:45:08 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Typically, however, small embedded systems and hard real-time
    systems have little need for things like std::map<> or
    std::vector<>, so it is not much of an issue in practice. But it
    is one of the reasons why I rule out such containers for use in
    the code I write.

    You're arguing about unimportant theoretical possibilities.


    Perhaps you don't understand about what "real-time" means. Real-time development does not care about theoretical possibilities of
    different implementations, because you fix your system at /one/ implementation. But it /does/ care about theoretical possibilities of
    what could happen in that one system. I don't care if some
    implementation uses Pink-Green trees to get O(1) average speed, or
    another one uses Purple-Brown trees to get O(n ^ 27) space
    complexity. I only care about the implementation I am using. And I
    don't care about its average speed, I only care about its worst-case
    spaces and times.

    I don't care if it does 99.999% of its operations within 1µs, I care
    if I can /guarantee/ that /every/ case is completed with 1 second.

    Sometimes in real-time systems you will prefer a linear search
    through a list, because it is easier to be sure of your limits and
    the correctness of the code in all cases - with the simpler code,
    it's likely that your worst case times are better than many kinds of
    tree.

    And the same applies to space considerations - the worst case is the
    only relevant case.

    You continue to argue about unimportant theoretical possibilities.
    std::map is *never* Pink-Green or Purple-Brown, It is either
    Guibas-Sedgewick, better known as red-black or, very rarely, Adelson-Velsky-Landis.
    The only Brown connection that I can think about is that Sedgewick got
    his Master degree from Brown University.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Tue Apr 9 15:22:23 2024
    From Newsgroup: comp.lang.c++

    Michael S <already5chosen@yahoo.com> writes:
    On Mon, 08 Apr 2024 23:00:47 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:


    In any case, the first C++ OS project I worked on ran from 1989 to
    1997, and there was no standard C++ library available. The first
    hypervisor project was 1998-1999 (skunk works at SGI -
    coincidentally, an early standardish C++ library originated at SGI)
    and the second hypervisor project was from 2004-2010.



    Used until scrapped or scrapped before used?

    The former, in all cases except the skunk works project.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Tue Apr 9 18:22:04 2024
    From Newsgroup: comp.lang.c++

    On 09/04/2024 17:12, Michael S wrote:
    On Tue, 9 Apr 2024 15:13:54 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 09/04/2024 12:27, Michael S wrote:
    On Tue, 9 Apr 2024 09:45:08 +0200
    David Brown <david.brown@hesbynett.no> wrote:


    Typically, however, small embedded systems and hard real-time
    systems have little need for things like std::map<> or
    std::vector<>, so it is not much of an issue in practice. But it
    is one of the reasons why I rule out such containers for use in
    the code I write.

    You're arguing about unimportant theoretical possibilities.


    Perhaps you don't understand about what "real-time" means. Real-time
    development does not care about theoretical possibilities of
    different implementations, because you fix your system at /one/
    implementation. But it /does/ care about theoretical possibilities of
    what could happen in that one system. I don't care if some
    implementation uses Pink-Green trees to get O(1) average speed, or
    another one uses Purple-Brown trees to get O(n ^ 27) space
    complexity. I only care about the implementation I am using. And I
    don't care about its average speed, I only care about its worst-case
    spaces and times.

    I don't care if it does 99.999% of its operations within 1µs, I care
    if I can /guarantee/ that /every/ case is completed with 1 second.

    Sometimes in real-time systems you will prefer a linear search
    through a list, because it is easier to be sure of your limits and
    the correctness of the code in all cases - with the simpler code,
    it's likely that your worst case times are better than many kinds of
    tree.

    And the same applies to space considerations - the worst case is the
    only relevant case.


    You continue to argue about unimportant theoretical possibilities.
    std::map is *never* Pink-Green or Purple-Brown, It is either Guibas-Sedgewick, better known as red-black or, very rarely, Adelson-Velsky-Landis.

    There are no guarantees about that at all. I am sure you are correct
    that these are the most common types of implementation - or rather, the implementations used in the most commonly used C++ standard libraries.
    But they are not the only possibilities - do you know that these are
    what are used by IAR's C++ library, and Metrowerks, and EASTL, and any
    other C++ library? No, you do not. You /might/ know that these are
    used in gcc's libc++ and llvm's library, because that is publicly
    available information. But you don't know that other implementations
    might have different implementations due to different requirements, such
    as absolute minimum ram space or code space, or maybe a wider B-tree
    aimed at improving cache friendliness, or something designed for
    parallel access.

    But let's assume that the implementation has both amortized and
    worst-case complexity O(log n) - that's a reasonable assumption and fits
    your insistence that it will be either a type of red-black tree or some
    kind of AVL tree. Where does that get us?

    Do we have a guarantee on the constant factors for the O(log n)
    complexity? No.

    Do we have a way to test the worst-case times? No.

    Do we know if an insertion or deletion will lead to a memory allocation
    or deallocation? No.

    Do we have any idea of a worst-case time for such allocations or deallocations? No.

    Do we have a solid upper bound on the time a given operation will take? No.

    Can we use a std::map in a hard real-time system without doing a great
    deal of work measuring and characterising a particular implementation? No.

    That's not theoretical - it's real.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c++ on Wed Apr 10 02:24:59 2024
    From Newsgroup: comp.lang.c++

    On 4/9/24 11:12, Michael S wrote:
    On Tue, 9 Apr 2024 15:13:54 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    complexity. I only care about the implementation I am using. And I
    don't care about its average speed, I only care about its worst-case
    spaces and times.
    ...
    You continue to argue about unimportant theoretical possibilities.

    He's doing the exact opposite of worrying about theorectical
    possibilities. He's saying that what the implementation he's using
    actually does is more important than your instance that it can only be
    doing something else.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Thu Apr 11 00:07:37 2024
    From Newsgroup: comp.lang.c++

    On Wed, 10 Apr 2024 02:24:59 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 4/9/24 11:12, Michael S wrote:
    On Tue, 9 Apr 2024 15:13:54 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    complexity. I only care about the implementation I am using. And
    I don't care about its average speed, I only care about its
    worst-case spaces and times.
    ...
    You continue to argue about unimportant theoretical possibilities.

    He's doing the exact opposite of worrying about theorectical
    possibilities. He's saying that what the implementation he's using
    actually does is more important than your instance that it can only be
    doing something else.

    He can check and see by himself that all toolsets that he uses have
    std::map implemented on top of red-black tree. It's easy, sources are
    here. But it seems arguing is more fun.
    And one of the the posibilities that he mentiond (wider nodes) does not
    look like even theoretically possible for C++17 and later, because it
    would mess with extract() and the rest of node-based APIs.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Thu Apr 11 09:57:12 2024
    From Newsgroup: comp.lang.c++

    On 10/04/2024 23:07, Michael S wrote:
    On Wed, 10 Apr 2024 02:24:59 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 4/9/24 11:12, Michael S wrote:
    On Tue, 9 Apr 2024 15:13:54 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    complexity. I only care about the implementation I am using. And
    I don't care about its average speed, I only care about its
    worst-case spaces and times.
    ...
    You continue to argue about unimportant theoretical possibilities.

    He's doing the exact opposite of worrying about theorectical
    possibilities. He's saying that what the implementation he's using
    actually does is more important than your instance that it can only be
    doing something else.

    He can check and see by himself that all toolsets that he uses have
    std::map implemented on top of red-black tree. It's easy, sources are
    here. But it seems arguing is more fun.

    No, it is /not/ easy - sources for big C++ standard libraries are far
    from simple to understand. (That is in no way a criticism - I
    understand why they are written the way they are.) I could look up
    sources and see identifiers or comments saying they use red-black trees,
    but as I have already explained several times, knowing that does not
    give me anywhere close to the guarantees that are needed before the code
    is useable for hard real-time systems. Of course I could study the code
    for long enough, and do enough analysis and testing to establish timing guarantees - but it would be vastly simpler and faster to write my own
    data structures from scratch to fulfil real-time requirements. I would
    not expect my implementations to be faster on average for general usage
    - indeed, I'd expect them to be a lot slower. But /knowing/ the limits
    is the important point, not the speed.

    If you don't know what real-time requirements are - what the phrase "real-time" means - that's fine. Most people don't need to know about
    it. Even most people who work with small embedded systems as a
    profession don't really understand it - most embedded systems don't have serious real-time requirements. But some do, and the more advanced C++ standard library features are not something that could be considered for
    them.

    If you are interesting it knowing what real-time means, I can try to
    explain. If you'd rather continue playing salesman for C++ standard
    library containers, then we'll just have to leave it.

    And one of the the posibilities that he mentiond (wider nodes) does not
    look like even theoretically possible for C++17 and later, because it
    would mess with extract() and the rest of node-based APIs.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From wij@wyniijj5@gmail.com to comp.lang.c++ on Thu Apr 11 19:25:44 2024
    From Newsgroup: comp.lang.c++

    On Thu, 2024-04-11 at 09:57 +0200, David Brown wrote:
    On 10/04/2024 23:07, Michael S wrote:
    On Wed, 10 Apr 2024 02:24:59 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 4/9/24 11:12, Michael S wrote:
    On Tue, 9 Apr 2024 15:13:54 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    complexity.  I only care about the implementation I am using.  And I don't care about its average speed, I only care about its worst-case spaces and times.
    ...
    You continue to argue about unimportant theoretical possibilities.

    He's doing the exact opposite of worrying about theorectical possibilities. He's saying that what the implementation he's using actually does is more important than your instance that it can only be doing something else.

    He can check and see by himself that all toolsets that he uses have std::map implemented on top of red-black tree. It's easy, sources are
    here. But it seems arguing is more fun.

    No, it is /not/ easy - sources for big C++ standard libraries are far
    from simple to understand.  (That is in no way a criticism - I
    understand why they are written the way they are.)  I could look up
    sources and see identifiers or comments saying they use red-black trees,
    but as I have already explained several times, knowing that does not
    give me anywhere close to the guarantees that are needed before the code
    is useable for hard real-time systems.  Of course I could study the code for long enough, and do enough analysis and testing to establish timing guarantees - but it would be vastly simpler and faster to write my own
    data structures from scratch to fulfil real-time requirements.  I would
    not expect my implementations to be faster on average for general usage
    - indeed, I'd expect them to be a lot slower.  But /knowing/ the limits
    is the important point, not the speed.

    Yup, the good point of std::list/set/map/... is that users can use it nearly blindingly. But when you calculate the gains/losses, things are different than what is usually thought.
    If you don't know what real-time requirements are - what the phrase "real-time" means - that's fine.  Most people don't need to know about it.  Even most people who work with small embedded systems as a
    profession don't really understand it - most embedded systems don't have serious real-time requirements.  But some do, and the more advanced C++ standard library features are not something that could be considered for them.

    If you are interesting it knowing what real-time means, I can try to explain.  If you'd rather continue playing salesman for C++ standard library containers, then we'll just have to leave it.

    And one of the the posibilities that he mentiond (wider nodes) does not look like even theoretically possible for C++17 and later, because it
    would mess with extract() and the rest of node-based APIs.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Thu Apr 11 13:47:48 2024
    From Newsgroup: comp.lang.c++

    On 11/04/2024 13:25, wij wrote:

    Yup, the good point of std::list/set/map/... is that users can use it nearly blindingly. But when you calculate the gains/losses, things are different than
    what is usually thought.

    Yes, most C++ users can use these standard library containers "blindly".
    And for others that have special requirements, things like custom
    allocators and other flexibilities can help.

    But people working on real-time systems, high-reliability systems, safety-related systems, or very resource-constrained systems can't do /anything/ blindly.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Thu Apr 11 14:04:47 2024
    From Newsgroup: comp.lang.c++

    Am 11.04.2024 um 13:47 schrieb David Brown:

    Yes, most C++ users can use these standard library containers "blindly".
     And for others that have special requirements, things like custom allocators and other flexibilities can help.

    Custom allocators only make sense with a vector. With all other con-
    tainers the standars library does a allocator_traits<X>::rebind_alloc<Y>
    to have a different allocator type. F.e. a map never uses the supplied allocator since the nodes also contain the links. For me the whole allocator-concept mostly doesn't make sense because of that.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Thu Apr 11 16:13:08 2024
    From Newsgroup: comp.lang.c++

    On Thu, 11 Apr 2024 19:25:44 +0800
    wij <wyniijj5@gmail.com> wrote:

    Yup, the good point of std::list/set/map/... is that users can use it
    nearly blindingly. But when you calculate the gains/losses, things
    are different than what is usually thought.


    I can not see a single reason to use std::list. It brings nothing to the
    table relatively to my own lists and just reduces flexibility and
    transparency.
    Associative containers are very different.
    Even std::vector is different - while it does not bring to table a lot,
    it brings quite enough to prefer std over home-bred.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Thu Apr 11 16:18:00 2024
    From Newsgroup: comp.lang.c++

    On Thu, 11 Apr 2024 14:04:47 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 11.04.2024 um 13:47 schrieb David Brown:

    Yes, most C++ users can use these standard library containers
    "blindly". And for others that have special requirements, things
    like custom allocators and other flexibilities can help.

    Custom allocators only make sense with a vector. With all other con-
    tainers the standars library does a
    allocator_traits<X>::rebind_alloc<Y> to have a different allocator
    type. F.e. a map never uses the supplied allocator since the nodes
    also contain the links. For me the whole allocator-concept mostly
    doesn't make sense because of that.


    I tried to implement custom allocator for std::multiset and actually
    succeed. But it was too hard and too ugly. So I happily forgot how to
    do it 2 hours later.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Thu Apr 11 15:31:21 2024
    From Newsgroup: comp.lang.c++

    Am 11.04.2024 um 15:18 schrieb Michael S:
    On Thu, 11 Apr 2024 14:04:47 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 11.04.2024 um 13:47 schrieb David Brown:

    Yes, most C++ users can use these standard library containers
    "blindly". And for others that have special requirements, things
    like custom allocators and other flexibilities can help.

    Custom allocators only make sense with a vector. With all other con-
    tainers the standars library does a
    allocator_traits<X>::rebind_alloc<Y> to have a different allocator
    type. F.e. a map never uses the supplied allocator since the nodes
    also contain the links. For me the whole allocator-concept mostly
    doesn't make sense because of that.


    I tried to implement custom allocator for std::multiset and actually
    succeed. But it was too hard and too ugly. So I happily forgot how to
    do it 2 hours later.

    To make this work a custom allocator should be rebind<>-able to any
    type, thereby having a typed malloc()-like allocator for all types
    and sizes.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c++ on Thu Apr 11 16:39:54 2024
    From Newsgroup: comp.lang.c++

    On Thu, 11 Apr 2024 15:31:21 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 11.04.2024 um 15:18 schrieb Michael S:
    On Thu, 11 Apr 2024 14:04:47 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 11.04.2024 um 13:47 schrieb David Brown:

    Yes, most C++ users can use these standard library containers
    "blindly". And for others that have special requirements, things
    like custom allocators and other flexibilities can help.

    Custom allocators only make sense with a vector. With all other
    con- tainers the standars library does a
    allocator_traits<X>::rebind_alloc<Y> to have a different allocator
    type. F.e. a map never uses the supplied allocator since the nodes
    also contain the links. For me the whole allocator-concept mostly
    doesn't make sense because of that.


    I tried to implement custom allocator for std::multiset and actually succeed. But it was too hard and too ugly. So I happily forgot how
    to do it 2 hours later.

    To make this work a custom allocator should be rebind<>-able to any
    type, thereby having a typed malloc()-like allocator for all types
    and sizes.



    The point is to not be more generic than absolutely necessary for
    particular application.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Thu Apr 11 16:21:44 2024
    From Newsgroup: comp.lang.c++

    Am 11.04.2024 um 15:39 schrieb Michael S:
    On Thu, 11 Apr 2024 15:31:21 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 11.04.2024 um 15:18 schrieb Michael S:
    On Thu, 11 Apr 2024 14:04:47 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wrote:

    Am 11.04.2024 um 13:47 schrieb David Brown:

    Yes, most C++ users can use these standard library containers
    "blindly". And for others that have special requirements, things
    like custom allocators and other flexibilities can help.

    Custom allocators only make sense with a vector. With all other
    con- tainers the standars library does a
    allocator_traits<X>::rebind_alloc<Y> to have a different allocator
    type. F.e. a map never uses the supplied allocator since the nodes
    also contain the links. For me the whole allocator-concept mostly
    doesn't make sense because of that.


    I tried to implement custom allocator for std::multiset and actually
    succeed. But it was too hard and too ugly. So I happily forgot how
    to do it 2 hours later.

    To make this work a custom allocator should be rebind<>-able to any
    type, thereby having a typed malloc()-like allocator for all types
    and sizes.



    The point is to not be more generic than absolutely necessary for
    particular application.


    A rebind()-able allocator is as generic as possible. There's no way
    to have a pool-allocator for a container's given type since the type
    is always rebound.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From wij@wyniijj5@gmail.com to comp.lang.c++ on Thu Apr 11 22:40:34 2024
    From Newsgroup: comp.lang.c++

    On Thu, 2024-04-11 at 13:47 +0200, David Brown wrote:
    On 11/04/2024 13:25, wij wrote:

    Yup, the good point of std::list/set/map/... is that users can use it nearly
    blindingly. But when you calculate the gains/losses, things are different than
    what is usually thought.

    Yes, most C++ users can use these standard library containers "blindly".
      And for others that have special requirements, things like custom allocators and other flexibilities can help.

    But people working on real-time systems, high-reliability systems, safety-related systems, or very resource-constrained systems can't do /anything/ blindly.

    Understand. People here seemingly do not program hard-wares (thus don't really understand C, IMO). I quit hard-ware things (and C) for a long time. But, for embedded systems, esp. real real-time systems, my first choice was Assembly.
    I am not aware whether C++ is suitable for real real-time systems or not.
    I had encountered programming for buggy hardwares (sound efffect chips, and some
    badly designed hard-wares, because the company wanted to save money) where
    some pins had to be read/written in a peculiar way (language optimization will be a problem here). And, an embedded system that had to simulate UART where timeing is really critical in order to communicate with other devices (every machine tick counts). And a system that uses two DOS machines to work as one machine where COM1 is set to the highest priority to provide basic synchronization mechanism...
    And, for embedded systems, RAM/pins is money, related to survival of product. Average people don't understand this.
    High-level languages normally involves more supporting tools, all these need update and learning, not necessarily good.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Thu Apr 11 16:55:47 2024
    From Newsgroup: comp.lang.c++

    On 11/04/2024 16:40, wij wrote:
    On Thu, 2024-04-11 at 13:47 +0200, David Brown wrote:
    On 11/04/2024 13:25, wij wrote:

    Yup, the good point of std::list/set/map/... is that users can use it nearly
    blindingly. But when you calculate the gains/losses, things are different than
    what is usually thought.

    Yes, most C++ users can use these standard library containers "blindly".
      And for others that have special requirements, things like custom
    allocators and other flexibilities can help.

    But people working on real-time systems, high-reliability systems,
    safety-related systems, or very resource-constrained systems can't do
    /anything/ blindly.


    Understand. People here seemingly do not program hard-wares (thus don't really
    understand C, IMO). I quit hard-ware things (and C) for a long time. But, for embedded systems, esp. real real-time systems, my first choice was Assembly. I am not aware whether C++ is suitable for real real-time systems or not.


    C++ is fine for hard real-time and safety-critical systems - as long as
    you use an appropriate subset of the features, and as long as your
    development process is suitable. It's not any different from any other language in that respect. And I'd rate C++ above C, and C above
    assembly, for suitability.

    I had encountered programming for buggy hardwares (sound efffect chips, and some
    badly designed hard-wares, because the company wanted to save money) where some pins had to be read/written in a peculiar way (language optimization will
    be a problem here).

    You need to know how to access the hardware properly, and how to work
    with the language and the tools to achieve that. Sometimes this can be
    a bit fiddly, but it is entirely possible to do in a solid and reliable way.

    And, an embedded system that had to simulate UART where
    timeing is really critical in order to communicate with other devices (every machine tick counts). And a system that uses two DOS machines to work as one machine where COM1 is set to the highest priority to provide basic synchronization mechanism...
    And, for embedded systems, RAM/pins is money, related to survival of product. Average people don't understand this.

    High-level languages normally involves more supporting tools, all these need update and learning, not necessarily good.


    Sure. (But don't update the build tools for a project - they are part
    of the project.)



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Thu Apr 11 18:18:35 2024
    From Newsgroup: comp.lang.c++

    11.04.2024 16:13 Michael S kirjutas:

    I can not see a single reason to use std::list. It brings nothing to the table relatively to my own lists and just reduces flexibility and transparency.

    You are right, but for wrong reasons. There is usually little reason to
    use std::list because std::vector or std::deque is almost always a
    better choice.

    std::list might be occasionally useful for holding a bunch of entity
    objects whose addresses must remain unchanged.

    --- Synchronet 3.20a-Linux NewsLink 1.114