Anthony Williams | 24 Oct 13:25
Picon
Favicon

Re: Boost Exception and support for cloning

"Josh Napoli" <jnapoli <at> actuality-systems.com> writes:

>> Mathias Gaunard wrote:
>> C++0x has virtual constructors?
>
> No - but exceptions (only) will get a special built-in clone mechanism
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html) 

I have an implementation of this for MSVC which I intended to upload soon for
boost.thread, once I'd got a generic (and thus incomplete) version working.

Source code attached.

Anthony
-- 
Anthony Williams
Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk
Registered in England, Company Number 5478976.
Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// (C) Copyright 2007 Anthony Williams
#ifndef EXCEPTION_PTR_HPP
#define EXCEPTION_PTR_HPP
#include <typeinfo>
#include "thread_heap_alloc.hpp"
#include <string.h>
#include "thread_primitives.hpp"
#include <excpt.h>

namespace jss
{
    namespace detail
    {
        namespace win32
        {
            unsigned const exception_maximum_parameters=15;
            unsigned const exception_noncontinuable=1;

            struct exception_record
            {
                unsigned long ExceptionCode;
                unsigned long ExceptionFlags;
                exception_record *ExceptionRecord;
                void* ExceptionAddress;
                unsigned long NumberParameters;
                ulong_ptr ExceptionInformation[exception_maximum_parameters];
            };

            struct exception_pointers
            {
                exception_record* ExceptionRecord;
                void* ContextRecord;
            };

            extern "C"
            {
                __declspec(dllimport) void __stdcall RaiseException(unsigned long,unsigned long,unsigned long,ulong_ptr*);
            }
        }

        unsigned const cpp_exception_code=0xE06D7363;
        unsigned const cpp_exception_magic_flag=0x19930520;
        unsigned const cpp_exception_parameter_count=3;

        struct dummy_exception_type
        {};

        typedef int(dummy_exception_type::*normal_copy_constructor_ptr)(void* src);
        typedef int(dummy_exception_type::*copy_constructor_with_virtual_base_ptr)(void* src,void* dest);
        typedef void (dummy_exception_type::*destructor_ptr)();

        union cpp_copy_constructor
        {
            normal_copy_constructor_ptr normal_copy_constructor;
            copy_constructor_with_virtual_base_ptr copy_constructor_with_virtual_base;
        };

        enum cpp_type_flags{
            class_is_simple_type=1,
            class_has_virtual_base=4
        };

        struct cpp_type_info
        {
            unsigned flags;
            std::type_info* type_info;
            int this_offset;
            int vbase_descr;
            int vbase_offset;
            unsigned long size;
            cpp_copy_constructor copy_constructor;
        };

        struct cpp_type_info_table
        {
            unsigned count;
            const cpp_type_info* info[1];
        };

        struct cpp_exception_type
        {
            unsigned flags;
            destructor_ptr destructor;
            void(*custom_handler)();
            const cpp_type_info_table* type_info_table;
        };

        struct cloned_exception
        {
            cpp_exception_type const* exception_type;
            void* exception_object;
            bool run_destructor;
            long count;

            cloned_exception(cloned_exception const& other):
                exception_type(other.exception_type),
                exception_object(0),
                count(0),
                run_destructor(false)
            {
                clone(other.exception_object);
            }

            void clone(void* source_object)
            {
                exception_object=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,exception_type->type_info_table->info[0]->size);

                const cpp_type_info *type = exception_type->type_info_table->info[0];

                if(!(type->flags & class_is_simple_type) && type->copy_constructor.normal_copy_constructor)
                {
                    dummy_exception_type* dummy_exception_ptr=reinterpret_cast<dummy_exception_type*>(exception_object);
                    if(type->flags & class_has_virtual_base)
                    {
                        (dummy_exception_ptr->*(type->copy_constructor.copy_constructor_with_virtual_base))(source_object,exception_object);
                    }
                    else
                    {
                        (dummy_exception_ptr->*(type->copy_constructor.normal_copy_constructor))(source_object);
                    }
                }
                else
                {
                    memmove(exception_object,source_object,type->size);
                }
                run_destructor=true;
            }

            cloned_exception(void* source_object,cpp_exception_type const* exception_type_):
                exception_type(exception_type_),
                exception_object(0),
                count(0),
                run_destructor(false)
            {
                clone(source_object);
            }

            ~cloned_exception()
            {
                if(exception_object)
                {
                    if(run_destructor)
                    {
                        dummy_exception_type* dummy_exception_ptr=reinterpret_cast<dummy_exception_type*>(exception_object);
                        (dummy_exception_ptr->*(exception_type->destructor))();
                    }
                    detail::win32::HeapFree(win32::GetProcessHeap(),0,exception_object);
                }
            }

            void rethrow()
            {
                cloned_exception temp(*this);

                detail::win32::ulong_ptr args[detail::cpp_exception_parameter_count];
                args[0]=detail::cpp_exception_magic_flag;
                args[1]=reinterpret_cast<detail::win32::ulong_ptr>(temp.exception_object);
                args[2]=reinterpret_cast<detail::win32::ulong_ptr>(temp.exception_type);

                temp.run_destructor=false;

                detail::win32::RaiseException(detail::cpp_exception_code,detail::win32::exception_noncontinuable,detail::cpp_exception_parameter_count,args);
            }

            friend void intrusive_ptr_add_ref(cloned_exception * p)
            {
                JSS_INTERLOCKED_INCREMENT(&p->count);
            }

            friend void intrusive_ptr_release(cloned_exception * p)
            {
                if(!JSS_INTERLOCKED_DECREMENT(&p->count))
                {
                    detail::heap_delete(p);
                }
            }
        };

    }

    typedef boost::intrusive_ptr<detail::cloned_exception> exception_ptr;

    namespace detail
    {
        extern "C" int* _errno();

        bool is_cpp_exception(win32::exception_record* record)
        {
            return record && 
                (record->ExceptionCode==cpp_exception_code) &&
                (record->NumberParameters==cpp_exception_parameter_count) &&
                (record->ExceptionInformation[0]==cpp_exception_magic_flag);
        }

        unsigned long exception_cloning_filter(exception_ptr* ptr,void* info_)
        {
            win32::exception_pointers* info=reinterpret_cast<win32::exception_pointers*>(info_);
            win32::exception_record* record=info->ExceptionRecord;
            if(is_cpp_exception(record))
            {
                if(!record->ExceptionInformation[2])
                {
#if _MSC_VER==1310
                    unsigned const exception_info_offset=0x74;
#elif _MSC_VER==1400
                    unsigned const exception_info_offset=0x80;
#endif
                    record=*reinterpret_cast<win32::exception_record**≥(reinterpret_cast<char*>(_errno())+exception_info_offset);
                }
                if(is_cpp_exception(record) && record->ExceptionInformation[2])
                {
                    *ptr=detail::heap_new<cloned_exception>(reinterpret_cast<void*>(record->ExceptionInformation[1]),
                                                            reinterpret_cast<cpp_exception_type const*>(record->ExceptionInformation[2]));
                }
            }
            return EXCEPTION_EXECUTE_HANDLER;
        }

        void clone_current_exception(exception_ptr* res)
        {
            __try
            {
                throw;
            }
            __except(exception_cloning_filter(res,GetExceptionInformation()))
            {
            }

        }

    }

    
    inline exception_ptr current_exception()
    {
        exception_ptr res;
        clone_current_exception(&res);

        return res;
    }

    inline void rethrow_exception(exception_ptr p)
    {
        if(p)
        {
            p->rethrow();
        }
        else
        {
            throw "no exception stored";
        }
    }

    template<class E>
    exception_ptr copy_exception(E e)
    {
        try
        {
            throw e;
        }
        catch( ... )
        {
            return current_exception();
        }
    }

}

#endif
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Gmane