C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

unsigned int std::uncaught_exceptions()
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Andrei Alexandrescu (See
Guest





PostPosted: Thu Jul 29, 2004 10:29 am    Post subject: unsigned int std::uncaught_exceptions() Reply with quote



As Herb wrote in GotW and in Exceptional C++, std::uncaught_exception() is
not a useful function because it can't be used to detect whether or not an
exception was engendered in a particular scope.

What I think would be good to have would be kind of an exception stack; that
could be implemented but it's not easy.

The next good thing to have is a simple unsigned int
std::uncaught_exceptions() that tells how many exceptions are being thrown
right now; that way, a class' constructor can store the current value
returned by std::uncaught_exceptions, and then the destructor can compare
that value with the current one. If the current one is greater than the old
one, then the variable's scope is being exited via an exception.

So I'd like to ask:

1. Would others find such a function useful?

2. Is this function easy to implement?


Andrei



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Alf P. Steinbach
Guest





PostPosted: Thu Jul 29, 2004 3:18 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote



* Andrei Alexandrescu (See Website for Email):
Quote:

The next good thing to have is a simple unsigned int
std::uncaught_exceptions() that tells how many exceptions are being thrown
right now; that way, a class' constructor can store the current value
returned by std::uncaught_exceptions, and then the destructor can compare
that value with the current one. If the current one is greater than the old
one, then the variable's scope is being exited via an exception.

So I'd like to ask:

1. Would others find such a function useful?

It would be interesting to know what _a_ usage could be.

Say a destructor encounters a problem.

If the destructor is not invoked by exception-caused stack unwinding,
then it could conceivably report the problem via an exception.

But if it is invoked that way, then it would have to use some
alternative.

And given that this alternative is built into the destructor anyway,
why not use that in both cases?

Quoting from the FAQ (17.3):
<quote>
The easy way to prevent this is never throw an exception from a destructor. But
if you really want to be clever, you can say never throw an exception from a
destructor while processing another exception. But in this second case, you're
in a difficult situation: the destructor itself needs code to handle both
throwing an exception and doing "something else", and the caller has no
guarantees as to what might happen when the destructor detects an error (it
might throw an exception, it might do "something else"). So the whole solution
is harder to write. So the easy thing to do is always do "something else". That
is, never throw an exception from a destructor.

Of course the word never should be "in quotes" since there is always some
situation somewhere where the rule won't hold. But certainly at least 99% of the
time this is a good rule of thumb.
</quote>



Quote:
2. Is this function easy to implement?

I can see no reason why it shouldn't be since the runtime keeps track of this
information anyway, but I'm no compiler writer.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alexander Terekhov
Guest





PostPosted: Thu Jul 29, 2004 3:19 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote




"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
Quote:
that value with the current one. If the current one is greater than the old
one, then the variable's scope is being exited via an exception.

Extending C++ to allow destructors (and pre-destructos, of course)
to have an optional bool "is_unwinding" argument is probably much
better. std::expected_exception<>() just ought to become standard
as well. AFAIK, David Abrahams loves it. ;-)

regards,
alexander.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Chris Uzdavinis
Guest





PostPosted: Thu Jul 29, 2004 9:07 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

Quote:
The next good thing to have is a simple unsigned int
std::uncaught_exceptions() that tells how many exceptions are being thrown
right now;

So I'd like to ask:

1. Would others find such a function useful?

Seems like it could be useful, though probably few would actually use
it. (I've seen your talk on injecting context info into the exception
object... and see that that idiom may benefit. I don't know of many
other ways this would be used, though.)

Once you consider threads, a few minor problems arise. Clearly the
value would have to be in thread-specific-storage. However, the way
you describe using this (storing the initial value in an object's
constructor, and comparing with that to the current value in the
destructor) may not work if your object is used in different threads
as the values for different stacks would be reported. If you're
careful, I don't think this would be a real problem though. Just
always declare the object on the stack, and never copy it or pass its
address around, and threads won't be a problem.

Quote:
2. Is this function easy to implement?

It seems like it would be straight forward. After completing the
evaluation of the object to be thrown, increment this number. After
completing initialization of the exception-declaration in the matching
handler, decrement the number. Then std::uncaught_exception could be
rewritten in terms of this number very easily.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andrei Alexandrescu (See
Guest





PostPosted: Thu Jul 29, 2004 9:16 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

"Alexander Terekhov" <terekhov (AT) web (DOT) de> wrote

Quote:
"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
that value with the current one. If the current one is greater than the
old
one, then the variable's scope is being exited via an exception.

Extending C++ to allow destructors (and pre-destructos, of course)
to have an optional bool "is_unwinding" argument is probably much
better.

That's cool and would perfectly fit what I was thinking of doing - planting
objects on the stack that track the "health" of a system and log exceptional
behavior.

In my current framework, I can do that only if the state of the system is
initially "healthy". The logger object's constructor asserts that
std::uncaught_exception is false, and the destructor tests whether
std::uncaught_exception is true. That's the only way in which you can get
sensible information.

I have a macro EXECUTION_CONTEXT that can be used like this:

void UpdateDatabase() {
EXECUTION_CONTEXT("Updating Database");
... code ...
}

Now if an exception is thrown from within the function, the string will be
properly logged and nice error messages can be created, e.g.:

<<
Error: User's spouse cut access to funds
While: Performing ATM transaction at Fry's
While: Withdrawing funds
While: Updating Database
Quote:


The "While" lines reflect the salient places in the execution stack (where
the system authors planted EXECUTION_CONTEXTs). There is an increasing level
of detail. Without execution contexts, only the throw and catch sites can
issue messages:

<<
Error: User's spouse cut access to funds
While: Updating Database
Quote:


which gives a less informative error message (the lowest-level problem and
the highest-level context) while losing important information about the
dynamic trace that generated the exception.

Quote:
std::expected_exception<>() just ought to become standard
as well. AFAIK, David Abrahams loves it. Wink

For those as lazy as my usual self to google around, here's a link:

http://lists.boost.org/MailArchives/boost/msg53429.php

Not that I understood a lot Surprised). What's that?


Andrei



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Maxim Yegorushkin
Guest





PostPosted: Thu Jul 29, 2004 9:19 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

Andrei Alexandrescu (See Website for Email) <SeeWebsiteForEmail (AT) moderncppdesign (DOT) com> wrote:

Quote:
What I think would be good to have would be kind of an exception stack; that
could be implemented but it's not easy.
The next good thing to have is a simple unsigned int
std::uncaught_exceptions() that tells how many exceptions are being thrown
right now; ...

IOW, you propose that throwing an exception during stack unwinding should not cause terminate(), rather it should push the active exception in some kind of a stack, don't you?

This leads to questions as to whether the stack unwinding process should finish its job (invoking destructors) before the new exception is thrown or be postponed before the new exception is handled; or where the execution must continue after handling the active exception and popping the previous one from the stack; or which catch() blocks should be tryed for the previous exception - the next blocks or the same blocks which were tryed for the just handled exception?

Could you please elaborate?

--
Maxim Yegorushkin

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andrei Alexandrescu (See
Guest





PostPosted: Fri Jul 30, 2004 9:52 am    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote

Quote:
Andrei Alexandrescu (See Website for Email)
[email]SeeWebsiteForEmail (AT) moderncppdesign (DOT) com[/email]> wrote:

What I think would be good to have would be kind of an exception stack;
that
could be implemented but it's not easy.
The next good thing to have is a simple unsigned int
std::uncaught_exceptions() that tells how many exceptions are being
thrown
right now; ...

IOW, you propose that throwing an exception during stack unwinding should
not cause terminate(), rather it should push the active exception in some

kind of a stack, don't you?

No. The model would be the same. I was just thinking that some access to the
exceptions that could possibly be rethrown would be nice. Consider:

try {
struct A {
~A() {
try {
B obj;
throw Exception2();
}
catch (...) {
}
}
} obj;
throw Exception1();
}
catch (...) {
}

When Exception1 is being thrown, A's destructor is invoked. Exception1 is
active. But A's destructor issues another try, so now Exception2 is active
on top of Exception1. I was saying that it would be nice to have access to
both, say, inside B's destructor.

Quote:
This leads to questions as to whether the stack unwinding process should
finish its job (invoking destructors) before the new exception is thrown or

be postponed before the new exception is handled; or where the execution
must continue after handling the active exception and popping the previous
one from the stack; or which catch() blocks should be tryed for the previous
exception - the next blocks or the same blocks which were tryed for the just
handled exception?
Quote:

Could you please elaborate?

Wow, my head is spinning Surprised). I just meant the above, no more. The model
wouldn't change, just access to what's going on would improve.


Andrei



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alf P. Steinbach
Guest





PostPosted: Fri Jul 30, 2004 9:53 am    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

* Andrei Alexandrescu (See Website for Email):
Quote:

That's cool and would perfectly fit what I was thinking of doing - planting
objects on the stack that track the "health" of a system and log exceptional
behavior.

In my current framework, I can do that only if the state of the system is
initially "healthy". The logger object's constructor asserts that
std::uncaught_exception is false, and the destructor tests whether
std::uncaught_exception is true. That's the only way in which you can get
sensible information.

I have a macro EXECUTION_CONTEXT that can be used like this:

void UpdateDatabase() {
EXECUTION_CONTEXT("Updating Database");
... code ...
}

Now if an exception is thrown from within the function, the string will be
properly logged and nice error messages can be created, e.g.:


Error: User's spouse cut access to funds
While: Performing ATM transaction at Fry's
While: Withdrawing funds
While: Updating Database


The "While" lines reflect the salient places in the execution stack (where
the system authors planted EXECUTION_CONTEXTs). There is an increasing level
of detail. Without execution contexts, only the throw and catch sites can
issue messages:


Error: User's spouse cut access to funds
While: Updating Database


which gives a less informative error message (the lowest-level problem and
the highest-level context) while losing important information about the
dynamic trace that generated the exception.

I gather that the problem might be that this doesn't work well in code
called from destructors, nor with a certain very popular compiler...

One solution within the current language and lib is to restrict code that would
like to track exceptions to Single Normal Exit behavior.

Then all that's needed is a statement before the end of the block, that
informs the tracker object that the block completed normally (otherwise
an exception can be assumed).

Hth.

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alexander Terekhov
Guest





PostPosted: Fri Jul 30, 2004 2:53 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote


Maxim Yegorushkin wrote:
[...]
Quote:
Could you please elaborate?

Try

http://groups.google.com/groups?selm=3CBC5F22.76708D0%40web.de
(Subject: Re: Alexandrescu on error handling at ACCU conference 2002)

I mean:

---
throw 0
throw 1
throw 2
throw 3
throw 4
throw 5
throw 6
throw 7
throw 8
throw 9
Okay... ENOUGH active exceptions! Wink
caught 9
caught 8
caught 7
caught 6
caught 5
caught 4
caught 3
caught 2
caught 1
caught 0
---

;-)

regards,
alexander.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alexander Terekhov
Guest





PostPosted: Fri Jul 30, 2004 2:55 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote


"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
Quote:
http://lists.boost.org/MailArchives/boost/msg53429.php

Not that I understood a lot Surprised). What's that?

Not going into details, it's mostly about how to fix exceptions
specifications and make it safe to call fclose() from C++ {pre}
destructors on a POSIX threaded system without wasting cycles on
cancel_off_guard a la

http://www.terekhov.de/DESIGN-futex-CV.cpp

---
~futex_condvar() {
mutex::guard guard(m_mutex);
assert(m_waiters[0] == m_wakeups);
while (m_waiters[0]) {
int ftx = m_futex = EOC();
mutex::release_guard release_guard(guard);
cancel_off_guard no_cancel;
^^^^^^^^^^^^^^^^^^^^^^^^^^
m_futex.wait(ftx);
}
}
---

Good old two-phase EH (also known as "condition handling" on MVS
[z/OS] and [Open]VMS, but without "restart" semantics Wink ).

http://groups.google.com/groups?threadm=3EB82EA0.F40E66C4%40web.de
(Subject: __attribute__((cleanup(function)) versus try/finally)

regards,
alexander.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alexander Terekhov
Guest





PostPosted: Fri Jul 30, 2004 3:26 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote


"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
Quote:
Wow, my head is spinning Surprised). I just meant the above, no more. The model
wouldn't change, just access to what's going on would improve.

Yeah.

1. deprecate uncaught_exception()

2. mandate 2-phase EH and fix ES

3. size_t exception_scope() retuns 0 (no exceptions active) or scope
number

4. T * active_exception<T>(size_t scope = 0)" (scope zero means
current -- in effect, exception_scope()), works with void.

5. bool exception_caught<T>(T *) to check whether it is caught
e.g. if (std::exception_caught(std::active_exception<void>()))
we're inside catch handler.

or something like that.

regards,
alexander.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alexander Terekhov
Guest





PostPosted: Fri Jul 30, 2004 3:26 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote


"Andrei Alexandrescu (See Website for Email)" wrote:
[...]
Quote:
Extending C++ to allow destructors (and pre-destructos, of course)
to have an optional bool "is_unwinding" argument is probably much
better.

That's cool and would perfectly fit what I was thinking of doing - planting
objects on the stack
^^^^^^^^^^^^^^^^^^^^


< dreams on, syntax/keywords aside >

template< context >
struct _Uninit_fill_cleanup {

typeof(context._First) m_Next;

_Uninit_fill_cleanup() :
m_Next(context._First) {
}

~_Uninit_fill_cleanup(bool unwinding) {
if (unwinding)
for (; context.m_First != m_Next; ++m_Next)
_Destroy(&*m_Next);
}

};

template< class _FwdIt, class _Tval >
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val) {
_Uninit_fill_cleanup< context > _Cleanup;
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}

<dreams off>

Or things like that. Oder?

regards,
alexander.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Sergey P. Derevyago
Guest





PostPosted: Fri Jul 30, 2004 3:35 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

"Andrei Alexandrescu (See Website for Email)" wrote:
Quote:
As Herb wrote in GotW and in Exceptional C++, std::uncaught_exception() is
not a useful function because it can't be used to detect whether or not an
exception was engendered in a particular scope.

IMHO std::uncaught_exception() is useless by definition rather than due its

return type.
The solution is destructor with in_stack_unwinding parameter:

class A {
// ...
~A(bool in_stack_unwinding) {
if (in_stack_unwinding) { /* ... */ }
else { /* ... */ }
}
};

Please refer to my old posting
http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com

And moreover, if all exceptions have common base class (virtual base,
actually) life becomes more interesting:

class A {
// ...
~A(const VBException* pe) {
if (pe) { /* use pe */ }
else { /* no exceptions */ }
}
};

Quote:
What I think would be good to have would be kind of an exception stack; that
could be implemented but it's not easy.

An exception stack is already implemented in all recent C++ compilers, we can

use it right now.

For example, my Russian "Interfaces and Messages" tutorial
http://ders.angen.net/cpp3/intmsg/intmsg.html suggests to throw exceptions as
sh_ptr<CException>. That is the object is being thrown is always
sh_ptr<CException>.

So the following code:
-----------------------------------8<-----------------------------------
#include #include <vector>
#include "cexception.hpp"

using namespace std;

class ExampleCException : public CException { // CException as a base
protected:
ExampleCException(const FileLine& loc, const std::string& msg,
sh_ptr<CException> nest) : CException(loc, msg, nest) {}

public:
friend sh_ptr<CException> newExampleCException(const FileLine& loc,
const std::string& msg, sh_ptr<CException> nest=sh_ptr<CException>());

virtual std::string getClassName() const { return "ExampleCException"; }
};

inline sh_ptr<CException> newExampleCException(const CException::FileLine&
loc,
const std::string& msg, sh_ptr<CException> nest)
{
#ifndef DERS_RETHROW_BUG // usual implementation
return sh_ptr<CException>(new ExampleCException(loc, msg, nest));
#else // for broken compilers
return CException::current=sh_ptr<CException>(new ExampleCException(loc, msg,
nest));
#endif
}

int i;

void g()
{
try {
switch (i) {
case 1: throw newExampleCException(_FLINE_, "Hello from g()");
case 2: {
vector<int> v;
v.at(0);
}
case 3: throw 3;
}
}
catch (...) {
throw newCException(_FLINE_, "Problems in g()", toCException(_FLINE_));
}
}

void f()
{
try { g(); }
catch (...) {
throw newCException(_FLINE_, "Problems in f()", toCException(_FLINE_));
}
}

int main()
{
for (i=1; i<=4; i++) {
try {
if (i<4) f();
else throw 4;
}
catch (...) {
printf("tException #%d:n%s", i,
toCException(_FLINE_)->toStringAll().c_str());
}
}
}
-----------------------------------8<-----------------------------------
produces the following output:

Exception #1:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
ExampleCException [ceexample.cpp:64]: Hello from g()
Exception #2:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
STDExternalCException [ceexample.cpp:81], typeName="St12out_of_range",
message="vector"
Exception #3:
CException [ceexample.cpp:90]: Problems in f()
CException [ceexample.cpp:81]: Problems in g()
UnknownExternalCException [ceexample.cpp:81], typeName="unknown",
message="Unknown exception"
Exception #4:
UnknownExternalCException [ceexample.cpp:107], typeName="unknown",
message="Unknown exception"

The code is available here: http://ders.angen.net/cpp3/intmsg/code.zip

Note, only one catch(...) per function can be used.
And old constructions like this:

void f()
{
try { g(); }
catch (const ExampleCException&) {} // ignore ExampleCException
catch (...) { throw; } // rethrow all the others
}

can be rewritten in the following way:

void f()
{
try { g(); }
catch (...) {
sh_ptr
if (ce->is<ExampleCException>()) {} // ignore ExampleCException
else { throw; } // rethrow all the others
}
}

--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Herb Sutter
Guest





PostPosted: Sat Jul 31, 2004 3:47 am    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote

On 30 Jul 2004 11:35:19 -0400, "Sergey P. Derevyago"
<non-existent (AT) iobox (DOT) com> wrote:
Quote:
IMHO std::uncaught_exception() is useless by definition rather than due its
return type.
The solution is destructor with in_stack_unwinding parameter:

class A {
// ...
~A(bool in_stack_unwinding) {
if (in_stack_unwinding) { /* ... */ }
else { /* ... */ }
}
};

Please refer to my old posting
http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com

And see Item 19 in More Exceptional C++ for why this has the same problems
and why this particular example doesn't actually do what you think (for
most motivations to use this idiom).

Herb

---
Herb Sutter (www.gotw.ca)

Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Visual C++ architect, Microsoft (www.gotw.ca/microsoft)

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alexander Terekhov
Guest





PostPosted: Sat Jul 31, 2004 6:48 pm    Post subject: Re: unsigned int std::uncaught_exceptions() Reply with quote


Herb Sutter wrote:
[...]
Quote:
~A(bool in_stack_unwinding) {
if (in_stack_unwinding) { /* ... */ }
else { /* ... */ }
}
};

Please refer to my old posting
http://groups.google.com/groups?selm=3BC7214E.CDD9CF8E%40iobox.com

And see Item 19 in More Exceptional C++ for why this has the same problems
and why this particular example doesn't actually do what you think (for
most motivations to use this idiom).

Nobody has said anything about throwing destructors. Destructors
shall have implicit throw()-nothing ES ("fixed edition") by default
imposed on them. And it wouldn't make {pre}~T(bool) less useful
(for most motivations to use this idiom Wink ).

regards,
alexander.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.