Skip to content
Katie Dektar edited this page Dec 11, 2020 · 7 revisions

C++ Utils

Libraries for student C++ code.

Graphics

C/C++ CI

graphics/image.h can be used to create, view, load and save images.

You can try using this library with this CS50 lab.

Usage

image.h provides a Color class and an Image class. Images can be loaded from file or created. Users may access and set per-pixel color data. Image can be modified by manipulating them at the pixel level, as well as by drawing lines, circles, rectangles and text. Images can be displayed and saved as bitmaps (an uncompressed format).

image_event.h provides a MouseEvent and a MouseEventListener interface. Extend the MouseEventListener interface and implement OnMouseEvent. To begin receiving mouse events, use Image::AddMouseEventListener to register for mouse events, and enter the event loop with Image::ShowUntilClosed. Note you will need to use Image::Flush (and cout::flush) to ensure updates are noted after calling Image::ShowUntilClosed.

Development

image.cc is backed by CImg, an open-source C++ image processing library. To update CImg.h to the latest, run make update_cimg in graphics/.

It would be possible to back image.cc with some other implementation, for example Skia or OpenGL. CImg was chosen because it is relatively light-weight, as all the implementation is contained within the CImg.h header.

Karel the Robot

Karel lives in a two-dimensional grid where (1, 1) is at the bottom left corner. Karel has a position and an orientation in the grid (north, east, south or west).

Each square of the grid may contain one or more beepers, or no beepers at all. Karel has a bag of beepers which may be empty or may contain many or even infinite beepers.

Karel can move one cell and turn left. Karel can put down a beeper or pick up a beeper. In addition, Karel is able to check state of the area around themselves.

Cells may be separated by walls. Karel cannot move through walls or off the edge of the world.

Karel has four actions:

void Move();
void TurnLeft();
void PutBeeper();
void PickBeeper();

Karel has several booleans for getting robot state:

bool FrontIsClear();
bool FrontIsBlocked();
bool LeftIsClear();
bool LeftIsBlocked();
bool RightIsClear();
bool RightIsBlocked();
bool HasBeepersInBag();
bool NoBeepersInBag();
bool BeepersPresent();
bool NoBeepersPresent();
bool FacingNorth();
bool NotFacingNorth();
bool FacingEast();
bool NotFacingEast();
bool FacingSouth();
bool NotFacingSouth();
bool FacingWest();
bool NotFacingWest();

robot.h provides a Karel the Robot implementation in C++. This is functional programming: simply #include "cpputils/karel/karel.h" and call the Karel functions in the global namespace.

Example program

#include "cpputils/karel/karel.h"

void TurnRight() {
  TurnLeft();
  TurnLeft();
  TurnLeft();
}

void KarelProgram() {
  while(FrontIsClear()) {
    Move();
  }
  TurnRight();
  Move();
  TurnLeft();
  Move();
  PickBeeper();
  TurnLeft();
  TurnLeft();
  while(FrontIsClear()) {
    Move();
  }
  TurnRight();
  Move();
  TurnRight();
}

int main() {
  LoadWorld("worlds/CollectNewspaperKarel.w");
  KarelProgram();
  Finish();
  return 0;
}

Building

Compile (from the directory containing cpputils) with:

clang++ -std=c++17 test.cc cpputils/karel/karel.cc cpputils/karel/src/robot.cc cpputils/graphics/image.cc -o main -lm -lX11 -lpthread

A few more flags are needed in Mac.

For instructors

Instructors should call LoadWorld before student code and Finish after.

void LoadWorld(std::string filename);
void Finish();

World files

Same as Stanford 106A. Dimension must be the first line.

CollectNewspaperKarel.w:

Dimension: (7, 5)
Wall: (3, 2) west
Wall: (3, 2) south
Wall: (3, 3) west
Wall: (3, 4) west
Wall: (3, 5) south
Wall: (4, 2) south
Wall: (4, 5) south
Wall: (5, 2) south
Wall: (5, 5) south
Wall: (6, 2) west
Wall: (6, 4) west
Beeper: (6, 3) 1
Karel: (3, 4) east
Speed: 1.00

Unit testing

See src/test/karel_unittest.cc

Must get robot instance with enable animations set to false -- so unittest shouldn't run over code that does Setup -- must set up at the beginning of each test instead.

TODO(katie)

cpputils usage (for lab and project creators):

As a subtree:

If you don't expect your lab or project to need to back-port changes into cpputils, it may be easier to check out cpputils as a subtree.

From the root directory of your lab, you can run:

git subtree add --prefix problem/cpputils https://github.com/ILXL/cpputils master --squash

This creates a copy of the cpputils repository's master branch as a single commit in your git history. If you want to update it, run:

git subtree pull --prefix problem/cpputils https://github.com/ILXL/cpputils master --squash

As a submodule:

To use this as a submodule in another lab or project, run:

git submodule add -b v2 https://github.com/ILXL/cpputils

This will create a .gitmodules file which you can add and commit, pointed at a particular branch (in this case v2).

Then make sure everyone who checks out the repo gets the submodule, for example adding this command to your project Makefile to check if image.h is available:

cpputils/graphics/image.h:
	@git submodule update --init --recursive

This checks out cpputils at a particular commit, and everyone cloning your git repository will get that exact commit. This is useful for versioning: it ensures everyone using that git repository will have the same version of cpputils until it is explicitly updated.

So, if you want to update the commit which cpputils is pointed at in an existing repo, you can pull from within the cpputils directory and then cd back out to add and commit the change. For example, this snippet pulls the latest from the v2 branch:

cd cpputils
git checkout v2 && git pull
cd ..
git add cpputils/ && git commit -m "Updating cpputils"

Release process (for cpputils library contributors):

Branches

Users of cpputils will check out a particular branch as a submodule within their repository. We have created branches for each version to make it easy for submodules to get a known stable cpputils.

  • Development of cpputils should be merged to the master branch.
  • Significant changes, once ready for usage, should be forked to new version branches (v1, v2, etc).
  • Changes should not be merged back to earlier version branches, except bugfixes.

Testing

Utility classes in cpputils should be tested to ensure changes do not cause any surprising breakages. You can set up github continuous integration testing using Github Actions.

You can add new tests by editing .github/workflows/c-cpp.yml. For example, the graphics class is tested with graphcis/image_unittest.cc. c-cpp.yml has a workflow to make image_unittest which ensures all dependencies are installed (like gtest) and then builds and runs image_unittest.cc.

You can see the result of each test pass in the actions tab on GitHub.

Note: Github Actions is free for public repositories, but there are a limited number of minutes for private repos! cpputils is public so it has unlimited actions minutes.

Clone this wiki locally