Exception handling capabilities
Exception handling capabilities
I'm developing a small C++ library that heavily uses inheritance and polymorphinsm, and depends upon exceptions implementation. I'm not using std in any way (i'm not satisfied of it on certain technical and license aspects) but a quick look into some source code made me aim my attention on -fno-exceptions flag (that -if i understood well- makes code workaround the lack of hw exceptions handling background with a serie of if-like structures). Speaking of PSP-specific code, i noticed -fno-exceptions is ALWAYS set in all sources i inspected. Why? And: is PSP technically capable of handling exceptions "the classical way"??
Thanks for your attention
jean
Thanks for your attention
jean
the lack of hw exceptions ?
no, -fno-exceptions option has nothing about hardware exceptions. This is merely two different definitions indeed. c++ exceptions are used through "try/catch".
if I'm not wrong, to have c++ exception handling (with RTTI for dynamic_cast as well), you need to provide specifically some files with run-time functions to handle c++ exceptions properly. I guess we use -fno-exceptions because those files are probably not adapted so they could work on PSP.
To be frank, I'm not sure having -fno-exception(s) would prevent you from using "try/catch" (using for instance a simple setjmp/longjmp version) because I never tried so
no, -fno-exceptions option has nothing about hardware exceptions. This is merely two different definitions indeed. c++ exceptions are used through "try/catch".
if I'm not wrong, to have c++ exception handling (with RTTI for dynamic_cast as well), you need to provide specifically some files with run-time functions to handle c++ exceptions properly. I guess we use -fno-exceptions because those files are probably not adapted so they could work on PSP.
To be frank, I'm not sure having -fno-exception(s) would prevent you from using "try/catch" (using for instance a simple setjmp/longjmp version) because I never tried so
sorry, that "hw" before "exception" was not intended...it escaped from my fingers :) !! (i was to mean the "traditional" built-in way of handling exceptions) In fact the only thing i care is performance when making heavy use of software try..catch that surely has nothing to do with hw exception like a cpu issued "div by zero" (even if visual c++ seems to merge the two things in a weird manner...) Are you basically saying "if you don't try, you'll never know" ?
kinda :)jean wrote:Are you basically saying "if you don't try, you'll never know" ?
I'm not sure a lot of us were paying any attention about -fno-exceptions -no-rtti and were using them because they were already set in template or sample Makefiles.
EDIT:
"try/catch" are accepted when compiled but there is no special generated code to handle c++ exception (as if they were comments). However, "throw" is refused : "error: exception handling disabled, use -fexceptions to enable"
actually generally rtti slows the exe and makes it fatter. And the cases in which you really need them are rather rare.I'm not sure a lot of us were paying any attention about -fno-exceptions -no-rtti and were using them because they were already set in template or sample Makefiles.
and even if try/catch is more usefull, unfortunatly it makes the code unreadable (IMHO). That's probably why nobody never change those options.
--pspZorba--
NO to K1.5 !
NO to K1.5 !
Any success with exception handling? I'm trying to get it work but aparently it just hangs, here's the code:
Am i doing something wrong?
Code: Select all
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
/* Define printf, just to make typing easier */
#define printf pspDebugScreenPrintf
/* Define the module info section */
PSP_MODULE_INFO("cppException", 0, 1, 1);
/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main (void)
{
pspDebugScreenInit();
SceCtrlData pad;
try
{
throw myex;
}
catch (exception& e)
{
printf("%s\n", e.what());
}
printf("\nPress X to quit.\n");
for (;;)
{
sceCtrlReadBufferPositive(&pad, 1);
if (pad.Buttons & PSP_CTRL_CROSS)
break;
}
sceKernelExitGame();
return 0;
}
Since then, i used exceptions extensively and without issues... Don't know, maybe your makefile... let me look into an old project of mine....
makefile:
i specified my own type of base exception, never inherited from std::exception
exceptions.hpp:
Anyhow, where does the code hang? You can easily track the exact point setting some printf(); around: if it does hang on throw, try to write mujltiple catch statements ending with the (...) one just to make that code not hang, then you can dig the reason why you con't catch that type.
makefile:
Code: Select all
TARGET = SERIAL_ROBOT
OBJS = main.o Command.o GetMotorCommand.o GetSensorCommand.o MotorPacket.o Packet.o PacketFactory.o RcvGetMotorPacket.o RcvGetSensorPacket.o SerialComm.o serialPacketReaderMachine.o SetMotorCommand.o SndGetMotorPacket.o SndGetSensorPacket.o SndSetMotorPacket.o sioDriver.o callbacks.o MotorController.o
BUILD_PRX=1
PSP_FW_VERSION=390
INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fexceptions -fno-rtti -D "__PSP"
ASFLAGS = $(CFLAGS)
LIBS = -lstdc++
LIBDIR =
LDFLAGS =
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = SERIAL_ROBOT
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
exceptions.hpp:
Code: Select all
#ifndef __Exceptions_H__
#define __Exceptions_H__
/// \brief RException class.
class RException
{
};
/// \brief CommInitException class.
class CommInitException: public RException
{
};
/// \brief WrongMachineStateException class.
class WrongMachineStateException: public RException
{
};
// ...etc...
#endif // __Exceptions_H__
Code: Select all
try
{
// throwing code
}
catch (specializedException1 e)
{
// ...
}
catch (specializedException2 e)
{
// ...
}
catch (baseException e)
{
// ...
}
catch (...) // you can never know what can happen...
{
// ...
}
that was what i thought hlide, and started to add a simple threading implementation to gcc based on an old haiku-os implementation that only tels gcc that this device can handle mutexes, however when i throw an exception the mutex locks everything (i guess i got it all wrong from the gcc barely existing docs).
I've reverted my patch and exceptions work out of the box like jean says both with base user defined exception or using the sdt::exception one.
I've tested with gcc 4.3.3 and newlib 1.16 / gcc 4.3.3 and newlib 1.17
I've reverted my patch and exceptions work out of the box like jean says both with base user defined exception or using the sdt::exception one.
I've tested with gcc 4.3.3 and newlib 1.16 / gcc 4.3.3 and newlib 1.17
This is my test cpp file:
and this is my makefile:
the the usual, make, copy eboot and run :) on the makefile the only difference is that i define -fexceptions instead of -fno-exceptions on the CXXFLAGS variable, and obviously link againts -lstdc++ because this is a cpp sample.
PS: I'm adding this sample to the SDK in my builds.
Code: Select all
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
/* Define printf, just to make typing easier */
#define printf pspDebugScreenPrintf
/* Define the module info section */
PSP_MODULE_INFO("cppException", 0, 1, 1);
/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | HREAD_ATTR_VFPU);
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main (void)
{
pspDebugScreenInit();
SceCtrlData pad;
try
{
throw myex;
}
catch (exception& e)
{
printf("%s\n", e.what());
}
printf("\nPress X to quit.\n");
for (;;)
{
sceCtrlReadBufferPositive(&pad, 1);
if (pad.Buttons & PSP_CTRL_CROSS)
break;
}
sceKernelExitGame();
return 0;
}
Code: Select all
TARGET = CppException
OBJS = main.o
LIBS = -lstdc++
INCDIR =
CFLAGS = -G0 -Wall -O2
CXXFLAGS = $(CFLAGS) -fno-rtti -fexceptions
SFLAGS = $(CFLAGS)
LIBDIR =
LDFLAGS =
XTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = C++ Exception Sample
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
PS: I'm adding this sample to the SDK in my builds.
so you can use -fexception but not rtti (dynamic_cast<> unusable). If a bus error occurs, do you catch a C++ exception ?Heimdall wrote:that was what i thought hlide, and started to add a simple threading implementation to gcc based on an old haiku-os implementation that only tels gcc that this device can handle mutexes, however when i throw an exception the mutex locks everything (i guess i got it all wrong from the gcc barely existing docs).
I've reverted my patch and exceptions work out of the box like jean says both with base user defined exception or using the sdt::exception one.
I've tested with gcc 4.3.3 and newlib 1.16 / gcc 4.3.3 and newlib 1.17
It works for user defined exceptions that are programatically thrown, meaning all classes that extend std::exception (or not, like jean example ) can be throw and catched. For bus errors I haven't tested those thoroughly. For those I need to have at least mutexes working, but my gcc code failed:hlide wrote:so you can use -fexception but not rtti (dynamic_cast<> unusable). If a bus error occurs, do you catch a C++ exception ?
Code: Select all
static inline int
__gthread_mutex_lock (__gthread_mutex_t * mutex)
{
int stat;
int thid;
int own;
int rtn;
if (mutex->semid < 0)
{
mutex->semid = sceKernelCreateSema ("libgccMutex", 0x0100, 1, 1, 0);
}
thid = sceKernelGetThreadId ();
stat = sceKernelCpuSuspendIntr ();
if (mutex->owner < 0)
{
mutex->owner = thid;
own = 1;
}
else
own = 0;
sceKernelCpuResumeIntr (stat);
if (own || mutex->owner != thid)
{
rtn = sceKernelWaitSema (mutex->semid, 1, 0);
}
else
{
mutex->count++;
rtn = 0;
}
return rtn;
}
static inline int
__gthread_mutex_trylock (__gthread_mutex_t * mutex)
{
int stat;
int thid;
int own;
int rtn;
if (mutex->semid < 0)
{
mutex->semid = sceKernelCreateSema ("libgccMutex", 0x0100, 1, 1, 0);
}
thid = sceKernelGetThreadId ();
stat = sceKernelCpuSuspendIntr ();
if (mutex->owner < 0)
{
mutex->owner = thid;
own = 1;
}
else
own = 0;
sceKernelCpuResumeIntr (stat);
if (own || mutex->owner != thid)
{
rtn = sceKernelPollSema (mutex->semid, 1);
}
else
{
mutex->count++;
rtn = 0;
}
return rtn;
}
static inline int
__gthread_mutex_unlock (__gthread_mutex_t * mutex)
{
int rtn;
if (mutex->owner == sceKernelGetThreadId ())
{
if (mutex->count > 0 && --mutex->count == 0)
{
rtn = sceKernelSignalSema (mutex->semid, 1);
mutex->owner = -1;
sceKernelDeleteSema (mutex->semid);
mutex->semid = -1;
}
}
else if (mutex->owner >= 0)
{
rtn = sceKernelSignalSema (mutex->semid, 1);
mutex->owner = -1;
sceKernelDeleteSema (mutex->semid);
mutex->semid = -1;
mutex->count = 0;
}
else
rtn = -1;
return rtn;
}
Maybe I understood the semaphore api wrong or passing the wrong arguments either...[/code]
humm... those functions are only available in the SDK or DA SDK meaning I don't know the method signature... more work to do...hlide wrote:if you need a mutex, why not using those :
sceKernelCreateMutex
sceKernelDeleteMutex
sceKernelLockMutex
sceKernelLockMutexCB
sceKernelTryLockMutex
sceKernelUnlockMutex
ok,Heimdall wrote:humm... those functions are only available in the SDK or DA SDK meaning I don't know the method signature... more work to do...hlide wrote:if you need a mutex, why not using those :
sceKernelCreateMutex
sceKernelDeleteMutex
sceKernelLockMutex
sceKernelLockMutexCB
sceKernelTryLockMutex
sceKernelUnlockMutex
Code: Select all
SceUID sceKernelCreateMutex(
const char *name,
unsigned int attributes, /* using a FIFO (0) or PRIORITIZED (256) THREAD QUEUE */
int initial_count, /* the initial count of mutex when created */
void *optional_parameters /* just pass 0 */
); // returns Mutex UID if >0
int sceKernelDeleteMutex(
SceUID id /* Mutex UID */
); // returns 0 if OK
int sceKernelLockMutex(
SceUID id, /* Mutex UID */
int lock_count, /* > 0 */
unsigned int *timeout /* In : 0 if no timeout or a pointer to a placeholder containing the max time value in ms. Out: time remaining when mutex is acquired */
); // returns 0 if OK or 0x800201a8 if timeout
int sceKernelLockMutexCB(
SceUID id, /* Mutex UID */
int lock_count, /* > 0 */
unsigned int *timeout /* In : 0 if no timeout or a pointer to a placeholder containing the max time value in ms. Out: time remaining when mutex is acquired */
); // returns 0 if OK or 0x800201a8 if timeout
int sceKernelTryLockMutex(
SceUID id, /* Mutex UID */
int lock_count /* > 0 */
); // returns 0x800201c4 if current thread failed to own mutex or 0 if success
int sceKernelUnlockMutex(
SceUID id, /* Mutex UID */
int unlock_count /* >= 0 */
); // returns 0 if OK
Ok let's imagine we have a kernel prx wich installs a exception handler and that we can call a handler individually for each different cause :
void exc_install_handler(int cause, void (*handler)(int, exc_t *exc));
Basically, the general exception we installed through a kernel prx will be called when an exception is raised. This exception will call the right handler in an array where the cause is index. The previous function exported by the kernel prx allows us to define an handler per cause : bus error, FPU error, etc.
Now, we can define some C++ exceptions this way :
void exc_install_handler(int cause, void (*handler)(int, exc_t *exc));
Basically, the general exception we installed through a kernel prx will be called when an exception is raised. This exception will call the right handler in an array where the cause is index. The previous function exported by the kernel prx allows us to define an handler per cause : bus error, FPU error, etc.
Now, we can define some C++ exceptions this way :
Code: Select all
class HardwareException
{
public:
HardwareException(exc_t *exc)
{
m_exc = exc;
}
protected:
exc_t m_exc;
};
template <class ExceptionClass> class ExceptionTranslator
{
private:
class SingletonTranslator
{
public:
SingletonTranslator()
{
exc_install_handler(ExceptionClass::cause(), handler);
}
static void handler(int, exc_t *exc)
{
throw ExceptionClass(exc);
}
};
public:
ExceptionTranslator()
{
static SingletonTranslator s_obj_Translator;
}
};
// An example for BUS_ERROR
class SegmentationFault : public HardwareException, public exception
{
public:
SegmentationFault(exc_t *exc) : HardwareException(exc), exception() {}
static int cause() { return BUS_ERROR; }
...
};
ExceptionTranslator<SegmentationFault> g_obj_SegmentationFaultTranslator;
// An example for FPU_ERROR
class FloatingPointException : public HardwareException, public exception
{
public:
FloatingPointException(exc_t *exc) : HardwareException(exc), exception() {}
static int cause() { return FPU_ERROR; }
...
};
ExceptionTranslator<FloatingPointException> g_obj_FloatingPointExceptionTranslator;
huh this is just an example to show how we could handle C++ exception to throw when an hardware exception occurs (access to null pointer, divide error, etc.) which are not catchable with the actual psp-gcc.
I think if we could tweak the source of exception.prx (not sure about the name) which is a homebrew kernel prx to allow a user application to catch every hardware exception and dump the details with a blue screen of death.
I think we can modify it so we can add an array of exception handlers and an exported function to install a user handler to call. Nothing is in C++.
The code I posted in C++ shows how in our c++ application we can try to make hardware exceptions turn into c++ exception. To do so, we could load this kernel prx and import such a function to install a user handler per hardware exception category to call the right "throw".
This is just an idea for those who are interested with.
I think if we could tweak the source of exception.prx (not sure about the name) which is a homebrew kernel prx to allow a user application to catch every hardware exception and dump the details with a blue screen of death.
I think we can modify it so we can add an array of exception handlers and an exported function to install a user handler to call. Nothing is in C++.
The code I posted in C++ shows how in our c++ application we can try to make hardware exceptions turn into c++ exception. To do so, we could load this kernel prx and import such a function to install a user handler per hardware exception category to call the right "throw".
This is just an idea for those who are interested with.
Last edited by hlide on Tue Mar 10, 2009 10:45 pm, edited 1 time in total.