CS 70

A Multifile Program

A First Attempt

Here we've attempted to break the cowchat program into two files: cowchat.cpp and printmoos.cpp.

printmoos.cpp:

#include <iostream> // for std::cin, std::cout

// Prints the given number of "moos"
void printMoos(int numMoos) {
    std::cout << "Moo";
    for (int i = 1; i < numMoos; ++i) {
        std::cout << " moo";
    }
    std::cout << ".";
}

cowchat.cpp:

#include <iostream> // for std::cin, std::cout
#include <string>   // for std::string

int main() {
    std::cout << "Chat with a cow!";
    std::string userIn;
    do {
        std::cout << std::endl  // New line
                  << "> ";      // The prompt
        std::getline(std::cin, userIn);
        int nMoos = 1 + userIn.size() % 2;
                     // ^-- Either 1 or 2 moos;
        printMoos(nMoos);
    } while (std::cin.good() && userIn != "");
    std::cout << "\n\nBye!\n";

    return 0;
}

Give the minimal command to compile printmoos.cpp into an object file named printmoos.o.

Give the minimal command to compile cowchat.cpp into an object file named cowchat.o.

The Problem

What do you think is going to happen if we run these commands?

When I try it, printmoos.cpp compiles just fine, but I when I try to compile cowchat.cpp, I get

cowchat.cpp:13:9: error: use of undeclared identifier 'printMoos'
        printMoos(nMoos);
        ^
1 error generated.

The Problem: printmoos Not Declared!

The problem is that we call the function printmoos() in cowchat.cpp, but the compiler has no idea what printmoos() is. The compiler doesn't dig around in any of our other files, so it doesn't know such a function exists.

  • Horse speaking

    So from the compiler's perspective, it's just like a made-up nonsense word. It could be feebleglorp, as far as the compiler is concerned.

  • Alien speaking

    You have contacted Feebleglorp. Why do you summon me?

  • Horse speaking

    Hay, there! No, no. That was just an example. Sorry!

  • Duck speaking

    Why does the compiler need to know ahead of time?

The compiler checks things as it goes through the code it's provided. It needs to know

  • That printmoos is a genuine function.
  • What parameters it takes (both how many and what types they are).
  • What type of result it returns.

The solution is to declare the function (saying it exists) without defining it (giving all the details of how it works, which we already did in printmoos.cpp).

Fixing the Problem

cowchat.cpp (fixed!):

#include <iostream> // for std::cin, std::cout
#include <string>   // for std::string

// This line declares the printMoos function.
// It ends in a ; instead of { } because the function definition is elsewhere.
void printMoos(int numMoos);

int main() {
    std::cout << "Chat with a cow!";
    std::string userIn;
    do {
        std::cout << std::endl  // New line
                  << "> ";      // The prompt
        std::getline(std::cin, userIn);
        int nMoos = 1 + userIn.size() % 2;
                     // ^-- Either 1 or 2 moos;
        printMoos(nMoos);
    } while (std::cin.good() && userIn != "");
    std::cout << "\n\nBye!\n";

    return 0;
}

The new line (highlighted) is called a forward declaration. It tells the compiler that somewhere in the code there is a function with this name that takes these parameters and has this return type.

  • LHS Cow speaking

    That's enough information to check whether the code in this file has consistent types and correct names.

  • RHS Cow speaking

    Now we can compile cowchat.cpp into cowchat.o. Of course cowchat.o is kind of incomplete. It refers to a function that it doesn't define.

  • LHS Cow speaking

    In order to have a complete, self-contained executable file, we need to combine both object files (cowchat.o and printmoos.o) together!

The process of combining the files is called linking.

It's not really a form of compilation (since we aren't really converting anything). Instead, the linker just fills in gaps where pieces of code refer to things that are defined elsewhere.

Altogether, the commands needed to compile both of these files into a single executable are

  • clang++ -c printmoos.cpp, which compiles printmoos.cpp into printmoos.o.
  • clang++ -c cowchat.cpp, which compiles cowchat.cpp into cowchat.o.
  • clang++ -o cowchat cowchat.o printmoos.o, which links the object files into the executable file cowchat.

(When logged in, completion status appears here.)