0

Would it be possible to install a custom handler for GCC?

I'm trying to throw a wrapper class to a pointer (like shared_ptr) and then catch it covariantly. This is actually for my Managed C++ for GCC project (on sourceforge) but to illustrate the problem in a manner more conventional C++ friendly, I'll use boost::shared_ptr in this particular instance. This is what I'm trying to achieve.

void raise()
{
    throw shared_ptr<DerivedException>(new DerivedException);
}

int main()
{
    try
    { 
        raise();
    }
    catch (shared_ptr<Exception> ex)
    {
        // Needs to catch DerivedException too!
    }
}

Any ideas as to whether this is achievable?

Zach Saw
  • 4,308
  • 3
  • 33
  • 49
  • I am not sure what you are trying to achieve. You mean derived from shared_ptr or derived from Exception? Do you really need to throw a shared pointer? – Martin York Dec 11 '12 at 03:54
  • @LokiAstari: Derived from Exception. As the goal of the project is to be as similar to C++/CLI as possible, I'd like to be able to do that. – Zach Saw Dec 11 '12 at 03:55
  • 1
    As far as I know, you have to convert Boost's `shared_ptr` using `boost::dynamic_pointer_cast`, which you can't do when catching. [Also, I can't think of a way to work around this](http://stackoverflow.com/questions/2343208/can-you-catch-an-exception-by-the-type-of-a-conversion-operator). – Cornstalks Dec 11 '12 at 04:01
  • @Cornstalks: Some compilers (rather its runtime) allow you to install custom exception handlers that would make my example work, I just have no idea how to do that with GCC. – Zach Saw Dec 11 '12 at 04:08
  • Easiest thing seems to be to "upcast" before throwing – Mooing Duck Dec 17 '12 at 22:21
  • @MooingDuck doing an "upcast" before throwing would be simpler but it makes discriminating on the derived type harder because you lose the actual type of the referred to object. Also it wouldn't work if your exception hierarchy uses multiple inheritance without a common base class. But if you do not need either of those, it would be easier. – Bowie Owens Dec 17 '12 at 22:55
  • I put quotes in upcast, because it's not a true upcast, it's casting to the type that would be caught. if there's not a common type that would be caught... that doesn't even make sense. So we can assume all things being thrown can be cast to something that would be caught. Yes, losing the actual type is a downside :( – Mooing Duck Dec 17 '12 at 23:06
  • @MooingDuck the problem with multiple inheritance is not the absence of a type to be caught but that there is more than one. Lets say you have a file exception, a network exception and a network file exception which inherits from both. To throw a network file exception what do you cast to? You can't know without inspecting the catch handlers. I have edited my answer below to illustrate. In this case if f() throws an nfs_exception_t it will be handled as a network_exception_t but if you change the order of the catch blocks it will be handled as a file_exception_t. – Bowie Owens Dec 17 '12 at 23:39
  • @BowieOwens: That's a good concern, but if we compare that situation to the non-wrapped version, we see that that situation would require one to manually cast it to one or the other type anyway. The method I described causes the wrapped exceptions to behave in almost the same way as regular exceptions. – Mooing Duck Dec 17 '12 at 23:49

1 Answers1

0

If I understand correctly, you can do what you want in C++ without a custom exception handler but not with the syntax you are using. One solution I can see is that you combine virtual functions with the exception mechanism. First you create a base class to make catching easy and give it an interface to allow easy rethrowing of the object itself and its referred to object.

struct shared_exception_base_t {
    virtual void raise_ref() = 0;
    virtual void raise_self() = 0;
};

template <class value_t>
class shared_ptr_t : public shared_exception_base_t {
    value_t* ptr_;
public:
    shared_ptr_t(value_t* const p) : ptr_ (p) { }

    void raise_ref()
    {
        throw *ptr_;
    }

    void raise_self()
    {
        throw *this;
    }
};


template <class value_t>
shared_ptr_t<value_t> mk_with_new()
{
    return shared_ptr_t<value_t>(new value_t());
}

Then you can use the exception mechanism to do the discrimination. Note that the try/catch blocks have to be nested.

#include <iostream>

struct file_exception_t { };
struct network_exception_t { };
struct nfs_exception_t : file_exception_t, network_exception_t { };
struct runtime_exception_t { };

void f()
{
    throw mk_with_new<runtime_exception_t>();
}

int
main()
{
    try {
        try {
            f();
        } catch (shared_exception_base_t& x) {
            try {
                x.raise_ref();
            } catch (network_exception_t& fx) {
                std::cerr << "handling network exception\n"; 
            } catch (file_exception_t& fx) {
                std::cerr << "handling file exception\n"; 
            } catch (...) {
                x.raise_self();
            }
        }
    } catch (...) { 
        std::cerr << "no idea\n";
    }

    return 0;
}
Bowie Owens
  • 2,798
  • 23
  • 20