exceptions and throw inside a class

Pages: 12
I have a need to throw an exception inside a class which I derive from <exxception>

how am I going to do this? gcc 4.7.0 always gives me the error
rnd.h:77:9: error: looser throw specifier for 'virtual CRnd::~CRnd()'
c:\mingw-w32-bin_i686-mingw_20111127\bin\../lib/gcc/i686-w64-mingw32/4.7.0/../../../../i686-w64-mingw32/include/c++/4.7.0/exception:
65:13: error: overriding 'virtual std::exception::~exception() throw ()'
rnd.h:78:28: error: conflicting return type specified for 'virtual const char CRnd::what() const'
c:\mingw-w32-bin_i686-mingw_20111127\bin\../lib/gcc/i686-w64-mingw32/4.7.0/../../../../i686-w64-mingw32/include/c++/4.7.0/exception:
69:25: error: overriding 'virtual const char* std::exception::what() const'
rnd.h:79:20: error: invalid conversion from 'const char*' to 'char' [-fpermissive]

when I try to use the example given on the exceptions page on this site.
my guess is I would have to
throw this;
closed account (zb0S216C)
Don't throw exceptions within a class's constructor or destructor. It's a bad habit.

Wazzak
Don't throw in a destructor.

If there is a problem constructing, you should throw. You must be very careful how you do this, though, so as not to leave any resources dangling.

Hope this helps.
closed account (1yR4jE8b)
There is nothing wrong with throwing exceptions during constructors.

http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.8
I never said I was throwing inside a constructor. (I don't see anything wrong with it either)

I did fix the code however. I was trying to throw inside public methods where an improper condition existed: 2 arguments, mn and mx, where mx<mn.
I was unable to voice this earlier. I just wanted a good example of a program that did something like that. I don't know what to throw.
Last edited on
> I have a need to throw an exception inside a class which I derive from <exxception>
> gcc 4.7.0 always gives me the error
> error: looser throw specifier for 'virtual CRnd::~CRnd()

The destructor of std::exception promises not to throw.
1
2
virtual ~exception() noexcept; // C++11
virtual ~exception() throw(); // C++98 


So your derived class destructor must also have a noexcept or throw() specifier.


> error: conflicting return type specified for 'virtual const char CRnd::what() const

This is because of a typo; the return type of what is a const char*, not a const char.


> I was trying to throw inside public methods where an improper condition existed:
> 2 arguments, mn and mx, where mx<mn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdexcept>
#include <iostream>

struct my_exception : public virtual std::invalid_argument
{
    typedef std::invalid_argument base ;

    my_exception() : base( "invalid argument: mx<mn") {}

    const char* what() const throw() /* noexcept */ /* override */
    { return base::what() ; }

    ~my_exception() throw() /* noexcept */ { /* ... */ }
};

void foo( int mx, int mn )
{
    if( mx < mn ) throw my_exception() ;
}

int main()
{
    try { foo( 20, 30 ) ; }
    catch( const std::exception& e ) { std::cerr << e.what() << '\n' ; }
}
Last edited on
what I have is more like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class CRnd : public std::exception {
    std::tr1::mt19937 gen;//mersenne twister random number generator
    public:
            #if defined(__MINGW32__)||defined(_MSC_VER)||defined(__BORLANDC__)
        uint64_t gettimestamp() {
            SYSTEMTIME st;
            GetSystemTime(&st);
            return
            #if defined(__BORLANDC__)||defined(_MSC_VER)
              (uint64_t(st.wYear)        * 1000i64U * 60i64U * 60i64U * 24i64U * 31i64U * 12i64U)
            + (uint64_t(st.wMonth-1I64U) * 1000i64U * 60i64U * 60i64U * 24i64U * 31i64U)
            + (uint64_t(st.wDay-1i64U)   * 1000i64U * 60i64U * 60i64U * 24i64U)
            + (uint64_t(st.wHour)        * 1000i64U * 60i64U * 60i64U)
            + (uint64_t(st.wMinute)      * 1000i64U * 60i64U)
            + (uint64_t(st.wSecond)      * 1000i64U)
            +  uint64_t(st.wMilliseconds);
            #else
              (uint64_t(st.wYear)        * 1000LLU * 60LLU * 60LLU * 24LLU * 31LLU * 12LLU)
            + (uint64_t(st.wMonth-1LLU)  * 1000LLU * 60LLU * 60LLU * 24LLU * 31LLU)
            + (uint64_t(st.wDay-1LLU)    * 1000LLU * 60LLU * 60LLU * 24LLU)
            + (uint64_t(st.wHour)        * 1000LLU * 60LLU * 60LLU)
            + (uint64_t(st.wMinute)      * 1000LLU * 60LLU)
            + (uint64_t(st.wSecond)      * 1000LLU)
            +  uint64_t(st.wMilliseconds);
            #endif
        }
        #endif
        CRnd() {
            // initialize the generator
            #if defined(__MINGW32__)||defined(_MSC_VER)||defined(__BORLANDC__)
            gen.seed(gettimestamp());
            #else
            gen.seed((unsigned int)time(NULL));
            #endif
            //unif(gen);
        }
        ~CRnd() throw() {}
        const char * what() const throw() {
            return "ERROR: programmer error using class CRnd: mx>mn!\n";
            exit(1);
        }


        uint64_t randomuint64_t() {
            std::tr1::uniform_int<> unifuint64_t(0, 0x7fffffff);
            return unifuint64_t(gen);
        }
        int64_t  randomint64_t()  {
            std::tr1::uniform_int<> unifint64_t(0, 0x7fffffff);
            return unifint64_t(gen);
        }
        uint64_t randomuint64_t(uint64_t mn, uint64_t mx) {
            uint64_t n;
            if (mx < mn || mn > randomuint64_t()) {
                //return 0; //should throw here
                throw this;
            }
            std::tr1::uniform_int<> unifuint64_t(mn, mx);
            /*n=unifuint64_t(gen)+mn;
            while (n+mn > mx) {
                n=unifuint64_t(gen)+mn;
            }
            return n;*/
            return unifuint64_t(gen);
        }
        int64_t  randomint64_t(int64_t mn, int64_t mx)  {
            int64_t n;
            if (mx < mn || mn > randomuint64_t()) {
                //return 0; //should throw here
                throw this;
            }
            std::tr1::uniform_int<> unifint64_t(mn,  mx);
            /*n=unifint64_t(gen)+mn;
            while (n+mn > mx) {
                n=unifint64_t(gen)+mn;
            }
            return n;*/
            return unifint64_t(gen);
        }

        uint64_t randomuint64_t_MAX() {return 0x7fffffff;}//mersenne twister goesn't go any higher.
        int64_t  randomint64_t_MAX()  {return 0x7fffffff;}//mersenne twister goesn't go any higher.
};


this isn't the full code.
it works, but I haven't tested it yet.
I mean it compiles, but I haven't tested it yet.
You don't need to inherit from an exception class to throw one.
I would suggest not inheriting and..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
     uint64_t randomuint64_t(uint64_t mn, uint64_t mx) {
            uint64_t n;
            if (mx < mn || mn > randomuint64_t()) {
                //return 0; //should throw here
                throw std::invalid_argument("ERROR: programmer error using class CRnd: mx<mn!\n");
            }
            std::tr1::uniform_int<> unifuint64_t(mn, mx);
            /*n=unifuint64_t(gen)+mn;
            while (n+mn > mx) {
                n=unifuint64_t(gen)+mn;
            }
            return n;*/
            return unifuint64_t(gen);
        }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
            #if defined(__BORLANDC__)||defined(_MSC_VER)
              (uint64_t(st.wYear)        * 1000i64U * 60i64U * 60i64U * 24i64U * 31i64U * 12i64U)
            + (uint64_t(st.wMonth-1I64U) * 1000i64U * 60i64U * 60i64U * 24i64U * 31i64U)
            + (uint64_t(st.wDay-1i64U)   * 1000i64U * 60i64U * 60i64U * 24i64U)
            + (uint64_t(st.wHour)        * 1000i64U * 60i64U * 60i64U)
            + (uint64_t(st.wMinute)      * 1000i64U * 60i64U)
            + (uint64_t(st.wSecond)      * 1000i64U)
            +  uint64_t(st.wMilliseconds);
            #else
              (uint64_t(st.wYear)        * 1000LLU * 60LLU * 60LLU * 24LLU * 31LLU * 12LLU)
            + (uint64_t(st.wMonth-1LLU)  * 1000LLU * 60LLU * 60LLU * 24LLU * 31LLU)
            + (uint64_t(st.wDay-1LLU)    * 1000LLU * 60LLU * 60LLU * 24LLU)
            + (uint64_t(st.wHour)        * 1000LLU * 60LLU * 60LLU)
            + (uint64_t(st.wMinute)      * 1000LLU * 60LLU)
            + (uint64_t(st.wSecond)      * 1000LLU)
            +  uint64_t(st.wMilliseconds);
            #endif 
This kind of code is really messy.

In + (uint64_t(st.wHour) * 1000LLU the LLU is unnecessary; (uint64_t(st.wHour) * 1000 would do -widening conversions are automatically performed.

Too many magic numbers; foe example in:
(uint64_t(st.wYear) * 1000LLU * 60LLU * 60LLU * 24LLU * 31LLU * 12LLU)

Code with a lot of compile time optioning is not easy to read.

Perhaps, something like this instead:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class CRnd
{
    std::tr1::mt19937 gen ;

    public:
        static uint64_t gettimestamp()
        {
            #if defined(__MINGW32__)||defined(_MSC_VER)||defined(__BORLANDC__)

            SYSTEMTIME st ;
            GetSystemTime( &st ) ;
            
            // simpler to use the facilities provided by the API
            FILETIME ft ;
            SystemTimeToFileTime( &st, &ft ) ;
            uint64_t ts = ( uint64_t(ft.dwHighDateTime) << 32U ) + ft.dwLowDateTime ;
            
            enum { FT_TO_MSEC_RATIO = 10000 } ;
            return ts / FT_TO_MSEC_RATIO ;

            #else

            return std::time(0) ;

            #endif // defined(__MINGW32__)||defined(_MSC_VER)||defined(__BORLANDC__)
        }

        CRnd() : gen( gettimestamp() ) {}

        uint64_t randomuint64_t() { return gen() ; } // ***

        int64_t  randomint64_t()  { return gen() ; } // ***

        uint64_t randomuint64_t( uint64_t mn, uint64_t mx )
        {
            if ( mx<mn || mn>randomuint64_t() )
               throw std::invalid_argument( what() ) ;

            // default is std::tr1::uniform_int<int>   
            std::tr1::uniform_int<uint64_t> unifuint64_t(mn, mx) ; // ***
            return unifuint64_t(gen);
        }

        int64_t  randomint64_t( int64_t mn, int64_t mx )
        {
            if ( mx<mn || mn>randomint64_t() )
               throw std::invalid_argument( what() ) ;
           
            // default is std::tr1::uniform_int<int>   
            std::tr1::uniform_int<uint64_t> unifuint64_t(mn, mx) ; // ***
            return unifuint64_t(gen);
        }

        uint64_t randomuint64_t_MAX() const { return gen.max() ; }  // ***

        int64_t  randomint64_t_MAX() const  { return gen.min() ; } // ***

        const char* what() const // avoid magic constants
        { return "ERROR: programmer error using class CRnd: mx<mn!\n" ; }
};

you would think conversions would be done automatically, but testing with gcc in my case has proved otherwise and resulted in erroneous results.

ALWAYS put in the constant's type specifier when going to something larger than int. otherwise, that number such as 1000 will default to int and stay int. maybe this is a bug in gcc I need to report? I am using gcc 4.7.0. does this in 4.6.2 all the way down as well.

this just seem to work better when they are all the same type. I know what they told you in engineering school, but I find it's just not true in the real world with gcc when doing complicated calculations with things like time conversions, LBA to CHS and such.

I also discovered a bug in uniform_int on gcc.
Last edited on
> you would think conversions would be done automatically,
> but testing with gcc in my case has proved otherwise
> ALWAYS put in the constant's type specifier when going to something larger than int.
> otherwise, that number such as 1000 will default to int and stay int.

Don't be absurd.
Compile this program and run it (variadic templates and long long, so use --std=c++11 or --std=c++0x):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include <string>

template< typename T > std::string type_name( T v)
{
    enum { MAX_LEN = 2047 } ;
    char temp[ MAX_LEN+1 ] ;
    std::size_t sz = MAX_LEN+1 ;
    int status ;
    __cxxabiv1::__cxa_demangle( typeid(v).name(), temp, &sz, &status ) ;
    return status==0 ? temp : "__cxa_demangle error" ;
}

template< typename FIRST, typename SECOND >
void test_it( FIRST f, SECOND s )
{
    std::cout << type_name(f) << " + " << type_name(s) << " => "
              << type_name( f+s ) << '\n' ;
}

template< typename FIRST, typename SECOND, typename... REST >
void test_it( FIRST f, SECOND s, REST... r )
{
    test_it( f, s ) ;
    test_it( s, f ) ;
    test_it( f, r... ) ;
    test_it( s, r... ) ;
}

int main()
{
    test_it( 78, 'b', 9L, 22LL, 31ULL, short(0), L'w' ) ;
}


Output:
int + char => int
char + int => int
int + long => long
long + int => long
int + long long => long long
long long + int => long long
int + unsigned long long => unsigned long long
unsigned long long + int => unsigned long long
int + short => int
short + int => int
int + wchar_t => int
short + wchar_t => int
unsigned long long + short => unsigned long long
short + unsigned long long => unsigned long long
unsigned long long + wchar_t => unsigned long long
...
many more lines...
...

#include <iostream>
#include <tr1/stdint.h>
using namespace std;
int main(void) {
unsigned short cyl=5000, head=5000, sector=5000;
uint64_t n=cyl * head * sector; //result should be
cout<<"result should be 125000000000:="<<n<<endl;
return 0;
}
/*
Wed 03/14/2012 16:10:39.73|C:\prj\test\mingw-w64\conversion|>c
result should be 125000000000:=445948416

Wed 03/14/2012 16:10:46.90|C:\prj\test\mingw-w64\conversion|>
*/


like I said, it doesn't work like you think it should...
this gets even more interesting:

#include <iostream>
#include <tr1/stdint.h>
using namespace std;
int main(void) {
//MAXINT of a short is signed 2^15-1=32767 or unsigned 2^16-1=65535
unsigned short cyl=5000, head=5000, sector=5000;
uint64_t n=cyl * head * sector; //result should be
cout<<"result should be 125000000000:="<<n<<endl;//5000^3

unsigned long long m=cyl*head*sector;
cout<<"result should be 125000000000:="<<m<<endl;//5000^3

//so on the chance that signed long long alone might work,
short a=10000, b=10000, c=10000;
long long o=a*b*c;
cout<<"result should be 1000000000000:="<<o<<endl;//10000^3

return 0;
}
/*
Thu 03/15/2012 15:36:35.84|C:\prj\test\mingw-w64\conversion|>32\c
result should be 125000000000:=445948416
result should be 125000000000:=445948416
result should be 1000000000000:=-727379968

Thu 03/15/2012 15:36:38.75|C:\prj\test\mingw-w64\conversion|>
*/



what I run into ALL THE TIME is not so much problems with multiplying and adding stuff together, they can all be the same type, like above, but I usually want to upconvert them to a larger type to handle the larger result the equation gives.

just like something you might see above. usually it's a bunch of multiples or a bunch of multiplies and adds. there MIGHT be mixed types in there. thanks for the assurance on that. I now know they ARE being converted. but they DON'T get converted on assignments. I have to explicitly convert them before assigning. this is why I use all those ULL's on the constants. it's so I don't get garbage for results like what you see above. I get it in every compiler I use. BC++, VC++, gcc, etc.

I wish it weren't true. I wish someone would fix that and upconvert everything to the type of whatever is being assigned or returned.
Or is there some hidden purpose to that which I am not aware of?
I now know they ARE being converted. but they DON'T get converted on assignments. I have to explicitly convert them before assigning.


Actually, you need to convert them before the calculation. You are getting an automatic conversion on assignment. You only need to convert one of them to get automatic widening conversions on the rest.

1
2
unsigned short cyl = 5000, head = 5000, sector = 5000 ;
uint64_t n = static_cast<uint64_t>(cyl)*head *sector ;


In general it is impossible for the compiler to know whether a calculation is going to result in overflow before it happens. Requiring them to convert it to larger type when this is going to occur isn't really feasible without additional overhead.

The type you store the result in isn't really relevant to the calculation. Are you storing it in a long because you're using it in calculations that require that storage later? Do you want the overflow behavior? (Obviously you do if you're doing the calculation with types that result in it!)

double n = cyl*head*sector ;

should we convert everything to double first?

Or maybe we should convert everything to uint64_t first, then convert it to double on assignment? Or maybe we should leave them as they are and just convert to double on assignment.
Last edited on
the compiler should know to convert everything at least to the return or assign type I should think. the data type should not be hard to detect by the parser.
I hope someone makes that a feature someday.
for instance,
1
2
3
4
5
6
int = short*char*short; 
//should be compiled as
int=int*int*int;
return short*char*short; 
//that returns an int should be compiled as
return int*int*int;

this is what I mean. as simple as that. I should have given examples.

*If* I am assigning an int but the calculations require an int64_t temporarily (which is what I think you are describing), then I already know I need to do the conversions myself manually:
 
int=static_cast<int64_t>(char)*static_cast<int64_t>(short)*static_cast<int64_t>(int);


cool! thanks!

this is a side issue,
what I need to know to fix this is, HOW do you detect whether a given template type is unsigned or signed? this will determine the data type I use for calculations.
1
2
3
4
5
6
int = short*char*short; 
//should be compiled as
int=int*int*int;
return short*char*short; 
//that returns an int should be compiled as
return int*int*int;


I don't really agree with your "shoulds." the compiler should do what I tell it to do, and if I want overflow behavior then I should get it, regardless of how I store the result.

Unfortunately, I wouldn't be able to accomplish that your way without introducing a layer of indirection via assignment.

int a = int*in*int ;
long whereIreallywantedit = a ;

Additional difficulties are encountered if you consider overloaded functions/operators that take different flavors of int. How do you resolve which to call with function(int*int*int)?

As for your aside, google std::enable_if and std::is_unsigned if your compiler supports them. Otherwise you'll probably have to use something from boost. I'm not much with the template trickery.
Last edited on
the compiler is arbitrarily stopping upconversion at int even though I fee it shorts and want an int64_t on assignment. it is not taking the next steps in upconverting.

upconverting should be done like a while loop. like the list of conversions you had above. they are stopping short at *1* conversion. I am saying it shouldn't stop converting until it reaches the target data type (or hits a conversion error), which is:
- function argument
- left hand side of assignment op
- generally reaching the target of the expression evaluation
- returning something from a function
- there may be other examples, but they still boil down to the same thing

one possible implementation might be like this algorithm (for usage in parser sort of):
1
2
3
4
5
6
7
8
9
10
11
12
COMMENT: find highest data type in expression
highest_data_type=NULL
for every data_type in expression_tree do
    if (NULL==highest_data_type) then
        if (data_type==integer and data_type.width > highest_data_type.width) then
            highest_data_type=data_type
        endif
    endif
done
COMMENT: after this,it's just a matter of walking the syntax tree again 
COMMENT: and generating code that upconverts any data_type that is
COMMENT: data_type.width < highest_data_type.width 


the for is actually a syntax tree walk, so it's recursive...

do you mean
function(int int*int*int)? it should leave them as an int. handling overflow should be the programmer's responsibility as usual.
function(long int*int*int) it should convert them to a long or produce some sort of data type error.
function(int64_t int*int*int) it should upconvert the ints to int64_ts as part of the eval.

what sense would it make to not fully upconvert expressions?
the compiler already piecemeal-does it now.
I don't know if the actual translation would be table driven or not in order to check for conversion type errors, that would be a simple matter to put in a compiler.
having such a compiler fix would sure would eliminate a lot of confusion.
ok, overflow behavior, could be argued for. I got it. ugh.
isn't there some way to induce overflow behavior artificially?
like using & for instance like everybody else? or does this not do the same thing?
> isn't there some way to induce overflow behavior artificially?

Yes, there is - a cast.
double dbl = double(1000000000) * 1000000000 * 1000000000 ;
Pages: 12