Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Lab 1 is (hopefully) an easy way to get familiar with some
- of the tools used in this class; the autograder, makefile, and get_opt.
- get_opt is a function in the C libraries that processes options
- input on the command line at runtime. For example, a program
- might be compiled and run like so:
- g++ main.cpp --std=c++17 -o main
- ./main
- There was an argument provided to the g++ compiler: '--std=c++17'.
- The program recognizes the '--' string and identifies
- it as an argument. Get_opt will essentially do this for you,
- all you need to do is make sure it knows what look for, and what to
- do if it finds something. An example of how you might run a project
- in EECS 281 is as follows (using the makefile that we provide):
- make
- ./lab1 -p 2 < test.txt
- Other examples of valid command lines (the first uses the long version
- of print, the second specifies the -n or --name also):
- ./lab1 --print 2 < test.txt
- ./lab1 -p 2 -n < test.txt
- This brings the EECS 281 makefile to attention. There are instructions
- in the makefile and we will go over it in class, but there are a few places
- that you need to edit with your program information. The makefile will
- save you a lot of time when compiling your programs and submitting to the
- autograder. In fact, it is required that you include a makefile with your
- autograder submissions.
- */
- #include <vector>
- #include <iostream>
- #include <algorithm> // std::sort
- #include <getopt.h>
- #include "sorting.h"
- // ----------------------------------------------------------------------------
- // MusicLibrary Declarations
- // ----------------------------------------------------------------------------
- class MusicLibrary {
- public:
- // Read in the CSV music file through stdin.
- void read_data();
- // Read and process command line arguments.
- void get_options(int argc, char** argv);
- // Sort and print the data.
- void run();
- private:
- // Holds all of the song objects for the program.
- std::vector<Song> music;
- // The number of songs to print after sorting.
- int num_print = 2;
- // Signifies what we attribute we are sorting by.
- char policy = '\0';
- };
- // ----------------------------------------------------------------------------
- // Driver
- // ----------------------------------------------------------------------------
- int main(int argc, char** argv) {
- try {
- // Instantiate a music library.
- MusicLibrary library;
- // Read and process the command line options.
- library.get_options(argc, argv);
- // Read in the provided file through stdin.
- library.read_data();
- // Sort the data with the provided policy and print
- // the first 'num_print' songs.
- library.run();
- }
- // Catch runtime_errors, print the message, and exit the
- // program with a non-zero status.
- catch (std::runtime_error& e) {
- std::cerr << e.what() << std::endl;
- return 1;
- }
- // All done!
- return 0;
- }
- // ----------------------------------------------------------------------------
- // MusicLibrary Definitions
- // ----------------------------------------------------------------------------
- /*
- This program can be run with five different command line options:
- [--print | -p] <num_print>
- Determines how many songs to print.
- [--name | -n]
- Sorts by song title
- [--artist | -a]
- Sorts by song artist
- [--listens | -l]
- Sorts by number of listens that a song has.
- [--help | -h]
- Displays a helpful message! We wrote one for you,
- so feel free to take a look for additional clarification.
- These are the options you will be incorporating into get_options!
- The program could be run like so:
- ./lab1 --print 3 --artist
- Equivallentaly,
- ./lab1 -p 3 -a
- */
- // Read and process command line options.
- void MusicLibrary::get_options(int argc, char** argv) {
- int option_index = 0, option = 0;
- // Don't display getopt error messages about options
- opterr = false;
- // use getopt to find command line options
- struct option longOpts[] = { { "print", required_argument, nullptr, 'p' },
- { "help", no_argument, nullptr, 'h' },
- { "listens", no_argument, nullptr, "l" },
- { "name", no_argument, nullptr, "n" },
- { "artist", no_argument, nullptr, "a" },
- { nullptr, 0, nullptr, '\0' }
- };
- /*
- */
- while ((option = getopt_long(argc, argv, "p:hlna", longOpts, &option_index)) != -1) {
- switch (option) {
- case 'p':
- num_print = std::atoi(optarg);
- break;
- case 'n':
- policy = 'n';
- break;
- case 'l':
- policy = 'l';
- break;
- case 'a':
- policy = 'a';
- break;
- case 'h':
- std::cout << "This program reads a CSV file that contains song names,\n"
- << "the artist who wrote them, and the number of plays each song\n"
- << "has on Spotify. It then outputs the number of songs specified\n"
- << "in the command line arguments (the print option), which\n"
- << "defaults to 2, sorted by the option specified (one of name,\n"
- << "artist, or listens).\n"
- << "Usage: \'./lab1\n\t[--listens | -l]\n"
- << "\t[--name | -n]\n"
- << "\t[--artist | -a]\n"
- << "\t[--print | -p] <# of songs to print>\n"
- << "\t[--help | -h]\n"
- << "\t< <CSV Music File>\'" << std::endl;
- exit(0);
- }
- }
- // After all the options have been processed,
- // check to make sure a sorting policy has been selected.
- // If one has not been selected, we will default to
- // sorting by song title (name).
- if (!policy)
- policy = 'n';
- // If num_print is still 0, then we are just
- // wasting time sorting, so throw an error.
- if (!num_print)
- throw std::runtime_error("No print argument was provided!"
- " This program will have no tangible results. Exiting...");
- }
- /*
- An important concept in efficiency is portrayed in the next function,
- but it can be easy to miss. std::vector::reserve(size_t new_capacity)
- is a function that reserves the EXACT amount of memory necessary to
- fit 'new_capacity' number of elements. We will go over exactly why
- this is helpful in the first few weeks of class; for now just know
- that it is limiting wasted memory in our program, as well as cutting
- down on a non-trivial amount of time that would normally be lost
- growing the vector. This is not important with a sample file with
- six songs, but there are autograder tests with thousands of songs
- that it will make a difference in.
- std::vector::reserve allocates the memory and increases the CAPACITY
- of the array, but not the SIZE of the array. This means those objects
- don't actually exist yet, and a call to std::vector::size will portray that.
- To increase the size of the array and insert objects then, we will still
- use std::vector::push_back. If one was to use std::vector::resize(size_t new_size)
- instead, they would be increasing both the size as well as the capacity of
- the array, and there would be 'new_size' many default constructed elements
- in the array if the array was initially empty.
- */
- // Read data into the program through stdin.
- void MusicLibrary::read_data() {
- Song song;
- std::string plays;
- int num_songs = 0;
- // Read in the first number to find the number
- // of songs in the file.
- // Also consume the new line after the integer.
- std::cin >> num_songs >> std::ws;
- // Reserve the exact amount of memory needed
- // to fit all the songs.
- music.reserve(num_songs);
- // Read to the end of the CSV file.
- while (std::getline(std::cin, song.name, ',')) {
- // Skip comments.
- if (song.name[0] == '#') {
- size_t pos = 0;
- // If there is a newline, we have consumed
- // part of a song, so adjust the string.
- if ((pos = song.name.find_last_of('\n')) != std::string::npos)
- // Need a +1 here to move past the \n
- song.name = song.name.substr(pos + 1);
- // Otherwise just grab the rest of the line.
- else {
- std::getline(std::cin, song.name);
- continue;
- }
- }
- // Get the rest of the line.
- std::getline(std::cin, song.artist, ',');
- std::cin >> song.plays >> std::ws;
- // Put the song into the music list.
- music.push_back(song);
- }
- // If we didn't read in any data, throw an error.
- if (!music.size())
- throw std::runtime_error("No data was read in! Refer to the help option to see program usage.");
- }
- // Sort and print the data.
- void MusicLibrary::run() {
- // Determine our sorting policy and sort.
- if (policy == 'n') {
- std::sort(music.begin(), music.end(), Song::NameSort());
- }
- else if (policy == 'a') {
- std::sort(music.begin(), music.end(), Song::ArtistSort());
- }
- else if (policy == 'l') {
- std::sort(music.begin(), music.end(), Song::ListensSort());
- }
- // Print out the first num_print songs with the
- // overloaded stream insertion operator.
- for (int i = 0; i < num_print; ++i)
- std::cout << music[i] << std::endl;
- }
- ------
- ## EECS 281 Advanced Makefile
- # How to use this Makefile...
- ###################
- ###################
- ## ##
- ## $ make help ##
- ## ##
- ###################
- ###################
- # IMPORTANT NOTES:
- # 1. Set EXECUTABLE to the command name given in the project specification.
- # 2. To enable automatic creation of unit test rules, your program logic
- # (where main() is) should be in a file named project*.cpp or specified
- # in the PROJECTFILE variable.
- # 3. Files you want to include in your final submission cannot match the
- # test*.cpp pattern.
- # enables c++17 on CAEN
- PATH := /usr/um/gcc-6.2.0/bin:$(PATH)
- LD_LIBRARY_PATH := /usr/um/gcc-6.2.0/lib64
- LD_RUN_PATH := /usr/um/gcc-6.2.0/lib64
- # TODO
- # Change EXECUTABLE to match the command name given in the project spec.
- EXECUTABLE = lab1
- DEBUG = $(EXECUTABLE)_debug
- # designate which compiler to use
- CXX = g++
- # list of test drivers (with main()) for development
- TESTSOURCES = $(wildcard test*.cpp)
- # names of test executables
- TESTS = $(TESTSOURCES:%.cpp=%)
- # list of sources used in project
- SOURCES = $(wildcard *.cpp)
- SOURCES := $(filter-out $(TESTSOURCES), $(SOURCES))
- # list of objects used in project
- OBJECTS = $(SOURCES:%.cpp=%.o)
- # If main() is in another file delete the line above, edit and uncomment below
- PROJECTFILE = lab1.cpp
- # name of the tar ball created for submission
- PARTIAL_SUBMITFILE = partialsubmit.tar.gz
- FULL_SUBMITFILE = fullsubmit.tar.gz
- # name of the perf data file, only used by the clean target
- PERF_FILE = perf.data*
- #Default Flags (would prefer -std=c++17 but Mac/Xcode/Clang doesn't support)
- CXXFLAGS = -std=c++1z -Wconversion -Wall -Werror -Wextra -pedantic
- # make release - will compile "all" with $(CXXFLAGS) and the -O3 flag
- # also defines NDEBUG so that asserts will not check
- release: CXXFLAGS += -O3 -DNDEBUG
- release: all
- # make debug - will compile "all" with $(CXXFLAGS) and the -g flag
- # also defines DEBUG so that "#ifdef DEBUG /*...*/ #endif" works
- debug: EXECUTABLE := $(DEBUG)
- debug: CXXFLAGS += -g3 -DDEBUG
- debug: clean all
- # make profile - will compile "all" with $(CXXFLAGS) and the -pg flag
- profile: CXXFLAGS += -pg
- profile: clean all
- # make static - will perform static analysis in the matter currently used
- # on the autograder
- static:
- cppcheck --enable=all --suppress=missingIncludeSystem $(SOURCES) *.h *.hpp
- # highest target; sews together all objects into executable
- all: $(EXECUTABLE)
- $(EXECUTABLE): $(OBJECTS)
- ifeq ($(EXECUTABLE), executable)
- @echo Edit EXECUTABLE variable in Makefile.
- @echo Using default a.out.
- $(CXX) $(CXXFLAGS) $(OBJECTS)
- else
- $(CXX) $(CXXFLAGS) $(OBJECTS) -o $(EXECUTABLE)
- endif
- # Automatically generate any build rules for test*.cpp files
- define make_tests
- ifeq ($$(PROJECTFILE),)
- @echo Edit PROJECTFILE variable to .cpp file with main\(\)
- @exit 1
- endif
- SRCS = $$(filter-out $$(PROJECTFILE), $$(SOURCES))
- OBJS = $$(SRCS:%.cpp=%.o)
- HDRS = $$(wildcard *.h *.hpp)
- $(1): CXXFLAGS += -g3 -DDEBUG
- $(1): $$(OBJS) $$(HDRS) $(1).cpp
- $$(CXX) $$(CXXFLAGS) $$(OBJS) $(1).cpp -o $(1)
- endef
- $(foreach test, $(TESTS), $(eval $(call make_tests, $(test))))
- alltests: clean $(TESTS)
- # rule for creating objects
- %.o: %.cpp
- $(CXX) $(CXXFLAGS) -c $*.cpp
- # make clean - remove .o files, executables, tarball
- clean:
- rm -f $(OBJECTS) $(EXECUTABLE) $(DEBUG) $(TESTS) $(PARTIAL_SUBMITFILE) $(FULL_SUBMITFILE) $(PERF_FILE)
- rm -Rf *.dSYM
- # make partialsubmit.tar.gz - cleans, runs dos2unix, creates tarball omitting test cases
- PARTIAL_SUBMITFILES=$(filter-out $(TESTSOURCES), $(wildcard Makefile *.h *.hpp *.cpp))
- $(PARTIAL_SUBMITFILE): $(PARTIAL_SUBMITFILES)
- rm -f $(PARTIAL_SUBMITFILE) $(FULL_SUBMITFILE)
- -dos2unix $(PARTIAL_SUBMITFILES)
- COPYFILE_DISABLE=true tar -vczf $(PARTIAL_SUBMITFILE) $(PARTIAL_SUBMITFILES)
- @echo !!! WARNING: No test cases included. Use 'make fullsubmit' to include test cases. !!!
- # make fullsubmit.tar.gz - cleans, runs dos2unix, creates tarball including test cases
- FULL_SUBMITFILES=$(filter-out $(TESTSOURCES), $(wildcard Makefile *.h *.hpp *.cpp test*.txt))
- $(FULL_SUBMITFILE): $(FULL_SUBMITFILES)
- rm -f $(PARTIAL_SUBMITFILE) $(FULL_SUBMITFILE)
- -dos2unix $(FULL_SUBMITFILES)
- COPYFILE_DISABLE=true tar -vczf $(FULL_SUBMITFILE) $(FULL_SUBMITFILES)
- @echo !!! Final submission prepared, test cases included... READY FOR GRADING !!!
- # shortcut for make submit tarballs
- partialsubmit: $(PARTIAL_SUBMITFILE)
- fullsubmit: $(FULL_SUBMITFILE)
- define MAKEFILE_HELP
- EECS281 Advanced Makefile Help
- * This Makefile uses advanced techniques, for more information:
- $$ man make
- * General usage
- 1. Follow directions at each "TODO" in this file.
- a. Set EXECUTABLE equal to the name given in the project specification.
- b. Set PROJECTFILE equal to the name of the source file with main()
- c. Add any dependency rules specific to your files.
- 2. Build, test, submit... repeat as necessary.
- * Preparing submissions
- A) To build 'partialsubmit.tar.gz', a tarball without tests used to find
- buggy solutions in the autograder. This is useful for faster autograder
- runs during development and free submissions if the project does not
- build.
- $$ make partialsubmit
- B) Build 'fullsubmit.tar.gz' a tarball complete with autograder test cases.
- ALWAYS USE THIS FOR FINAL GRADING! It is also useful when trying to
- find buggy solutions in the autograder.
- $$ make fullsubmit
- * Unit testing support
- A) Source files for unit testing should be named test*.cpp. Examples
- include test_input.cpp or test3.cpp.
- B) Automatic build rules are generated to support the following:
- $$ make test_input
- $$ make test3
- $$ make alltests (this builds all test drivers)
- C) If test drivers need special dependencies, they must be added manually.
- D) IMPORTANT: NO SOURCE FILES THAT BEGIN WITH test WILL BE ADDED TO ANY
- SUBMISSION TARBALLS.
- * Static Analysis support
- A) Matches current autograder style grading tests
- B) Usage:
- $$ make static
- endef
- export MAKEFILE_HELP
- help:
- @echo "$$MAKEFILE_HELP"
- #######################
- # TODO (begin) #
- #######################
- # individual dependencies for objects
- # Examples:
- # "Add a header file dependency"
- # project2.o: project2.cpp project2.h
- #
- # "Add multiple headers and a separate class"
- # HEADERS = some.h special.h header.h files.h
- # myclass.o: myclass.cpp myclass.h $(HEADERS)
- # project5.o: project5.cpp myclass.o $(HEADERS)
- #
- # ADD YOUR OWN DEPENDENCIES HERE
- lab1.o: lab1.cpp sorting.h
- #test_thing: test_thing.cpp class.o functions.o
- #class.o: class.cpp class.h
- #functions.o: functions.cpp functions.h
- #project0.o: project0.cpp class.h functions.h
- ######################
- # TODO (end) #
- ######################
- # these targets do not create any files
- .PHONY: all release debug profile static clean alltests partialsubmit fullsubmit help
- # disable built-in rules
- .SUFFIXES:
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement