5

I mainly use dynamic languages. For many years I see statically typed hello world examples like

const int STACK_SIZE = 100;

And I will think "wow, I can't think like that". I understand memory management, syntax, pointers (mostly). Please let me into your brain for a second and analyze this use case with me.

Let's say we have a command line application that's going to let us manage employees (exciting!). Let's just focus on adding new employees. We're a trendy new startup that's going to do a moonshot so our employee list size is going to start at 1 but we're eventually going to add 7 billion co-founders and maybe more (aliens? dogs? cats?).

In ruby/python/javascript, I'd just create an array and suffer/refactor later when performance is terrible. Of course, you'd use a database in either C++ or some other language. But let's entertain this for a second. Would a C++ person break the problem down past one data structure? I think of this problem as "an array" problem. But maybe someone with more low-level experience thinks like this?

  • Create a buffer to hold the list as the user adds in names, this can be fixed size
  • Periodically flush the buffer to a file or something like that.

Is that realistic? I understand the requirements here are contrived and vague. But let's say a C++ developer was creating this small application for themselves. Would they just use a smart pointer or something that would grow over time or would they create a std::string*?

Let me give another example. Let's say you write a problem to loop through a file and do something with each line. Some people (including myself) start thinking with a small example file. Then later, you find that your problem can't handle large files. Whoops. Sometime I feel like lower level programmers would naturally pre-optimize out of discipline or experience to read the file in a buffer or something like that. Do you agree?

  • 5
    Apart from what @Useless said, you should be aware that there are lots of online sources using C++ in an old-school way(as C with classes) that make for a poor introduction to modern C++. A modern C++ program won't be that different from a similar python program and should have very little (if any) manual memory management, instead relying on RAII. It should operate on a high abstraction level only becoming low-level in a few key areas. BTW: std::string* is an odd mixing of abstraction levels that you should rarely see. – user786653 Dec 5 '13 at 18:34
  • 1
    Can we come up with a more descriptive title for this question? – Liath Nov 7 '18 at 12:55
20

Small, fixed-size buffers might be used for any of the following reasons:

  1. constraints: either limited memory, or cache performance, or some other speed requirement that absolutely prohibits dynamic (re)allocation
  2. laziness: IMO this is mostly a hang-over from C, which didn't have a rich container library
  3. correctness: if you're matching some external entity (hardware, or a file format or wire protocol) that genuinely has fixed size elements, then why not represent them exactly?

So, let's look at your employee DB example.

  1. is it constrained in memory or performance? Not to start with, even if you're running it on a wristwatch these days
  2. are we either lazy, or writing C 20 years ago? No!
  3. are we reflecting some external truth? Maybe if we move to a DB later, and decide to make some column VARCHAR(100), then having an array of size 100 might be reasonable. But we'd be doing it for expressiveness , not to show our low-level l33tness. And besides, you excluded this case in your question.

The right way to do it would probably be to:

  1. choose a standard container that suits your access patterns (std::list and std::vector are popular all-purpose choices)
  2. write some class to represent an employee
  3. write some code to (de)serialize this employee

If you do this right, it manages all the memory for you, and doesn't require any fixed-length anything. It will also look somewhat similar to the equivalent logic in Python or javascript.


tl;dr

your stated problem is not a low-level problem, so there's no reason to trade off clarity or simplicity for low-level magic.

If you had a low-level problem, those low-level design decisions would proceed from the problem & solution domains.

Using low-level style on a high-level problem is just bad style, because it doesn't match the problem domain.

  • 1
    Great answer; helps me to identify that even C++ has sort of a high-level and low-level. I'm not sure if it's part of the OP's question, but maybe you could identify some types of problems that you would consider to be "low-level", and would demand fixed buffers, pointer access, etc.? – Katana314 Dec 5 '13 at 18:29
  • 1
    some binary network protocols map naturally onto fixed-size structures, as do things like on-disk filesystem structures and some file formats. All of those also suit overlaying the struct onto buffers, to avoid redundant copying. – Useless Dec 6 '13 at 14:07
10

Just because C++ programmers tend to delve into the lower layers of abstraction more often doesn't mean we always live there. When faced with a high-level problem, we use high-level abstractions.

Your example problem would likely be solved with a vector at first, refactored into an EmployeeList class backed by a vector as complexity grew, then refactored to be backed by a database as the size grew. C++ programmers aren't prescient about how their application will grow or not. We're just as bound by design principles like YAGNI.

If there wasn't a standard library vector, a good C++ programmer would write his own, and a bad programmer would suffer with the limitations that were handed him. People who fail to use abstraction appropriately are bad programmers in any language.

  • People who fail to use abstraction appropriately are bad programmers in any language. --- I've also found it exceedingly difficult to teach other developers how to identify abstractions, too. Dealing with abstractions, in my experience, is where you draw the line between "coders" and engineers --- a line that seems like a chasm many people aren't able to cross. – Greg Burghardt Nov 7 '18 at 13:49
  • 1
    @GregBurghardt As an Electrical Engineer (with a bit of paper that says so and everything - so I can be a bit pedantic about the label of engineer) I might dispute your classification naming. I deal with PLC programming in industrial controls which typically attracts mechanical and electrical engineers, and I regularly see the same block of code copy/pasted multiple times in a row (with only variable name changes) rather than defining a function once and calling it many times. I try not cry at work when I see it. – Peter M Nov 7 '18 at 14:03
  • @PeterM: I guess I should quality "engineer" in my comment - Software Engineer (someone who approaches writing code with the same discipline, as say, an Electrical Engineer would when designing circuitry) and not just someone who barfs code into a text editor and says "It works! I'm done." – Greg Burghardt Nov 7 '18 at 14:40
0

I thought some marketing guy posted something for that famous guy who wrote that thinking series books; 'Thinking in C++', 'thinking in Java','thinking in python'... Btw no offense, I loved his C++ volumes, volume 2 was like a part of my side table when I was in late 20s.

Based on my experience, I believe it is all about the industry and domain, if it is embedded system market, or you are working on extremely low latency systems, then by default, no matter if you are a hidden gem in performing abstractions, you'll start thinking about saving memory by using a union instead of a structure and then even declaring variables based on byte alignment and padding. that isn't any gift, just a practice that develops over a period of time.

For solving typical application development problems, it may depend upon whether you are a poor generation-x guy like me, who automatically start thinking like boomers while working with them, in order to impress (or may be out of respect??), pretending my quad-core i7 as a commodor-64 at best and uselessly try saving few bits to show them. But then after that achievement, when I try going out for a coffee/smoke break with millenials, I typically pretend old C is dead, who cares about memory anymore and hey please I'm not a dinosaur, people who write this sort of code retired long before I graduated. A never ending dilemma it seems till I leave coding.

  • I work with at least one guy who might count as a boomer and has distant memories of punched card programming. He's happy using modern C++! It is true though that my default level of abstraction is influenced by the codebase I'm working on, and who I have to share it with. There's no more benefit in using metaprogramming on a codebase where no-one else will understand it, than there is in low-level bit-twiddling where that is going to be confusing. – Useless Nov 7 '18 at 20:15

protected by gnat Nov 7 '18 at 21:21

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.