Transporting of Arbitrary Data to the Catch Site
Все типы исключений, которые происходят от повышения:Исключениеможет быть использовано в качестве типовобезопасных контейнеров произвольных объектов данных при соблюдении требований «без броска» (15.5.1) стандарта ANSI C++ для типов исключений.
Когда исключения вытекают из повышения:Исключение, произвольные данные могут быть добавлены к объектам исключения:
- В точке броска;
- В более позднее время, как исключения пузырь вверх стека вызова.
Adding of Arbitrary Data at the Point of the Throw
Следующий пример показывает, как errno может храниться в объектах исключений с помощью Boost Exception:
#include <boost/exception/all.hpp>
#include <iostream>
typedef boost::error_info<struct tag_my_info,int> my_info; //(1)
struct my_error: virtual boost::exception, virtual std::exception { }; //(2)
void
f()
{
throw my_error() << my_info(42); //(3)
}
Во-первых, мы инстанцируем шаблонerror_info, используя уникальный идентификатор - tag_my_info, и тип информации, которую он идентифицирует - int. Это обеспечивает безопасность типа компиляции для различных значений, хранящихся в объектах исключения.
Во-вторых, мы определяем класс my_error, который вытекает из повышения:исключение.
Наконец, (3) иллюстрирует, как типдеф из (1) может использоваться с операторомдля хранения значений в объектах исключения в точке броска.
Сохраненное значение my_info может быть восстановлено в более позднее время:
// ...continued
void
g()
{
try
{
f();
}
catch(
my_error & x )
{
if( int const * mi=boost::get_error_info<my_info>(x) )
std::cerr << "My info: " << *mi;
}
}
get_error_infoшаблон функции инстанциируется с типдефом из (1) и пропускается исключительным объектом полиморфного типа. Если объект исключения содержит запрошенное значение, на него будет указывать ошибка; в противном случае возвращается нулевой указатель.
Adding of Arbitrary Data to Active Exception Objects
Иногда сайт броска не имеет всей информации, которая необходима на сайте улова, чтобы понять, что пошло не так. Допустим, у нас есть файл типа исключения_read_error, который берет имя файла в своем конструкторе. Рассмотрим следующую функцию:
void
file_read( FILE * f, void * buffer, size_t size )
{
if( size!=fread(buffer,1,size,f) )
throw file_read_error(????);
}
Как функция file_read может передать имя файла конструктору типов исключений? Все, что у него есть, это ручка.
Использование boost::исключениепозволяет нам освободить функцию file_read от бремени хранения имени файла в исключениях, которые он бросает:
#include <boost/exception/all.hpp>
#include <boost/shared_ptr.hpp>
#include <stdio.h>
#include <errno.h>
struct file_read_error: virtual boost::exception { };
void
file_read( FILE * f, void * buffer, size_t size )
{
if( size!=fread(buffer,1,size,f) )
throw file_read_error() << boost::errinfo_errno(errno);
}
Если файл_read обнаруживает сбой, он делает исключение, которое содержит информацию, доступную в то время. Другая соответствующая информация, такая как имя файла, может быть добавлена в контекст выше в стек вызовов, где это известно естественным образом:
#include <boost/exception/all.hpp>
#include <boost/shared_ptr.hpp>
#include <stdio.h>
#include <string>
boost::shared_ptr<FILE> file_open( char const * file_name, char const * mode );
void file_read( FILE * f, void * buffer, size_t size );
void
parse_file( char const * file_name )
{
boost::shared_ptr<FILE> f = file_open(file_name,"rb");
assert(f);
try
{
char buf[1024];
file_read( f.get(), buf, sizeof(buf) );
}
catch(
boost::exception & e )
{
e << boost::errinfo_file_name(file_name);
throw;
}
}
Вышеупомянутая функция является (почти) исключительно нейтральной — если исключение испускается любым вызовом функции в блоке пробы, parse_file не нужно выполнять какую-либо реальную работу, но он перехватывает любое увеличение:исключениеобъект, сохраняет имя файла и повторно бросает, используя экспрессию броска без операнда (15.1.6). Обоснование для получения любого повышения:исключениеобъект заключается в том, что имя файла имеет отношение к любому сбою, который происходит в parse_file,даже если сбой не связан с файлом I/O.