联系方式

  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-23:00
  • 微信:codehelp

您当前位置:首页 >> Java程序Java程序

日期:2020-11-24 08:58

ENGN2912B 2020 | Lecture15 - 1
Brown University ENGN2912B Fall 2020
Scientific Computing in C++
Lecture 15 | The IfsViewer Application
In this lecture we will start to build an interactive application of moderate complexity for reading,
visualizing, operating on, and saving polygon meshes and point clouds. This is a much more complex
project than the ones we have worked on so far, which requires careful class design, partitioning the
project files into libraries, and also linking your program using external libraries. These libraries are part
of even more complex packages, which you will have to install in your machine, and configure for use in
conjunction with Qt.
Figure 1 The IfsViewer application shown running in OSX and Windows.
This application, as shown above running in OSX and Windows, will be extended in subsequent lectures
and homework assignments. What you will implement in this first assignment provides basic
functionality to load, save, and operate on polygon meshes and point clouds. It will be able to load a
polygon mesh or a point cloud from a file, visualize it, and save the polygon mesh or point cloud to a file.
In this first homework assignment you will implement the main data structures for representing a
polygon mesh or a point cloud in memory, a method to load a polygon mesh or point cloud from a file,
and a method to save the polygon mesh or point cloud to a file. You will implement the loading and
saving using the Factory framework, which we covered in a previous lecture. In subsequent homework
assignments you will implement additional loaders and savers to support other popular file formats used
to store polygon meshes and point clouds. For this assignment, all the user interface and OpenGL
graphics programming is provided. In subsequent assignments you will extend the application by
writing, in addition to the loaders and savers mentioned above, a number of geometry processing
operations. Since most of these algorithms do require additions to the user interface to set various
parameters, we will design a unified framework to add these user interface components, which you will
also implement in an incremental fashion.
ENGN2912B 2020 | Lecture15 - 2
Qt
The graphical user interface for IfsViewer is built using Qt. Your part of the code can be implemented in
its vast majority using the standard C++ classes, but you can also use Qt classes, if you prefer to do so.
We have discussed the installation process for Qt within the context of the QtCalc assignments. For this
assignment you should download the archive file Assignment8 archive from the course web site, unzip
it, and use CMake to build project files using the CMakeLists.txt file located in the src directory. In this
assignment we will not use QMake.
The goal is to complete the IfsViewer application implementation
I have decided to partition the project into three subprojects. The files in the directory
IfsViewer/src/viewer implement the user interface. In this assignment you don’t need to make any
changes to those files. The files in the directory IfsViewer/src/util implement some utility functions
which in this assignment you don’t have to change either. So far there is only one class in this directory.
The BBox class provides functionality to represent a bounding box containing a set of points in ddimensional
space. In this application we are only using it for d=3. One of the constructors builds a
bounding box from a set of points, stored as an instance of the vector class which stores all the
coordinates of the points as a linear array (x0,y0,z0),(x1,y1,z1),…,(xN,yN,ZN). Due to lack of time, the
BBox class is already implemented.
The VRML (Virtual Reality Modeling Language, pronounced vermal or by its initials) is an international
standard file format for representing 3-dimensional (3D) interactive vector graphics, designed
particularly with the World Wide Web in mind. It has been superseded by X3D, but still widely used.
https://en.wikipedia.org/wiki/VRML
As a reference, this is where you can read the VRML specification
http://www.web3d.org/documents/specifications/14772/V2.0/
You can download the VRML 2.0 - Cheat Sheat, by Jan Hardenbergh from the course web site. This is a
summary of all the VRML nodes.
The “Annotated VRML 97 Reference Manual” by Rikk Carey and Gavin Bell, provides additional
information about the VRML standard
http://accad.osu.edu/~pgerstma/class/vnv/resources/info/AnnotatedVrmlRef/about.htm
In Assignment 8 you will have to complete the implementation of two classes in the directories
IfsViewer/src/ifs. First of all you will have to complete the implementation of the Ifs class. This
class is intended to be a container for the information which can be represented in a VRML
IndexedFaceSet node. This is how the IndexedFaceSet node is defined in the VRML standard
IndexedFaceSet {
eventIn MFInt32 set_colorIndex
eventIn MFInt32 set_coordIndex
eventIn MFInt32 set_normalIndex
eventIn MFInt32 set_texCoordIndex
exposedField SFNode color NULL
exposedField SFNode coord NULL
exposedField SFNode normal NULL
exposedField SFNode texCoord NULL
field SFBool ccw TRUE
ENGN2912B 2020 | Lecture15 - 3
field MFInt32 colorIndex [] # [-1,)
field SFBool colorPerVertex TRUE
field SFBool convex TRUE
field MFInt32 coordIndex [] # [-1,)
field SFFloat creaseAngle 0 # [ 0,)
field MFInt32 normalIndex [] # [-1,)
field SFBool normalPerVertex TRUE
field SFBool solid TRUE
field MFInt32 texCoordIndex [] # [-1,)
}
We are going to ignore the eventIn fields, as well as whether fields are “exposed” or not, and to keep
things simpler we are not going to implement the color, coord, normal, and texCoord fields as
separate classes. Instead, the fields will be represented as follows in the Ifs class. Various methods to
access these private variables are defined in the Ifs.hpp header file. You have to implement them all.
Pay particular attention to understanding the convention adopted in the standard to represent property
bindings, and in particular you need to understand what is the meaning of properties bound
PER_VERTEX, PER_FACE, PER_FACE_INDEXED, and PER_CORNER, as well as what is the role of the four
index fields in all these cases.
class Ifs {
private:
bool _ccw;
bool _convex;
float _creaseAngle;
bool _solid;
bool _normalPerVertex;
bool _colorPerVertex;
vector _coord;
vector _coordIndex;
vector _normal;
vector _normalIndex;
vector _color;
vector _colorIndex;
vector _texCoord;
vector _texCoordIndex;
// …
}
The remaining files in this directory implement Factory frameworks to load instances of the Ifs class
from files, and to save instances of the Ifs class to files
IfsLoader.hpp
IfsSaver.hpp
IfsLoaderOpt.hpp
IfsLoaderOpt.cpp
IfsLoaderWrl.hpp
IfsLoaderWrl.cpp
IfsSaverWrl.hpp
IfsSaverWrl.cpp
IfsSaverFactory.hpp
IfsSaverFactory.cpp
ENGN2912B 2020 | Lecture15 - 4
You also have to implement the IfsLoaderWrl class using technique based on partitioning the input
stream into “tokens”. The other classes are already implemented, and you can use some as examples to
figure out how to implement the IfsLoaderWrl class. In subsequent assignments we will extend the
VRML parser, and will implement other parsers using the same methodology.
But we will first look at how to parse command line parameters, both for command line applications
such as those we have been writing since the beginning of the course. One important use for command
line parameters is to be able to invoke your programs to run without user interaction. Command line
parameters can be used to specify the names of input and output files, to specify values for Boolean
variables, also called “switches”, and to specify values for numerical variables.
For example, to debug the code that you need to implement for the IfsViewer application, and due to
lack of time, we are providing a command line application named ifstest which will take as arguments
a number of optional switches, the name of an input file, and the name of an output file. One of those
switches turns on and off the printing of console messages used to monitor the progress of the program.
More details about this application are provided in the ignment8 description file. The application will be
run from a console by typing the following command
> ifstest –debug inputFile.wrl outputFile.wrl
In this case the command line parameters is an array of four C-style (char*) strings composed of:
“ifstest”, “-debug”, “inputFile.wrl”, and “outputFile.wrl”. Note that the first parameter is the
name of the application itself.
Even though the application ifstest is already implemented, you should study it carefully, and if you have
xtra time you should try to implement your own version from scratch.
Parsing Command Line Parameters in Command Line Applications
A command line application executes the main() function. To get access to the command line
parameters you must declare main() as follows
int main(int argc char* argv) {
// process ...
}
The actual names of the arguments is not important, but this is the established convention: argc stands
for argument count, and argv for argument vector. Since argv[0] is always equal to the name of the
application, it is guaranteed that argc>=1.
For the example given above, we could try to process the command line parameters as follows
#include
int main(int argc, char** argv) {
bool debug = false;
string inFile = “”;
string outFile = “”;
if(string(argv[1])==”-debug”)
debug = true;
ENGN2912B 2020 | Lecture15 - 5
inFile = string(argv[2]);
outFile = string(argv[3]);
// load input file
// process ...
// save output file
return 0;
}
Note that since the command line parameters are passed as C-style strings, and we have decided to
implement this program using C++ strings, we convert the parameters from char* to string using the
string class constructor. We first initialize the variables debug, inFile, and outFile to default values,
and then we set them to the values specified by the command line parameters. If argv[1] is equal to
the string “-debug” then we set the debug variable to true. Then we set the string inFile to the value
specified by the command line parameter argv[2], and outFile to the value specified by the command
line parameter argv[3].
This program will work as long as it run as described above, with the three arguments. But it will fail for
example it is run without the “-debug” switch
> ifstest inputFile.wrl outputFile.wrl
In fact, this program most likely will crash while trying to set the value of the outFile variable, since the
fourth argument argv[3] would not be defined. To prevent crashes we need to implement the parsing
of command line parameters in a different way, where we analyze each command line parameter and
set variables depending on its values. At the same time, we need to detect errors in the command line
syntax, and exit the program reporting an error code in such cases. For example, consider the following
code fragment
#include
int main(int argc, char** argv) {
bool debug = false;
string inFile = “”;
string outFile = “”;
for(int i=1;i if(string(argv[i])==”-debug”)
debug = true;
else if(argv[i][0]==’-‘)
return -1;
else if(inFile==””)
inFile = string(argv[i]);
else if(outFile==””)
outFile = string(argv[i]);
}
if(inFile==””) {
if(debug) { cerr << “ERROR | no input file name” << endl; }
return -2;
}
if(outFile==””) {
if(debug) { cerr << “ERROR | no output file name” << endl; }
return -3;
}
// load input file
// process ...
ENGN2912B 2020 | Lecture15 - 6
// save output file
return 0;
}
In the loop for(int i=1;iprocessed. In this implementation we are assuming that zero or more switches, an input file name, and
an output file name follow the application name in the command line. The application will neither crash
if the “-debug” switch is not specified, nor if the input or output file names are missing. However, the
application will subsequently exit with an error message if either one of the two file names are not
specified. Also note that the statement else if(argv[i][0]==’-‘) exit -1; will make the
application quit if any other switch is specified. By convention switches are specified with strings starting
with a ‘-‘ character. Alternatively, we could replace this statement with else if(argv[i][0]==’-‘)
continue; to skip unrecognized switches.
It is good practice to make the application print a message explaining what is the acceptable command
line syntax. For example, in this code segment the application will print the usage massage and quit if it
is run without parameters, if the “-u” or “-usage” flags are specified, and if any other unrecognized flag
is specified. Square brackets “[]” describe optional parameters, and the symbol “|” specifies
alternative syntax.
#include
void usage() {
cerr
<< “USAGE | ifstest [-d|-debug][-u|-usage] inputFile.wrl outputFile.wrl”
<< endl;
}
int main(int argc char* argv) {
bool debug = false;
string inFile = “”;
string outFile = “”;
if(argc==1) {
usage();
return 0;
}
for(int i=1;i if(string(argv[i])==”-d” || string(argv[i])==”-debug”) {
debug = true;
} if(string(argv[i])==”-u” || string(argv[i])==”-usage”) {
usage();
return 0;
} else if(argv[i][0]==’-‘) {
usage();
return -1;
} else if(inFile==””) {
inFile = string(argv[i]);
} else if(outFile==””) {
outFile = string(argv[i]);
}
}
if(inFile==””) {
if(debug) { cerr << “ERROR | no input file name” << endl; }
return -2;
}
if(outFile==””) {
ENGN2912B 2020 | Lecture15 - 7
if(debug) { cerr << “ERROR | no output file name” << endl; }
return -3;
}
// load input file
// process ...
// save output file
return 0;
}
As you can see in this example, adding more switches is straightforward. The convention in this
implementation is that all the switches must precede the input and output file names in the command
line. The switches can be specified in arbitrary order, but they have to be followed by the input and
output files in that order. If we want to also be able to specify the input and/or output file names at
arbitrary positions in the command line, we need to support the processing of command line
parameters with additional values. For example, consider the following revised usage() function
void usage() {
cerr << “USAGE | ifstest” < cerr << “ [-d|-debug ]” << endl;
cerr << “ [-u|-usage ]” << endl;
cerr << “ [-i inputFile.wrl ]” << endl;
cerr << “ [-o outputFile.wrl]” << endl;
}
In this command line syntax the input file name is specified as the command line parameter following a
command line parameter matching the string “-i”, and the output file name is specified as the
command line parameter following a command line parameter matching the string “-o”. The command
line processing loop can be modified as follows
for(int i=1;i if(string(argv[i])==”-d” || string(argv[i])==”-debug”) {
debug = true;
} if(string(argv[i])==”-u” || string(argv[i])==”-usage”) {
usage();
return 0;
} else if(string(argv[i])==”-i”) {
inFile = string(argv[++i]);
} else if(string(argv[i])==”-o”) {
outFile = string(argv[++i]);
} else if(argv[i][0]==’-‘) {
usage();
return -1;
}
}
Note that the index is incremented before assigning the command line parameter of the variables
inFile and outFile, and the application will most likely crash for example if the command line
parameter “-i” is the last one in the command line and it is not followed by the input file name. One
possibility is to add additional tests to catch this command line syntax error, such as
} else if(string(argv[i])==”-i”) {
if(++i >= argc) {
if(debug) { cerr << “ERROR | no input file name” << endl; }
ENGN2912B 2020 | Lecture15 - 8
return -4;
}
inFile = string(argv[i]);
} ...
Another possibility is to skip the parameter, since the missing file name error will be cached later
} else if(string(argv[i])==”-i”) {
if(++i >= argc) continue;
if(argv[i][0]==’-‘) {
if(debug) { cerr << “ERROR | missing input file name after –i” << endl; }
return -3;
}
inFile = string(argv[i]);
} ...
Note that we added another test to catch a missing argument error occurring in the middle of the
command line.
Parsing Numerical Parameters
In some cases it is necessary to specify numerical parameters in the command line. In those cases we
need to convert the string representation of the numerical values to actual numerical values. As an
example, suppose that we want to specify the center and radius of a sphere so that all the vertices of
the input polygon mesh not contained within the sphere are deleted, as well as all the polygons
containing those vertices. The radius will be specified as an integer, and the center as three floating
point numbers. The following usage() function specifies the command line syntax
void usage() {
cerr << “USAGE | ifstest” < cerr << “ [-d|-debug ]” << endl;
cerr << “ [-u|-usage ]” << endl;
cerr << “ [-i inputFile.wrl ]” << endl;
cerr << “ [-o outputFile.wrl ]” << endl;
cerr << “ [[-r|-radius] radius]” << endl;
cerr << “ [[-c|-center] x y z ]” << endl;
}
Additional variables need to be added to the main() function
int main(int argc char* argv) {
bool debug = false;
string inFile = “”;
string outFile = “”;
int r = 0;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
and statements need to be added to the command line processing loop
#include
ENGN2912B 2020 | Lecture15 - 9
// ...
} else if(string(argv[i])==”-r” || string(argv[i])==”-radius”) {
radius = atoi(argv[++i]);
} else if(string(argv[i])==”-c” || string(argv[i])==”-center”) {
x = atof(argv[++i]);
y = atof(argv[++i]);
z = atof(argv[++i]);
} ...
The functions atoi() and atof() are defined in the system header file cstdlib, which has to be
included as well for the program to compile.
int atoi (const char* str);
Parses the C string str interpreting its content as an integral number, which is returned as an int value.
The function first discards as many whitespace characters as necessary until the first non-whitespace
character is found. Then, starting from this character, takes an optional initial plus or minus sign
followed by as many base-10 digits as possible, and interprets them as a numerical value. The string can
contain additional characters after those that form the integral number, which are ignored and have no
effect on the behavior of this function. If the first sequence of non-whitespace characters in str is not a
valid integral number, or if no such sequence exists because either str is empty or it contains only
whitespace characters, no conversion is performed and zero is returned.
double atof (const char* str);
Parses the C string str interpreting its content as a floating point number and returns its value as a
double. The function first discards as many whitespace characters as necessary until the first nonwhitespace
character is found. Then, starting from this character, takes as many characters as possible
that are valid following a syntax resembling that of floating point literals (see below), and interprets
them as a numerical value. The rest of the string after the last valid character is ignored and has no
effect on the behavior of this function. A valid floating point number for atof using the "C" locale is
formed by an optional sign character (+ or -), followed by one of: A sequence of digits, optionally
containing a decimal-point character (.), optionally followed by an exponent part (an e or E character
followed by an optional sign and a sequence of digits). A 0x or 0X prefix, then a sequence of hexadecimal
digits optionally containing a decimal-point character (.), optionally followed by an hexadecimal
exponent part (a p or P character followed by an optional sign and a sequence of hexadecimal digits).
INF or INFINITY (ignoring case). NAN or NANsequence (ignoring case), where sequence is a sequence of
characters, where each character is either an alphanumeric character or the underscore character (_). If
the first sequence of non-whitespace characters in str does not form a valid floating-point number as
just defined, or if no such sequence exists because either str is empty or contains only whitespace
characters, no conversion is performed and the function returns 0.0.
Note that both atoi() nor atof() catch syntax errors in the input string, but rather than reporting the
error, they return default values (0 and 0.0 respectively). Alternatively, you could implement your own
numeric conversion functions based on the C-style sscanf(), which is defined in the header file
stdlib.h
int sscanf (const char* str, const char* format, ...);
where str is the C string that the function is trying to parse; format is a C string that contains a format
string that follows the same specifications as format in scanf (look for the scanf documentation on line
ENGN2912B 2020 | Lecture15 - 10
for details), and ... are additional arguments. Depending on the format string, the function may expect a
sequence of additional arguments, each containing a pointer to allocated storage where the
interpretation of the extracted characters is stored with the appropriate type. There should be at least
as many of these arguments as the number of values stored by the format specifiers. Additional
arguments are ignored by the function. On success, the function returns the number of items in the
argument list successfully filled. This count can match the expected number of items or be less (even
zero) in the case of a matching failure. In the case of an input failure before any data could be
successfully interpreted, EOF is returned. For example, the following code fragments illustrate how to
use sscanf() to parse an int and a float
const char* str = “-123”;
int radius = 0;
if(sscanf(str,”%d”,&radius)<1) {
// ERROR
}
const char* str = “0.876”;
float x = 0.0f;
if(sscanf(str,”%f”,&x)<1) {
// ERROR
}
Note however, that in certain cases which can be considered errors, sscanf() does not produce the
expected result. For example, for str=” -23sDS”, the call sscanf(str,”%d”,&radius) will set the
variable radius to the value -23, and will return the value 1.
Exception Handling
As we add more command line switches and parameters the number of possible errors increase, and it
becomes more and more difficult to handle them in an organized fashion. Exceptions provide a way to
react to exceptional circumstances in our program, such as command line processing errors, by
transferring control to special functions called handlers. To catch exceptions we must place a portion of
code under exception inspection. This is done by enclosing that portion of code in a try block. When an
exceptional circumstance arises within that block, an exception is thrown that transfers the control to
the exception handler. If no exception is thrown, the code continues normally and all handlers are
ignored. An exception is thrown by using the throw keyword from inside the try block. Exception
handlers are declared with the keyword catch, which must be placed immediately after the try block.
For example, we can modify our ifstest program as follows
#include
void usage() {
// ...
}
int main(int argc char* argv) {
bool debug = false;
string inFile = “”;
string outFile = “”;
try {
if(argc==1) trow 1;
for(int i=1;i if(string(argv[i])==”-d” || string(argv[i])==”-debug”) {
debug = true;
ENGN2912B 2020 | Lecture15 - 11
} if(string(argv[i])==”-u” || string(argv[i])==”-usage”) {
throw 2;
} else if(argv[i][0]==’-‘) {
trow 3;
} else if(inFile==””) {
inFile = string(argv[i]);
} else if(outFile==””) {
outFile = string(argv[i]);
}
}
if(inFile==””) throw 4;
if(outFile==””) throw 5;
} catch(int e) {
// print an appropriate error message and quit
switch(e) {
case 1: /* no command line parameters */ break;
case 2: /* usage */ break;
case 3: /* unrecognized switch */ break;
case 4: /* no input file name */ break;
case 5: /* no output file name */ break;
}
usage();
return -1;
}
// load input file
// process ...
// save output file
return 0;
}
A throw expression accepts one parameter (in this case an integer), which is passed as an argument to
the exception handler. The exception handler is declared with the catch keyword. As you can see, it
follows immediately the closing brace of the try block. The catch format is similar to a regular function
that always has at least one parameter. The type of this parameter is very important, since the type of
the argument passed by the throw expression is checked against it, and only in the case they match, the
exception is caught. We can chain multiple handlers (catch expressions), each one with a different
parameter type. Only the handler that matches its type with the argument specified in the throw
statement is executed.
try {
// ...
} catch(int eInt) {
// ...
} catch(float eFloat) {
// ...
} catch(...) {
// ...
}
If we use an ellipsis (...) as the parameter of catch, that handler will catch any exception no matter what
the type of the throw exception is. This can be used as a default handler that catches all exceptions not
caught by other handlers if it is specified at last. In this case the last handler would catch any exception
thrown with any parameter that is neither an int nor a float. After an exception has been handled the
program execution resumes after the try-catch block, not after the throw statement. It is also possible
ENGN2912B 2020 | Lecture15 - 12
to nest try-catch blocks within more external try blocks. In these cases, we have the possibility that an
internal catch block forwards the exception to its external level. This is done with the expression throw;
with no arguments. For example:
try {
try {
// ...
} catch(int eInt) {
throw; // throws exception to external try-catch block
}
} catch(float eFloat) {
// ...
} catch(...) {
// ...
}
When declaring a function we can limit the exception type it might directly or indirectly throw by
appending a throw suffix to the function declaration:
float myfunction (char param) throw (int);
This declares a function called myfunction which takes one argument of type char and returns an
element of type float. The only exception that this function might throw is an exception of type int. If it
throws an exception with a different type, either directly or indirectly, it cannot be caught by a regular
int-type handler. If this throw specifier is left empty with no type, this means the function is not allowed
to throw exceptions. Functions with no throw specifier (regular functions) are allowed to throw
exceptions with any type:
int myfunction (int param) throw(); // no exceptions allowed
int myfunction (int param); // all exceptions allowed
The C++ Standard library provides a base class specifically designed to declare objects to be thrown as
exceptions. It is called exception and is defined in the header file under the namespace
std. This class has the usual default and copy constructors, operators and destructors, plus an additional
virtual member function called what() that returns a null-terminated character sequence (char *) and
that can be overwritten in derived classes to contain some sort of description of the exception.
#include
#include
using namespace std;
class myexception: public exception {
virtual const char* what() const throw() {
return "My exception happened";
}
} myex;
int main () {
try {
throw myex;
} catch (exception& e) {
cout << e.what() << endl;
}
return 0;
}
ENGN2912B 2020 | Lecture15 - 13
For example, if we use the operator new and the memory cannot be allocated, an exception of type
bad_alloc is thrown:
try {
int * myarray= new int[1000];
} catch (bad_alloc& bae) {
cout << "Error allocating memory." << endl;
}
It is recommended to include all dynamic memory allocations within a try block that catches this type of
exception to perform a clean action instead of an abnormal program termination, which is what
happens when this type of exception is thrown and not caught. If you want to force a bad_alloc
exception to see it in action, you can try to allocate a huge array. Because bad_alloc is derived from the
standard base class exception, we can handle that same exception by catching references to the
exception class:
#include
#include
using namespace std;
int main () {
int* myarray = (int*)0;
try {
myarray= new int[1000];
} catch (exception& e) {
cout << "Standard exception: " << e.what() << endl;
return -1;
}
// myarray!=(int*)0 here
return 0;
}
We have placed a handler that catches exception objects by reference (notice the ampersand & after
the type), therefore this catches also classes derived from exception, like our myex object of class
myexception. All exceptions thrown by components of the C++ Standard library throw exceptions
derived from this std::exception class.
In the previous example the what() function of the class myexception returns same string
independently of what error you have detected. If you want to add an error message to the exception
that you throw you can define your exception class as follows
class myexception: public exception {
private:
const string& _errorMsg;
public:
myexeption(const string& errorMsg):_errorMsg(errorMsg) {
}
virtual const char* what() const throw() {
return _errorMsg.c_str();
}
};
ENGN2912B 2020 | Lecture15 - 14
Now in your code should look like this
try {
// ...
if(/* error 1 detected */) throw new myexception(“error 1 detected”);
// ...
if(/* error 2 detected */) throw new myexception(“error 2 detected”);
// ...
} catch (myexception* e) {
cout << "mexception: " << e->what() << endl;
delete e;
} catch (...) {
// handle other exceptions here
}
Note that the throw statement throws a pointer to an instance of the myexception class, and since a
new instance is created to be thrown, it has to be deleted after being catched. Also note that the
argument to the first catch statement is a pointer to an instance of the myexception class, and as a
result you get the error message through e->what().
Parsing ASCII Files using a Tokenizer class
We will describe how to parse a VRML file as required to complete the first phase of the IfsViewer. The
same techniques can be used to parse other ASCII files. Let us consider the following simple VRML file
#VRML V2.0 utf8
Shape {
geometry IndexedFaceSet {
coord Coordinate {
point [
0.0000 0.0000 1.0000
0.7236 0.5257 0.4472
-0.2764 0.8507 0.4472
-0.8944 0.0000 0.4472
-0.2764 -0.8507 0.4472
0.7236 -0.5257 0.4472
0.8944 0.0000 -0.4472
0.2764 0.8507 -0.4472
-0.7236 0.5257 -0.4472
-0.7236 -0.5257 -0.44

版权所有:留学生编程辅导网 2021,All Rights Reserved 联系方式:QQ:99515681 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。