?? exceptionhandling.htm
字號:
try {
		error_buffer.push_back(error information string)
	 } catch(..){
		set_buffer_overflow_flag
	 }
}
else {
try {
		error_buffer.push_back(data + error information error information string);
	 } catch(..) {
		set_buffer_overflow_flag
	 }
}
}
</PRE>
<FONT FACE="Courier New" SIZE=2><P>(Here we assume the dereference operator *in, simply returns the contents of the internal buffer -- yes, we could code the read to happen on *in, but then we may end up having to return an empty object if the data-stream goes 'bad' and we do not want to throw. Here, setting end-of-stream for the bad() case lets us terminate the read in a no-throw() kind of way.). <BR>
<BR>
3. For OutputIterators use the similar logic for ++. On ++, try to write the next 'data' element. If fail() or bad(), push the resulting 'data' element into an error buffer similar to what is shown for InputIterator. <BR>
<BR>
4. Set up the i/o Iterators so that they do not throw on any of their operations. Then, if we invoke an algorithm f(InputIterator, OutputIterator) we will have the property that every element that could be read from the InputIterator was either fetched completely or logged to an error buffer (up to error buffer overflow). Similarly, every element that could be written to the OutputIterator was either written completely or logged to an error buffer. Here, one could imagine a *standard* function for accessing such an error buffer so that errors could be dealt with in a generic way. Still, this is not a very strong guarantee since f(In, Out) could abort for reasons unrelated to the iterators it is working on. In any case , I think the best we can hope for is consistency at the end of f(In, Out), which should be enforced by the item-wise aCi property of our iterator, regardless of whether we throw or not. </P>
</FONT><FONT ><P> </P>
</FONT><FONT FACE="Courier New" SIZE=2><H2>DTL Input and Output Iterators -- The Practice</H2>
<H3>DTL Iterators vs. Standard Streams - The Good, The Bad, and The Fail</H3>
</FONT><FONT FACE="Courier New" SIZE=2><P>When iterators encounter an error when either reading or writing to the database or in initializing or maintaining their state, they act like streams. Errors could occur either through the manipulation of a single element (the element fails to pass the test in an InsVal or SelVal method) or the iterator itself could end up in an unusable state (say, if the connection with the database is lost). This behavior is very much like the C++ standard library streams, which have similar characteristics in their possible states:</P>
<UL>
<LI>Previous operation was successful and the stream is alive and well - the stream is in a <STRONG>good</STRONG> state - corresponds to a successful read, write (or initialization) in DTL.</FONT><FONT > </LI>
</FONT><FONT FACE="Courier New" SIZE=2><LI>Single stream in or stream out operation failed but stream is salvageable - the stream is in a <STRONG>fail </STRONG>state - this condition corresponds to the DTL iterator scenario such as a failed InsVal or SelVal call.</FONT><FONT > </LI>
</FONT><FONT FACE="Courier New" SIZE=2><LI>The stream itself could become corrupted such as if trying to read from or write to a nonexistent file. If the stream reads from a file that is there when the stream was created, but then the carpet gets yanked out from the stream's feet by someone removing the file, the stream will spit out to a junk place on the disk or read trash from such a bogus location. In this case, the stream is said to be in a <STRONG>bad </STRONG>state. The DTL equivalent would occur in a situation such as losing the connection with the database. Any iterators which try to read or write using that invalid connection would become corrupted and thus leave themselves in an unrecoverable state.</FONT><FONT > </LI></UL>
</FONT><FONT FACE="Courier New" SIZE=2><P>So as with standard libary streams, iterators provide flags for each of these three states and their values are OR'ed together to get the appropriate effect: dtl_iostate::goodbit, dtl_iostate::failbit, and dtl_iostate::badbit. </FONT><FONT FACE="Courier New" SIZE=2>The iterators inherit from dtl_ios_base, which contains setstate(), rdbuf(), clear(), good(), fail(), and bad() functions that work just like their counterparts in std::ios_base except that setstate() and clear() do not throw if badbit or failbit are set. In the case of DTL, all of these state operations are nothrow.</P>
<STRONG><H2>Now Introducing Our Hero, IOHandler<DataObj, ParamObj></H2>
</STRONG><FONT FACE="Courier New" SIZE=2><P>As described in the theory section above, there are fundamentally two main ways to deal with errors that occur while operating on an iterator. The first method is to simply throw in the event of an error, which will usually abort any algorithm operating on that range / iterator. In the throw case, we will usually want to rollback any operations performed on the range. The first version of our hero, AlwaysThrowsHandler<DataObj, ParamObj>, always tells the code that threw to rethrow the exception by returning dtl_ios_base::THROW_EXCEPTION. The assumption underlying this version of the error handler is that all errors should be passed back to the end-user who can then decide whether or not to roll back any changes made to the range. Examples of range commit and rollback are shown later. The second way of dealing with errors is to simply log & suppress them with the assumption that they will be dealt with after the calling algorithm is done with the iterator. This second method is supported by a version of our hero called LoggingHandler<DataObj, ParamObj> which records errors to a vector<string> for later use and suppresses exceptions by returning dtl_ios_base::SUPPRESS_ERROR. To see how an error handler is defined we turn our X-ray vision on some example code:</P>
</FONT>
<FONT FACE="Courier New" SIZE=3>
<pre><code><span class="codeComment">// Let's Use our X-Ray Vision to Look at the Innards of our Hero</span>
template<class DataObj, class ParamObj = DefaultParamObj<DataObj> > class OurHeroicHandler
{
private:
<span class="codeComment">// ... some state data, but assume handler is default constructible</span>
public:
dtl_ios_base::MeansOfRecovery
operator()(RootException &ex, dtl_ios_base &base,
DataObj &data, ParamObj &params)
{
<span class="codeComment">// example of what you might do in a handler</span>
if (bad())
{
LogErrorToFile(ex);
return dtl_ios_base::THROW_EXCEPTION;
}
else if (fail())
{
<span class="codeComment">// tries to make the DataObj valid and then reapplies previous operation
// to base on the good object ... may still fail</span>
bool failed = WorkMagicOnDataObjAndTryAgain(...);
if (failed)
{
LogErrorToFile(ex);
return dtl_ios_base::THROW_EXCEPTION;
}
else
return dtl_ios_base::SUPPRESS_ERROR; <span class="codeComment">// success ... our superhero
// has saved the day!</span>
}
}
};
</code></pre>
</FONT>
<FONT FACE="Courier New" SIZE=2><P>The above handler instructs the code invoking the handler to still throw the exception if the iterator is in a bad state. In the case that the iterator has only failed and is salvageable, our hero weaves some magic on the DataObj and then reapplies the operation that failed on the iterator. If the retry of the operation succeeded, our hero has saved the day and the handler tells the invoking code that all is well. Otherwise, it is a dark day for the iterator and the invoking code will be told to throw. To tell what personality we would like our IOHandler to take for a given DB_iterator it, we simply invoke it.set_io_handler(), passing in an instance of our handler as the argument. Similarly, it.get_io_handler() will return the current handler for the iterator. Note that you must pass a NULL pointer to this method, e. g.: </P>
</FONT><FONT FACE="Courier New" SIZE=3><PRE> it.get_io_handler((OurHeroicHandler<DataObj, ParamObj> *) NULL);</PRE></FONT>
<FONT FACE="Courier New" SIZE=2><P>Calling the similar members of the DBView<DataObj, ParamObj> template will set and get the default view for newly created iterators that refer to that view. Now you know everything needed to summon our superhero and to let him do his work to save the day!</P>
<FONT FACE="Courier New" SIZE=2><P>Now here's another example that shows the use of a logging handler class to report the exceptions that occur.<BR>
<FONT FACE="Courier New" SIZE=3>
<pre><code><span class="codeComment">// Example Code Using LoggingHandler on a DBView
// test of failed SelValidate() when reading data</span>
void TestBadSelValidate()
{
vector<Example> results;
<span class="codeComment">// construct view
// DBView<Example> is actually DBView<Example,
// DefaultParamObj<Example> > thanks to the default
// argument to the DBView template
// use our bad BCA which references a nonexistent column name in DB_EXAMPLE</span>
DBView<Example>
view("DB_EXAMPLE", BCAExampleObj(),
"WHERE INT_VALUE BETWEEN (?) AND (?) AND "
"STRING_VALUE = (?) OR EXAMPLE_DATE < (?) ORDER BY EXAMPLE_LONG",
BPAExampleObj(), BadSelValidate());
<span class="codeComment">// loop through query results and add them to our vector
// in this loop, read_it.GetLastCount() records read from DB</span>
DBView<Example>::select_iterator read_it = view.begin();
<span class="codeComment">// set parameter values for the WHERE clause in our SQL query</span>
read_it.Params().lowIntValue = 2;
read_it.Params().highIntValue = 8;
read_it.Params().strValue = "Example";
TIMESTAMP_STRUCT paramDate = {2000, 1, 1, 0, 0, 0, 0};
read_it.Params().dateValue = paramDate;
for ( ; read_it != view.end(); read_it++)
{
try
{
<span class="codeComment">// note that the read_iterator::GetLastCount() is incremented in operator++()
// remember that the record is fetched and thus the count incremented
// before operator*() is applied to the read_iterator</span>
cout << "Reading element #" << read_it.GetLastCount() << endl;
cout << "read_it->exampleInt = " << read_it->exampleInt << endl;
cout << "read_it->exampleStr = " << read_it->exampleStr << endl;
results.push_back(*read_it);
}
catch (RootException &ex)
{
cout << "Caught Exception!!!!" << endl;
cout << ex.what() << endl;
}
}
LoggingHandler<Example> handler =
read_it.get_io_handler((LoggingHandler<Example> *) NULL);
typedef LoggingHandler<Example>::LoggedTriple LoggedTriple;
vector<LoggedTriple> errors = handler.GetLog();
for (vector<LoggedTriple>::iterator log_it = errors.begin(); log_it != errors.end();
log_it++)
{
LoggedTriple error = *log_it;
cout << "Error msg = " << error.errmsg << endl;
cout << "Example = " << error.dataObj << endl;
}
}
</code></pre>
</FONT>
</FONT><FONT FACE="Courier New"><H3>Range commit and rollback</H3>
</FONT><FONT FACE="Courier New" SIZE=2><P>You can emulate transactions over ranges of operations using DBConnection::CommitAll() and DBConnection::RollbackAll().</P>
</FONT>
<FONT FACE="Courier New" SIZE=3>
<pre><code><span class="codeComment">// Range Transaction over a DBConnection: Insertion into a DBView </span>
const TIMESTAMP_STRUCT chrysalis = {2002, 4, 3, 0, 0, 0, 0};
const TIMESTAMP_STRUCT mikero = {2001, 11, 2, 0, 0, 0, 0};
const TIMESTAMP_STRUCT victory = {2001, 3, 10, 0, 0, 0, 0};
<span class="codeComment">// this example shows range insert transactions in action</span>
void RangeInsertExample()
{
DBConnection conn;
conn.Connect("UID=example;PWD=example;DSN=example;");
typedef DBView<Example> DBV;
DBV view("DB_EXAMPLE", DefaultBCA<Example>(),
"", DefaultBPA<DefaultParamObj<Example> >(), DefaultSelValidate<Example>(),
DefaultInsValidate<Example>(), conn);
cout << "Examples in view before attempted range insert:" << endl;
copy(view.begin(), view.end(), ostream_iterator<Example>(cout, "\n"));
vector<Example> read_from_DB_before;
copy(view.begin(), view.end(), back_inserter(read_from_DB_before));
<span class="codeComment">// examples that we want to insert into the DB ...
// we want an all or nothing on these guys!</span>
vector<Example> all_or_nothing_examples;
<span class="codeComment">// third element will fail to be inserted, should force rollback</span>
all_or_nothing_examples.push_back(Example(79, "FUBAR", 2.2, 99, mikero));
all_or_nothing_examples.push_back(Example(81, "All Messed Up", 21.09, 75, chrysalis));
all_or_nothing_examples.push_back(Example(85, "Bad Boy", -21.22, 11, victory));
all_or_nothing_examples.push_back(Example(99, "Good One", 77.99, 41, victory));
<span class="codeComment">// must write all the elements to succeed in the transaction</span>
// else we rollback
try {
DBV::insert_iterator write_it = view;
write_it.set_io_handler(AlwaysThrowsHandler<Example>());
for (vector<Example>::iterator ins_it = all_or_nothing_examples.begin();
ins_it != all_or_nothing_examples.end(); ins_it++, write_it++)
{
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -