Question
1. Please write a c++ program and show all outputs. PLEASE SEE LIKE EXAMPLE BELOW. In this assignment, you will implement the VectorGraphic and Point
1. Please write a c++ program and show all outputs. PLEASE SEE LIKE EXAMPLE BELOW.
In this assignment, you will implement the VectorGraphic and Point classes as described in Lesson One. There is no need to provide more features than those already outlined, so these classes will be fairly easy to implement. Create the classes from an XML file that defines a single VectorGraphic (use this example XML file). This XML file should be passed as the first parameter to your program. The second parameter should be a destination file. Parse the sourcefile and create the VectorGraphic instance, then persist that instance to the destination file; reuse as many of the tools and techniques you used for reading it in as possible.
To implement the parsing elegantly, make use of stringstreams and getline where they are convenient. You may also find you need to perform two algorithms: eating white space from a stream, and trimming string tokens. Write trim and eat functions as general tools for parsing and include them in a separate header and namespace that makes it clear they're generic, reusable, and primitive parsing tools. This collection of toolsbits of ultimately generic algorithmsoften grows throughout the life of a system, and keeping them separate from the beginning makes them easy to refer to and promotes their use as a helpful library. The eat and trim functions should be generic; that is, they should work on more than just whitespace. Their signatures should be as follows:
void trim(std::string& sourceString, std::string const& trimmables);
void eat(std::istream& sourceStream, std::string const& edibles);
Note that white space can be defined by any character in the string " \t"(a space is the last character).
Make sure you consistently use exceptions as your only means of reporting exceptional circumstances. Remember, you should only catch an exception if you are going to do something particularly useful with it; otherwise, it should propagate until it hits a boundary point (such as main). For example, an std::bad_alloc exception thrown by operator new needn't be handled necessarily by the code that invoked new, but might be best handled in main and treated as an unrecoverable failure. Make sure to report problems with malformed XML cleanly, with specific information about the offending line and the type of offense.
Resources:
Example_unit_tests_assignment1.zip
VectorGraphic.xml
To submit your assignment:
Use the +Submit Assignment link located in the top right.
Click the Choose File button to find and select the saved file.
Click the Submit Assignment button to turn in your assignment.
Here are the items I will be especially looking for in Assignment 1:
VectorGraphic /10
Trim/Eat
Persist from XML
Persist to XML
Unit Tests
Transparent Persistence
Key Term
transparent persistence
Transparent persistence is an object-oriented answer to the fact that things must be "saved" at times, and that applications will be opened and closed, services stopped, and so forth. Transparent persistence simply abstracts us from these issues by abstracting the developer from where exactly the objects are located (whether in RAM or on hard disk). In transparent persistence, objects don't get destructed when a program is closed; they simply are moved to permanent storageautomatically, of course.
This is truly an ideal world. The developer can design and implement free of translation concerns; objects have less complicated states; there's no need to worry about hopelessly mundane complexities like what delimiter to use in the file format; and, best of all, it's all automatic: not even an onPersist method to overload. Think of it as literally taking the object that sits in memory and putting it onto disk (or other storage medium). It's a surprisingly simple concept; yet few languages have provided it and it's still the exception, not the rule. The power of C++ is in its ubiquity, standardness, efficiency, and flexibility.
Serialization
Key Term
serialization
Serialization is the term for the current de facto standard means of persisting objects. If you've worked with certain Microsoft libraries or are familiar with Java, you might have seen an ISerializable interface and methods like onSerialize. With serialization, each object that can be persisted usually implements an interface such as ISerializable, which includes operations to read and write the object to a stream.
For this to work elegantly, it must be applied down to the very last object, similar to the swap idiom you learned about in C++: Intermediate. When serialization is used ubiquitously like this, it can be fairly straightforward to work with; just implement the ISerializable interface and your object will be serialized when and how it should be by those who can make such decisions.
File Formats
Key Term
file-format based persistence
Consumer applications most commonly use file-format based persistence. This stems primarily from the past, but also from the need for portability (or interoperability, or whatever you may call it). The portability issue is that an openly defined simple file format usually will work on multiple platforms and multiple products can support it. For example, Rich Text Format (RTF) is almost always supported by word processors. If each word processor simply used its own home-grown means of serialization or different language-defined means of transparent persistence, portability would be foiled.
A more ideal approach to persistence would take the best of both worlds (transparency and portability) by creating standardized means of object storage, allowing all transparent-persistence vendors to work in the same language. Don't expect this any time soon, though, as even the newest of languages haven't taken this approach to heart.
File formats will be a reality for application developers for much of the foreseeable future, and applications will almost never be able to assume just one stable file format. In our vector-graphic application, we'd no doubt need to make sure to support at least a half-dozen common (and radically different) file formats. As each year passed, so would the file formats, whether because a popular product had gone out of vogue or because new features were needed, and thus, new versions of the file format would arise.
An XML-like File Format
Since we're working mostly in the domain of consumer applications, a file format-based approach is what our system calls for as its solution to persistence. For our file format, we'll use XML (eXtensible Markup Language) or something that closely resembles it (actual XML might require more information than what we'll be using). XML is not so much a language, as a common framework for defining simple structured information. This course will not focus much on XML, but be aware that the file format we will use and expand upon is done in an XML-ish manner (once again, it might not be proper and standard).
Example 1.4 will be used to test the assignment that concludes this lesson.
Code Example 1.4 |
---|
|
Example 1.4 illustrates a few of the more fundamental rules of XML, which you will need to take into account in your support for this file format.
The end of an element is marked by a / character in its tag. If it is a composite element, the / character will be at the very beginning of the tag. If it is a primitive element, the / will be at the end.
White space is legal in the places where it varies in the example, but not before the name of the tag or after the forward slash that ends the primitive tag.
XML is case sensitive.
Parameter values are quoted.
There is only a syntactical difference between the and when nothing lies between the beginning and ending tags.
This basic file format should suffice for our current needs, though, in the future, it is clear that we'd have to support other formats besides our own, home-grown one. During this course, we'll be sticking to our own file format, in order to stay focused on the system and not on the way in which it is persisted. In an industry-quality system, we'd have to allow an easy way to plug in new file-format support; we might not even need to provide our own format, but instead could use an industry-standard one that met our needs.
Pushing File Formats Into the Client
File formats have strong change cases; which to support and how to support them almost always changes regularly throughout the life of a product. On top of this, not all file formats for a given purpose have the same design epiphanies, so abstraction isn't always possible. For example, we can't assume we can come up with one universal interface and process in our framework (or client) that works with all file formats (which would just be different implementations of the interface)or at least the task would not always be as rewarding as you might think.
There are many interesting approaches to supporting multiple file formats (or even just one) elegantly, but almost all of these are in the client's domain. Frameworks are not upgraded regularly; they're built for a lifetime that often encompasses more than one generation of products. Since we can't make assumptions in our framework about tomorrow's file formats, and we can't expect to anticipate every client's needs, we won't provide one-size-fits-all file format support in our framework, but instead keep all persistence on the client side.
Using Streams in the XML Parsing
Key Term
peek operation
stringstreams
Although you should already be familiar with streams, you will need a few less-than-common operations to cleanly parse the XML file. One of these is the peek operation, which allows you to get the next character from the stream without actually removing it from the stream. Although you can put characters back onto a stream using putback and ungetc, these methods are not reliable, as not all stream implementations allow putting more than one character back (which doesn't provide much over peek).
Another perhaps less-commonly used technique on which you might find yourself relying heavily is the use of stringstreams, as available in the standard header. A stringstream turns a string into a stream, effectively. You can use the stringstream class for either extracting elements from a string using stream operations, or inserting elements into a stream and requesting a string object from the stream. A stringstream (available as istringstream, ostringstream, or just stringstream for both input and output) has a normal stream interface with a few additions that will be helpful to you.
std::istringstream and std::stringstream provide a constructor that takes a single string parameter. This string is what will be used as the stream's underlying data.
Two str() methods are provided. One takes a single parameter of a string and the other returns a string (this of course, is only clear if you look at documentation, because C++ Standard Library naming is often vague and abbreviated). These two methods, respectively, set the entire contents of the stream and get the entire contents of the string. Note that the str() that returns the contents of the buffer will return the full buffer, not where you currently might be positioned in it. So, for example, the output of the following lines of code would be "avocado is a fruit."
Code Example 1.5 |
---|
std::istringstream sourceStream("avocado is a fruit"); std::string firstString; sourceStream >> firstString; std::cout |
One final general-purpose tool for working with streams is the getline function as defined in . This algorithm is of the form "std::istream& getline (std::istream& sourceStream, std::string& destination, char delimiter = ' ')" and is fairly straightforward. It reads, ignoring all leading white space, up until the first occurrence of the specified delimiter, which defaults to the end-of-line character. The ability to specify a delimiter other than the end of a line to getline should necessitate a name more like "gettoken," of course. Be aware that there is also a getline method provided by the istream interface that is not string-based, but instead c-string based.
Some more C++11 concepts and features
constexpr
C++11 introduces the keyword "constexpr", which enables an expression to be evaluated at compile time. It's worth using whenever possible to maximize what is done at compile time vs. run time.
A constant expression is an expression whose value cannot change and that can be evaluated at compile time (Lippman). For example, a literal (e.g., 42) is a constant expression. A const object initialized with a constant expression is a constant expression:
const int max_size = 42; const int boundary = max_size + 1;
We can tell the compiler to verify that a variable is a constant expression by declaring it with the constexpr keyword:
constexpr int max_size = 42; constexpr boundary = max_size + 1;
Functions can also be constexpr, if their return types and parameters are literals, and they contain a single return statement. For example:
constexpr int array_size(int sz) { return sz + 1; }
Such functions can then be used for compile-time actions like declaring the size of an array:
std::array a;
A constexpr function can be called with a non-const argument, but then the result is not a constant expression -- and in that case it could not be used at compile-time, e.g. to declare the size of an array).
Constructors and other class member functions can also be declared constexpr, which means that user-defined types can now be literal also (because their values can be determined during compilation), and some operations on user-defined objects can now be available at compile time. (See Meyers for a Point class example.)
Literals
A literal is a value. The compiler looks at that value, figures out its type, and creates an object of that type with that value.
We can affect the type the compiler creates by decorating the value with some different prefixes and suffixes. For example:
20 // decimal integer 0x14 // hexadecimal integer 20U // unsigned integer 20LL // long long 20.2F // float (by default, floating point literals are double) 20.2L // long double
For characters and character strings:
'a' // character literal "char string" // string literal L"wide" // wide character string
C++11 adds some new string literals:
u"hello" // Unicode 16 string (char16_t) U"hello" // Unicode 32 string (char32_t) u8"hello" // utf-8 string literal (char)
Another new C++11 feature is the "raw string" literal. Traditional string literals are surrounded with double quotes, and any special characters (like newlines) in the string will start with a backslash (e.g., for newline). If you wanted a string containing a backslash or some double quotes, you would have to prefix those with a backslash. So to set a string equal to something like this: \"word1"\"word2"\, I would do this:
string s = "\\\"word1\"\\\"word2\"\\"; // I think that's right.
The raw string literal makes this much easier:
string s = R"(\"word1"\"word2"\)"; // that's certainly easier to read
Raw strings use the notation R"(ccc)" for a sequence of characters ccc. The parentheses are used to allow "unescaped" double quotes in the string. But now, you might notice, we could have a problem if we want parentheses in our string. That's handled by allowing delimiters before and after the enclosing parentheses, like this:
string s = R"***("Now I can (if I wish) put parentheses in here")***";
Raw strings also can contain literal newlines, like this:
string s = R"(line1 line2 line3)";
C++11 also lets us create user-defined literals... literals for user-defined types or new forms of literals for built-in types. These are supported by mapping a given suffix to a desired type by defining an operator"" function.
For example, let's say I have a user-defined Time class. I could do something like this:
Time operator"" _m(const char* minutes) { return Time(minutes); // assumes appropriate Time constuctor } Time t = 20_m;
Note: at the time of writing, my compiler required user-defined literal suffixes to start with "_".
Generalized POD
The term "POD" has been around a while; it stands for "Plain Old Data"... in contrast to, say, a complicated user-defined class. It is data that occupies a contiguous sequence of bytes in memory. Sometimes we want to use POD objects for the speed and efficiency with which they can be moved around. Using std::memcpy() to copy a large array of elements is much faster than calling a bunch of copy constructors.
C++11 defines rules for being able to treat any given object as a POD. See Stroustrup for a complete description. The details can get complicated, but at a high-level there are a few general characteristics of a POD:
it has to be trivial -- support trivial/default copy and move operations
it has to have a standard layout -- no vptr
members and base classes also have to be PODs
Fortunately, you don't have to memorize all the rules to know whether you can treat an object as a POD. The standard library provides an "is_pod" predicate function in that makes it easy. Here's an example from Stroustrup:
template void optimized_copy(T* source, T* destination, int count) { if (std::is_pod::value) { // can use optimized memcpy std::memcpy(source, destination, count); } else { // must copy each individually for (int i = 0; i
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started