Preventing implicit calling of non-explicit constructor.

Discussion in 'C++' started by jason.cipriani@gmail.com, Dec 3, 2008.

  1. Guest

    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
     
    , Dec 3, 2008
    #1
    1. Advertising

  2. Alan Johnson Guest

    wrote:
    > 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.

    --
    Alan Johnson
     
    Alan Johnson, Dec 3, 2008
    #2
    1. Advertising

  3. Salt_Peter Guest

    On Dec 2, 8:57 pm, ""
    <> wrote:
    > 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.
     
    Salt_Peter, Dec 3, 2008
    #3
  4. Guest

    On Dec 2, 10:58 pm, Alan Johnson <> wrote:
    > wrote:
    > > 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.


    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


    > 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.
    >
    > --
    > Alan Johnson
     
    , Dec 3, 2008
    #4
  5. Guest

    On Dec 2, 11:00 pm, Salt_Peter <> wrote:
    > On Dec 2, 8:57 pm, ""
    >
    >
    >
    > <> wrote:
    > > 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.



    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
     
    , Dec 3, 2008
    #5
  6. Guest

    On Dec 3, 12:46 am, ""
    <> wrote:
    > 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*
     
    , Dec 3, 2008
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dmoos AT esigma-systems DOT de

    implicit vs. explicit type conversion for string vs. (char *)

    dmoos AT esigma-systems DOT de, Jun 26, 2003, in forum: C++
    Replies:
    2
    Views:
    2,520
    Default User
    Jun 26, 2003
  2. al
    Replies:
    2
    Views:
    5,620
    Ernst Murnleitner
    Jan 7, 2004
  3. Generic Usenet Account
    Replies:
    10
    Views:
    2,248
  4. werasm
    Replies:
    7
    Views:
    1,061
    werasm
    Mar 25, 2010
  5. Michael Doubez
    Replies:
    4
    Views:
    340
    werasm
    Mar 25, 2010
Loading...

Share This Page