CS 70

Style and Elegance

  • LHS Cow speaking

    When you write code, readability should be your top priority!

  • RHS Cow speaking

    To the extent that you can do it without sacrificing readability, you should maximize maintainability and extensibility.

  • LHS Cow speaking

    And remember: Nobody writes beautiful code all the time!

  • RHS Cow speaking

    So it's important to go back and fix things.

Consistency

Cat amongst a group of meerkats, meme text reading: One of these things is not what the other…

Use (In)Consistency to Your Advantage

  • Similar things should look similar.

  • Different things should look different.

But (In)Consistent With What?

  • With ourselves (within a file, and across files in a single program).
  • With the culture of the company, organization, or team that we're developing software with.

In CS 70, that means that your code should be internally consistent, and that it should be consistent with the CS 70 course style guide. Generally speaking, we follow Google's Style Guide, and you'll be able to use a tool called cpplint that checks Google style on homework assignments to catch some style issues.

  • Duck speaking

    Can you tell us a little more about the style expectations in CS 70?

  • LHS Cow speaking

    I'm so glad you asked!

Consistency: Variable Names

All variable names and function names should be written in “camelCase”. Start the first word with a lower-case letter, and subsequent words with a capital letter, with no underlines between them. Valid names include count, i, activeTask, and buildBarn()

When we learn about classes in C++, a special type of variable called a data member will also be named in camelCase, but with a trailing underscore. Possible data member names include front_ and currentCapacity_.

Class names will always have a capitalized first letter, and the rest of the name will be in camel case. We could name a class Gene or StudentTranscript.

Constant values will be named in all caps, with an underscore between words. We might see constants named VERSIONor MAX_STUDENTS.

  • Cat speaking

    So that means that we can tell just by looking at the name whether something is a regular variable, a data member, a class, or a constant!

  • LHS Cow speaking

    Yes! Similar things look similar, and different things look different!

Consistency: Applying Idioms

The primary audience of the code we write is other humans. We want other coders to be able to focus on the interesting parts of our code that solve the problems we're interested in.

That means that as much as possible, we should follow the accepted conventions of the coding community we're part of. An experienced C++ programmer shouldn't have to spend mental energy understanding the structure of our for loop, so long as we use the patterns that are expected by people who look at a lot of C++ code.

In C++, for loop index variables should start at 0 and go up to (but not including) the iteration count. variables should almost always be incremented using the syntax ++i instead of the syntax i++

constexpr size_t NUM_LETTERS = 26;
string alphabet;
for (size_t i = 0; i < NUM_LETTERS; ++i) {
    alphabet += ('a' + i);
}
  • LHS Cow speaking

    Incrementing this way is called pre-incrementing.

  • Cat speaking

    But wait… I learned some C once, and they used i++.

  • LHS Cow speaking

    That's right! In C, post-incrementing is more idiomatic for reasons lost in the ancient history of the language. In a for loop like this one, they do almost exactly the same thing (add one to the value of i). Let's look at the differnce. If we had this code and ran it

    int i = 41;
    std::cout << i++;
    

    it would print

    41
    

    even though afterwards i would have the value 42. Because i++ means “stash the old value of i somewhere, increment i, and then give me the old value that you stashed away”.

    In contrast, if we'd said:

    int i = 41;
    std::cout << ++i;
    

    it would print

    42
    

    So ++i just means “increment i and give me its value”.

  • Hedgehog speaking

    The first one is weird, the second one actually makes sense.

  • LHS Cow speaking

    Exactly! We could live happy productive lives and never want that weird old-value stashing behavior.

  • Duck speaking

    So why do people use i++

  • LHS Cow speaking

    I wish I knew! It's like a cargo cult where everyone copies what everyone else does. When you throw away the value rather than printing it and the thing you're incrementing is an int, it doesn't do any harm to use i++, but it's still a bit pretty odd to ask a language to go to all the trouble of remembering the old value just to throw it away, unused!

  • Duck speaking

    So if they're basically the same for most places we will use them in CS 70, why does it matter which one we use?

  • LHS Cow speaking

    Because other C++ programmers expect to see pre-incrementing. If you post-increment a variable, they'll notice. And then they'll spend time wondering why you didn't pre-increment, when they could be appreciating the beauty of the rest of your code!

  • RHS Cow speaking

    Ooh, tell them about the C++ name!

  • LHS Cow speaking

    The name “C++” is kind of a joke. It's like an increment beyond C, but you still get all the old value that C had.

Consistency: Whitespace

Unlike Python, C++ is whitespace insensitive—it doesn't care how far you indent your lines, or whether you put your curly braces on the end of one line or the start of the next line. But using consistent whitespace and bracketing placement can make it much easier for others to understand the structure of your code, and can lead to fewer logic errors. Some highlights of the style guide we follow in CS 70 include:

  • No lines longer than 80 characters: Longer code lines can wrap in weird ways, making it harder to follow your code.
  • Inside blocks (functions, conditionals, loops, etc.) use a consistent amount of space. (VS Code's default is good!)
  • Opening curly braces go on the end of the line before the block they enclose. For functions, curly braces go with the return type, name, and parameters of the function. For loops and if statements, curly braces go with the for or if.
  • Closing curly braces go on their own line, indented to match the start of the block they close.

More details can be found on Google's C++ Style Guide. Don't worry about the parts that you don't understand yet; we'll learn (more) features of the language as the semester goes on!

Exceptions

  • Google's style guide recommends a two-space indent, perhaps because a lot of code at Google is deeply nested. In CS 70, we don't encourage deeply nested code so we use a four-space indent (our style checker, cpplint allows both).
  • Google's style guide allows very short functions to be written all on a single line (in violation of the rule above). We don't allow that.

Comprehension Check

Variable Name Formats

How should we format the name of a regular variable that keeps track of the number of cows in a barn?

The CS 70 Hall of Shame

  • LHS Cow speaking

    We've scoured the Internet (so you don't have to!)

  • RHS Cow speaking

    We found the worst of the worst.

  • LHS Cow speaking

    The ugliest of the ugly.

  • RHS Cow speaking

    The grossest of the gross.

  • LHS Cow speaking

    Presenting… Your candidates for the CS 70 Coding Style Hall of Shame!

  • RHS Cow speaking

    Your mission, should you choose to accept it, is to review the candidates below. Then pick one to nominate based on its terrible coding style.

Candidate A: A Capital Idea

std::string capitalizedName(std::string name)
{
    if (name == "aafke") {
       return "Aafke";
    } else if (name == "aaron") {
       return "Aaron";
    }
        // lots and lots of similar lines, not shown
    } else if (name == "zuzana") {
       return "Zuzana";
    } else if (name == "zuzanna") {
       return "Zuzanna";
    } else if (name == "zuzanny") {
       return "Zuzanny";
    } else {
       // Name not in the database yet,
       // but lowercase is better than nothing
       return name;
    }
}

Candidate B: Seems Valid to Me

bool validateSSN(std::string ssn) {
  if ( ssn[0] != '0' && ssn[0] != '1' && ssn[0] != '2' && ssn[0] != '3' && ssn[0] != '4' && ssn[0] != '5' && ssn[0] != '6' && ssn[0] != '7' && ssn[0] != '8' && ssn[0] != '9' ) {
    return false;
  }
  if ( ssn[1] != '0' && ssn[1] != '1' && ssn[1] != '2' && ssn[1] != '3' && ssn[1] != '4' && ssn[1] != '5' && ssn[1] != '6' && ssn[1] != '7' && ssn[1] != '8' && ssn[1] != '9' ) {
    return false;
  }
  // plus 7 more similar cases ...
  return true;
}

Candidate C: Twenty(-ish) Spaces

// Yes, this is the whole example. :)
const std::string twentySpaces = "                   ";

Candidate D: Spudfisser???

void SPdfsR(Graph G, int s)
  { link u; int i, t; double wt;
    int **p = G->path; double **d = G->dist;
    for (u = G->adj[s]; u != NULL; u = u->next)
      {
        t = u->v; wt = u->wt;
        if (d[s][t] > wt)
          { d[s][t] = wt; p[s][t] = t; }
        if (d[t][t] == maxWT) SPdfsR(G, t);
        for (i = 0; i < G->V; i++)
          if (d[t][i] < maxWT)
            if (d[s][i] > wt+d[t][i])
              { d[s][i] = wt+d[t][i]; p[s][i] = t; }
      }
  }

Candidate E: surelyWonderfulJavaFunctionWhichHasBeenLabeledAsSuch

// This is Java code to critique, not C++ syntax!

synchronized (surelyReachableObjectsWhichHaveToBeMarkedAsSuch) {
    waitRecommended =
      surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContentedSize
        == surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContented.size();

    surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContentedSize =
        surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContented.size();

    while (!surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContented.isEmpty())
        {
          surelyReachableObjectsWhichHaveToBeMarkedAsSuch.push(
            surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContented.getFirst() );
        }
}

Candidate F: is03or09or10orAwesome

/**
 * function is03or09or10.
 * takes: prodCode
 * returns: bool
 */
bool is03or09or10(std::string prodCode)
{
    if ("03" == prodCode) return true;
    else if ("09" == prodCode) return true;
    else if ("10" == prodCode) return true;
    else return false;
}

// other functions not shown: is01, is02, and is004or005

Candidate G: I Feel Validated

void validate(Person p)
{
    const int TWO = 2;
    bool validated = true;
    std::string myMessage, val;

    // Name
    val = p.name;
    if (!(val.find(" ")!=std::string::npos)) || !(val.size()>TWO)) {
       myMessage += "Please fill in your full Name\n";
       validated = false;
    }

    // Address
    val = p.address;
    if (!(val.size()>TWO)) {
       myMessage += "Please fill in your full Address\n";

Candidate H: Alphabet Soup

bool b(std::string a) {
    int b = -1, c = a.length();
    goto p;
    while(b<c){
        if  (a   [b] != a   [c])  return false;
    p:  ++b; c--; }
                                  return true;
}

Candidate I: Go With the (Control) Flow

// In this example, we've omitted everything except the control flow,
// so that you can get a sense of how that aspect of the code is organized.
public boolean foo(... omitted ...) {
    try {
        synchronized (... omitted ...) {
            if (... omitted ...) {
                (omitted)
            } else {
                (omitted)
            }
            for (... omitted ...) {
                if (... omitted ...) {
                    if (... omitted ...) {
                        if (... omitted ...) {
                            if (... omitted ...) {
                                if (... omitted ...) {
                                    for (... omitted ...) {
                                        (... omitted ...)
                                    }
                                }
                            }
                        } else {
                            if (... omitted ...) {
                                for (... omitted ...) {
                                    if (... omitted ...) {
                                        (... omitted ...)
                                    } else {
                                        (... omitted ...)
                                    }
                                    if (... omitted ...) {
                                        (... omitted ...)
                                    } else {
                                        if (... omitted ...) {
                                            (... omitted ...)
                                        }
                                    }
                                    if (... omitted ...) {
    // ... etc. (we've seen enough!)

Welcome to the Hall of Shame

Which candidate would you like to nominate for the 2023 CS 70 Hall of Shame?

  • LHS Cow speaking

    Okay, but what was it that made it the worst?

Write a brief note to the nominating committee explaining your choice for this award. Your note will be displayed, anonymously, after you submit it.

(When logged in, completion status appears here.)