Re: [AD] 4.3 error handling

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


On Mon, 2006-06-05 at 16:28 -0700, Chris wrote:
>
> Besides, with the way that code is, you can use any of the three at any time. 
> Don't want to use the try/catch defines? Then just call 
> al_error_disable_throws() once at the beginning of your program and don't 
> think anything of them (just be sure to check for failure return codes). Too 
> lazy to do your own error checking? Leave throws enabled and don't worry 
> about the try and catch defines.

True. We could even additionally provide a callback (NULL by default),
but if you set one with al_set_error_callback, then it is gets called
(and no longjmp-exception is raised).

> There's many times I wish I could just execute a string of commands and not 
> have to check the return code of each one individually. But I'd still like to 
> be able to handle the errors manually myself, without having to use a 
> seperate callback function. Something like:
> 
> al_error_try {
>    audio_driver = al_audio_init_driver(NULL);
> 
>    main_voice = al_voice_create(audio_driver, 44100, AL_AUDIO_16_BIT_INT, 
> AL_AUDIO_4_CH, 0);
>    al_voice_get_long(main_voice, AL_AUDIO_FREQUENCY, &voice_freq);
>    al_voice_get_enum(main_voice, AL_AUDIO_CHANNELS, &channels);
> 
>    main_mixer = al_mixer_create(voice_freq, AL_AUDIO_32_BIT_FLOAT, channels);
> 
>    al_voice_attach_mixer(main_voice, main_mixer);
> }
> al_error_catch {
>    AL_ERROR code = al_get_last_error();
>    show_my_error("Error initializing sound!\n%s\n", al_get_error_string());
>    if(no_sound_is_fatal)
>       al_error_throw(code);
> }

Yeah, this example makes more sense. Still, it won't occur often - an
initialization as complicated as above only should be necessary if you
do something special. If you just want to use al_play_sample, then an
al_init_audio() should be all that's needed (and therefore a simple if
would be less code than the al_error_try {} al_error_catch {}).

Actually, I would like this:

al_init(AL_INIT_ALL)

with:

#define AL_INIT_ALL (AL_INIT_KEYBOARD | AL_INIT_TIMER | AL_INIT_MOUSE |
AL_INIT_SOUND)

Anyway, I better try to get back on topic..

> 
> > And actual C++ users wouldn't benefit from setjmp/longjmp and those
> > #defines, since real exceptions should be raised for C++. I'd say, the
> > best idea for C++ is a wrapper, where al_sound::init would raise an
> > exception.
> 
> A C++ wrapper could still raise real exceptions:
> 
> al_audio::init()
> {
>    al_error_try {
>       driver = al_audio_init_driver(NULL);
>    }
>    al_error_catch {
>       throw al_get_last_error();
>    }
> }
> 
> That also has the benefit of leaving the real throwing in C++, so you needn't 
> have to worry about C++ exceptions through C.

Interesting point. So for actual C++ user code (which probably will be
significantly more than C) this would work quite nicely.

> > The TLS error code and NULL return is all that seems needed to me for C.
> 
> Then that's all you have to use. Just call al_error_disable_throws() and do it 
> the C way.

Well, having to call al_error_disable_throws() for each program would
not look favorably for the API to me. Could always discuss later what
should be default. If it's agreed to have this as default, they I would
try to use it myself though.

> > For the #1 approach, we could have a flag to al_init, like
> > al_init(ABORT_ON_ERROR). And then Allegro would automatically abort on
> > error - just like in your code.
> 
> My code has the benefit of being able to turn such aborts on and off at will. 
> Like, say, if you want to init video without doing specific error checking, 
> but then check the errors while initializing sound. It's also transparent, so 
> you could put in a single try/catch around the whole initialization section 
> without having to change the initialization code itself.

Yes. In a way it is nice. I just have learned in the past to dislike any
hackish (using setjmp/longjmp always is hackish, and #defines like this
are so even more) features in C code.

> 
> That said, however, I do see a /small/ problem with my code. You wouldn't be 
> able to jump out from the try block (via goto, a caught exception, another 
> longjmp, etc) and continue using Allegro code, or else the code will throw to 
> the wrong setjmp point later on. Though I don't think it's that big of a deal 
> to simply note that you can't do that, if something like this is used. You 
> could always throw a custom allegro error in the try block and check for it 
> in the catch block, where you can jump out however you want.

Yes, it shouldn't be a problem normally. A big danger I see is from
something like:

int my_init(void)
{
    al_error_try {
        ....
        if (al_audio_init) return -1; // user copy&pasted old init code
        ....
    }
    al_error_catch {
        return -1;
    }
    return 0;
}

Or even:

int my_init(void)
{
    int i;
    for (i = 0; i < modes_count; i++) {
        al_error_try {
            if (!al_set_gfx_mode(mode[i]))
                break;
        }
        al_error_catch {
            continue;
        }
    }
}

Also this will go wrong in a bad way:

int my_init(void)
{
    al_error_catch {
        al_audio_init();
    }
    al_error_try {

    }
    return 0;
}

Hm, or:

int my_init(void)
{
    if (want_sound) {
        al_error_try
            al_audio_init();
        else
            printf("If you code python this one will happen to you :P\n");
    }
}

In all of those, the C compiler will not even warn.

Something else which might be a problem (probably it's not) - how well
will setjmp/longjmp work when using other languages? E.g. if the
my_init() function really is called from Python. Well, only way to find
out would be to test it.

So, my opinion:
Pro:
    - Elegant syntax, almost works like C++ or Python exceptions.
    - Could be switched off by default.
    - Should also work if user code is in C++.
Con:
    - Easy to make very non-obvious mistakes, as is the problem with
many macros trying to add language features to C which it simply does
not have.
    - C++ users can as well use a proper C++ wrapper, which can use real
exceptions.

-- 
Elias Pschernig





Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/