For Developers‎ > ‎

C++ in Chromium 101 - Codelab

This tutorial will guide you through the creation of various example C++ applications, highlighting important Chromium C++ concepts. This tutorial assumes robust knowledge of C++ (the language) but does not assume you know how to write an application specific to Chromium's style and architecture. This tutorial does assume that you know how to check files out of Chromium's repository.

As always, consider the following resources as of primary importance:

This tutorial does not assume you have read any of the above, though you should feel free to peruse them when necessary. This tutorial will cover information across all of those guides.

Exercise 0: "Hello World!"

This exercise demonstrates the use of the ninja build system to build a simple C++ binary and demonstrates how typical C++ builds are organized within Chromium.

Create a new target in base.gyp for a new executable named hello_world. Then write the classic "Hello World!" program in C++. You should be able to build it with ninja -C out/Debug hello_world and execute it directly by finding the binary within out/Debug.

Sample execution:

$ cd /path/to/chromium/src
$ gclient runhooks
$ ninja -C out/Debug hello_world
$ out/Debug/hello_world
Hello World!
AMI: might be confusing how rietveld shows the new file as a copy/diff against an unrelated file.
AMI: output doesn't actually match what's in the solution.  Might be confusing.

Part 1: Using command-line arguments

We will augment our hello_world binary to parse command-line flags and use those values to print messages to the user.

Command-line arguments within Chromium are processed by the CommandLine::Init() function, which takes command line flags from the argc and argv (argument count & vector) variables of the main() method. A typical invocation of CommandLine::Init() looks like the following:

int main(int argc, char** argv) {
  CommandLine::Init(argc, argv);
  // Main program execution ...
  return 0;
}

Flags are not explicitly defined in Chromium. Instead, we use GetSwitchValueAsASCII() and friends to retrieve values passed in.

Important include files

Exercise 1: Using command-line arguments

Change hello_world to take a --greeting and a --name switch. The greeting, if not specified, should default to "Hello", and the name, if not specified, should default to "World".

Exercise solution

Part 2: Callbacks and Bind

C++, unlike other languages such as Python, Javascript, or Lisp, has only rudimentary support for callbacks and no support for partial application. However, Chromium has the base::Callback<Sig> class, whose instances can be freely passed around, returned, and generally be treated as first-class values.

AMI: last sentence might benefit from a pointer to refcounting (without which it might be less clear how binding works with copying of bound closures).

The Sig template parameter is a function signature type:

// The type of a callback that takes no arguments and does not return anything.
// base::Closure is a typedef of this type.
base::Callback<void(void)>


// The type of a callback that takes two arguments (a string and a double)
// and returns an int.
base::Callback<int(std::string, double)>

Callbacks are executed by invoking the Run() member function:

void my_function(const base::Callback<int(std::string, double)>& my_callback) {
  ...
  int result1 = my_callback.Run("my string 1", 1.0);
  // Run() can be called as many times as you wish.
  int result2 = my_callback.Run("my string 2", 2);
  ...
}

Callbacks are constructed using the base::Bind() function, which handles partial application:

// Declare a function.
void my_function(int32 a, double b);

base::Callback<void(double)> my_callback = base::Bind(&my_function, 10);


// Equivalent to:
//
// my_function(10, 3.5);
//
my_callback.Run(3.5);

base::Bind() can do a lot more, including binding class member functions and binding additional arguments to an existing base::Callback. See the comments in base/callback.h and base/bind.h for details.

Important Include Files

#include "base/bind.h"
#include "base/callback.h"

Exercise 2: Fibonacci closures

Implement a function that returns a callback that takes no arguments and returns successive Fibonacci numbers. That is, a function that can be used like this:

base::Callback<int()> fibonacci_closure = MakeFibonacciClosure();
printf("%d\n", fibonacci_closure.Run()); // Prints "1"
printf("%d\n", fibonacci_closure.Run()); // Prints "1"
printf("%d\n", fibonacci_closure.Run()); // Prints "2"
...

Each returned Fibonacci callback should be independent; running one callback shouldn't affect the result of running another callback. Write a fibonacci executable that takes an integer argument n and uses your function to print out the first n Fibonacci numbers.

(This exercise was inspired by this Go exercise: Function closures.)

Exercise solution

Part 3: Message loops

Chromium's abstraction for event loops is base::MessageLoop. base::MessageLoop handles running tasks (which are instances of base::Closure) on the current thread. Given a pointer to the message loop for a thread, you can post tasks on it with base::MessageLoop::PostTask (or base::MessageLoop::PostDelayedTask if you want to add a delay).

Normally you wouldn't have to worry about setting up a base::MessageLoop and keeping it running, since that is automatically done by Chromium's thread classes. However, since the main thread doesn't automatically start off with a base::MessageLoop, you have to create, pump, and shutdown one yourself if you're writing a new executable. The base::RunLoop class is the current recommended way of doing that.

Important header files

Exercise 3: Sleep

Implement the Unix command-line utility sleep using only a base::MessageLoop (i.e., without using the sleep function or base::PlatformThread::Sleep).

Exercise solution

Part 4: Threads and task runners

Chromium's platform-independent abstraction for threads is base::Thread. Once you call base::Thread::Start() (and it succeeds), you can post tasks on its message loop.

Chromium's abstraction for asynchronously running posted tasks is base::TaskRunner. You can get a base::TaskRunner for various things, including threads, message loops, and thread pools. base::TaskRunner allows you to write code that posts tasks without depending on what exactly will run those tasks.

An important member function of base::TaskRunner is base::TaskRunner::PostTaskAndReply. PostTaskAndReply lets you post a task to a base::TaskRunner which will post a task back to your current thread when its done. This sounds simple, but it's quite tricky to do this in a correct, thread-safe manner!

Important header files

More information

Exercise 4: Integer factorization

Take the given (slow) function to find a non-trivial factor of a given integer:

bool FindNonTrivialFactor(int n, int* factor) {
  // Really naive algorithm.
  for (int i = 2; i < n; ++i) {
    
if (n % i == 0) {
      
*factor = i;
      return true;
    }
  }
  return false;
}

Write a command-line utility factor that takes a number, spawns a thread to factor it in the background using FindNonTrivialFactor, and prints a status update on the main thread every second while the worker thread is busy.

Exercise solution

Comments