Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding several boost::error_infos of the same type to a boost::exception

#include <boost/exception/all.hpp>
#include <iostream>

struct myexception : virtual boost::exception, virtual std::exception {};
typedef boost::error_info<struct tag_info, std::string> info;

void main()
{
    try
    {
        BOOST_THROW_EXCEPTION(myexception()
            << info("1")
            << info("2") );
    }
    catch(const myexception& e)
    {
        std::cout << boost::diagnostic_information(e) << std::endl;
    }
}

This will output

[struct tag_info *] = 2

I understand why this is the case, but would rather have it output

[struct tag_info *] = 1
[struct tag_info *] = 2

I could, of course, typedef info as boost::error_info<struct tag_info, std::vector<std::string> > and then accumulate all infos in a std::vector before shifting it into the exception, but that has two downsides:
a) It involves copying of a std::vector
b) I need to build up the vector before throwing, i.e. I cannot simply use the shift operator to add more infos.

Thus I'm now looking for better solutions for adding several infos of the same error_info-type to an exception.


EDIT :
I tried to do as Josh Kelley suggested in his comment below and overload operator <<:

#include <boost/exception/all.hpp>
#include <iostream>
#include <vector>

typedef boost::error_info<struct tag_info, std::string> info;
typedef boost::error_info<struct tag_multiple_infos, std::vector<std::string> > multiple_infos;

struct myexception : virtual boost::exception, virtual std::exception
{
    myexception& operator<< (const info& rhs)
    {
        std::vector<std::string>* pinfos = boost::get_error_info<multiple_infos, myexception>(*this);
        if (pinfos != NULL)
        {
            pinfos->push_back(rhs.value());
        }
        else
        {
            std::vector<std::string> infos;
            infos.push_back(rhs.value());
            *this << multiple_infos(infos);
        }
        return *this;
    }
};

std::string to_string(const multiple_infos& info)
{
    std::ostringstream oss;
    std::for_each(info.value().begin(), info.value().end(),
        [&oss](const std::string& str) { oss << str << ' '; });
    return oss.str();
}

void main()
{
    try
    {
        BOOST_THROW_EXCEPTION(myexception()
            << info("1")
            << info("2") );
    }
    catch(const myexception& e)
    {
        std::cout << boost::diagnostic_information(e) << std::endl;
    }
}

That will output

[struct tag_multiple_infos *] = 1 2

That's neat, but I like Pyotrs answer better, because it appears more natural to me and requires less code. However, if I wanted to add the infos across multiple catch sites1, then this solution would be more suitable, because I don't need to know how many infos I have already added.

1 = I.e. shift infos into an exception, throw it, catch it somewhere else, shift more infos into it, then rethrow.

like image 902
Alexander Tobias Bockstaller Avatar asked Nov 04 '22 15:11

Alexander Tobias Bockstaller


1 Answers

Just use two tags:

struct tag_info1;
struct tag_info2;
typedef boost::error_info<tag_info1, std::string> info1;
typedef boost::error_info<tag_info2, std::string> info2;

Use like this:

    BOOST_THROW_EXCEPTION(myexception()
        << info1("1")
        << info2("2") );

If you want more infos, use template:

template <unsigned N>
struct tag_info {};

template <unsigned N>
struct Nth {
  typedef boost::error_info<tag_info<N>, std::string> info;
};

    BOOST_THROW_EXCEPTION(myexception()
        << Nth<1>::info("1")
        << Nth<2>::info("2") );
like image 157
PiotrNycz Avatar answered Nov 11 '22 07:11

PiotrNycz