Preventing implicit calling of non-explicit constructor.

J

jason.cipriani

I have an application with a class "AppException" derived from
std::exception. I have full control over the implementation of
"AppException". I have two constructors like this:

class AppException {
public:
...
AppException (const char *msg, ...);
AppException (const std::exception &cause, const char *msg, ...);
...
};

The first constructor takes a printf format string and optional
parameters. The second takes an std::exception as the root cause, and
the same printf-style message. This functionality is critical (I need
to be able to construct an AppException from just a message, or from a
message and an std::exception root cause), although this particular
interface is not critical.

My problem is that std::exception has a non-explicit const char *
constructor. Therefore it can be implicitly converted from a const
char *. So in cases where I am using the no-cause constructor but
where my format parameters are a single additional string, e.g.:

throw AppException("Some string: %s", someString);

The compiler (VS 2008's compiler) complains that both constructors are
possible matches (the second constructor also matches, it attempts to
implicitly convert the const char * to an std::exception, and pass
someString as "msg").

How can I get rid of this ambiguity, but still keep the same
functionality? I'm kind of frazzled and having trouble coming up with
ideas. If I could somehow say that I wanted std::exception(const char
*) to be explicit, that would be one way to solve the problem, but I
don't think that's possible.

Thanks,
Jason
 
A

Alan Johnson

I have an application with a class "AppException" derived from
std::exception. I have full control over the implementation of
"AppException". I have two constructors like this:

class AppException {
public:
...
AppException (const char *msg, ...);
AppException (const std::exception &cause, const char *msg, ...);
...
};

The first constructor takes a printf format string and optional
parameters. The second takes an std::exception as the root cause, and
the same printf-style message. This functionality is critical (I need
to be able to construct an AppException from just a message, or from a
message and an std::exception root cause), although this particular
interface is not critical.

My problem is that std::exception has a non-explicit const char *
constructor. Therefore it can be implicitly converted from a const
char *. So in cases where I am using the no-cause constructor but
where my format parameters are a single additional string, e.g.:

throw AppException("Some string: %s", someString);

The compiler (VS 2008's compiler) complains that both constructors are
possible matches (the second constructor also matches, it attempts to
implicitly convert the const char * to an std::exception, and pass
someString as "msg").

How can I get rid of this ambiguity, but still keep the same
functionality? I'm kind of frazzled and having trouble coming up with
ideas. If I could somehow say that I wanted std::exception(const char
*) to be explicit, that would be one way to solve the problem, but I
don't think that's possible.

Thanks,
Jason

std::exception doesn't have a constructor that takes a const char *.
It's full definition according to 18.6.1 is:

namespace std {
class exception {
public:
exception() throw();
exception(const exception&) throw();
exception& operator=(const exception&) throw();
virtual ̃exception() throw();
virtual const char* what() const throw();
};
}

Seems like you've found an error in Microsoft's implementation.

Anyway, the workaround is to exploit the fact that only one implicit
conversion is allowed. Create a class to wrap a standard exception:

class ExceptionWrapper
{
public:
ExceptionWrapper(const std::exception & e) : m_ref(e)
{}

const std::exception & get() const
{
return m_ref;
}
private:
const std::exception & m_ref;
};


Then change your exception class's interface to accept that:

class AppException {
public:
...
AppException (const char *msg, ...);
AppException (const ExceptionWrapper &cause, const char *msg, ...);
...
};

Within the AppException class use ExceptionWrapper::get to access the
exception.

You can still pass a std::exception as the first argument because an
ExceptionWrapper can be implicitly created, but because only one
implicit conversion is allowed, there is no way for the second
constructor to match a call with const char * as the first argument.
 
S

Salt_Peter

I have an application with a class "AppException" derived from
std::exception. I have full control over the implementation of
"AppException". I have two constructors like this:

class AppException {
public:
...
AppException (const char *msg, ...);
AppException (const std::exception &cause, const char *msg, ...);
...

};

The first constructor takes a printf format string and optional
parameters. The second takes an std::exception as the root cause, and
the same printf-style message. This functionality is critical (I need
to be able to construct an AppException from just a message, or from a
message and an std::exception root cause), although this particular
interface is not critical.

My problem is that std::exception has a non-explicit const char *
constructor. Therefore it can be implicitly converted from a const
char *. So in cases where I am using the no-cause constructor but
where my format parameters are a single additional string, e.g.:

throw AppException("Some string: %s", someString);

The compiler (VS 2008's compiler) complains that both constructors are
possible matches (the second constructor also matches, it attempts to
implicitly convert the const char * to an std::exception, and pass
someString as "msg").

How can I get rid of this ambiguity, but still keep the same
functionality? I'm kind of frazzled and having trouble coming up with
ideas. If I could somehow say that I wanted std::exception(const char
*) to be explicit, that would be one way to solve the problem, but I
don't think that's possible.

Thanks,
Jason

#include <iostream>
#include <stdexcept>

class AppException : public std::runtime_error
{
public:
AppException(const char* s)
: std::runtime_error(s) { }
// other ctors
};

int main()
{
try
{
throw AppException("testing AppException");
}
catch( std::exception& r_e )
{
std::cout << "Error: ";
std::cout << r_e.what() << std::endl;
}
}

/*
Error: testing AppException
*/

std::runtime_error is itself a derivative of std::exception and has an
explicit constructor. Consider std::logic_error, std::eek:ut_of_range,
std::domain_error, etc as well should you plan to specialize your
exceptions.
 
J

jason.cipriani

std::exception doesn't have a constructor that takes a const char *.
It's full definition according to 18.6.1 is:

namespace std {
   class exception {
   public:
      exception() throw();
      exception(const exception&) throw();
      exception& operator=(const exception&) throw();
      virtual  ̃exception() throw();
      virtual const char* what() const throw();
   };

}

Seems like you've found an error in Microsoft's implementation.

Indeed. After preprocessing, this is what is in MS's <exception>:

class __declspec(dllimport) exception {
public:
exception();
exception(const char *const&);
exception(const char *const&, int);
exception(const exception&);
exception& operator=(const exception&);
virtual ~exception();
virtual const char * what() const;
private:
const char *_m_what;
int _m_doFree;
};

I never noticed that before. It is also documented as such:

http://msdn.microsoft.com/en-us/library/c4ts6d5a(VS.80).aspx

Weird.
Anyway, the workaround is to exploit the fact that only one implicit
conversion is allowed.  Create a class to wrap a standard exception:

Hey, nice idea, thanks! It was easy to implement and solved the
problem nicely. :)

Thanks,
Jason
 
J

jason.cipriani

#include <iostream>
#include <stdexcept>

class AppException : public std::runtime_error
{
public:
    AppException(const char* s)
    : std::runtime_error(s) { }
    // other ctors

};

int main()
{
  try
  {
    throw AppException("testing AppException");
  }
  catch( std::exception& r_e )
  {
    std::cout << "Error: ";
    std::cout << r_e.what() << std::endl;
  }

}

/*
Error: testing AppException
*/

std::runtime_error is itself a derivative of std::exception and has an
explicit constructor. Consider std::logic_error, std::eek:ut_of_range,
std::domain_error, etc as well should you plan to specialize your
exceptions.


Thanks for the tip. This doesn't solve my problem, as I still want to
be able to use any std::exception as the "cause", not just a
runtime_exception. Still, I've modified it to derive from
runtime_exception anyways, as it definitely seems more appropriate.

Jason
 
J

jason.cipriani

Thanks for the tip. This doesn't solve my problem, as I still want to
be able to use any std::exception as the "cause", not just a
runtime_exception. Still, I've modified it to derive from
runtime_exception anyways, as it definitely seems more appropriate.

runtime_error*
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top