ostringstream derived problem

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • iu2

    ostringstream derived problem

    Hi all, can someone help me with this?

    I'm trying to shorthen some logging function in our project, i.e.,
    instead of

    LogString("...) ;

    use a more convenient

    log() << "String here " << 12.33 << " and a number";

    I create this syntax like this:

    struct log : public ostringstream {
    virtual ~log() { cout << "The log contains: " << str().c_str() <<
    endl;}
    };

    The trick I'm trying to do is that log() creates an object of type
    log, then it fills it using the ostringstream's parent '<<' and right
    after the statement finishes the destructor is called and the message
    is printed onto the screen.
    The problem is that it doesn't:
    It prints something like "The log contains: " 4129000
    But if the first element after "log() << " is not a string, e.g., a
    number, dec, hex or whatever, the scheme succeeds.

    Why does this happen? I've got a feeling that the destructor might be
    called before the first "<< " but I don't understand why.

    Thanks

  • John Harrison

    #2
    Re: ostringstream derived problem

    iu2 wrote:
    Hi all, can someone help me with this?
    >
    I'm trying to shorthen some logging function in our project, i.e.,
    instead of
    >
    LogString("...) ;
    >
    use a more convenient
    >
    log() << "String here " << 12.33 << " and a number";
    >
    I create this syntax like this:
    >
    struct log : public ostringstream {
    virtual ~log() { cout << "The log contains: " << str().c_str() <<
    endl;}
    };
    >
    The trick I'm trying to do is that log() creates an object of type
    log, then it fills it using the ostringstream's parent '<<' and right
    after the statement finishes the destructor is called and the message
    is printed onto the screen.
    The problem is that it doesn't:
    It prints something like "The log contains: " 4129000
    But if the first element after "log() << " is not a string, e.g., a
    number, dec, hex or whatever, the scheme succeeds.
    >
    Why does this happen? I've got a feeling that the destructor might be
    called before the first "<< " but I don't understand why.
    >
    Thanks
    >
    Very subtle. I can't tell you the answer, although I can give you a few
    pointers.

    Firstly it's nothing to do with destructors. Destructors for temporaries
    are called at the end of the containing expression.

    The number you are seeing is the address of the string "String here ".
    Remember that there are several sersions of operator<< some of them are
    member functions and some are global functions. The one you want to be
    called is this one, which prints a string

    ostream& ostream::operat or<<(const char*)

    the one that is being called is this one, which prints a pointer address

    ostream& operator<<(ostr eam&, const void*)

    In other words your expression is being interpretted like this

    operator<<(log( ), "String here ") << 12.33 << " and a number";

    when you would prefer to have it intepreted like this

    log().operator< <("String here ") << 12.33 << " and a number";

    Both these expression are legal, and if you write either one you get the
    expected output (pointer in the first case, string in the second
    case). Also if you write this


    log myLog;
    myLog << "String here " << 12.33 << " and a number";

    you get the desired string output.

    So the question is why does having a temporary of a derived class on the
    left hand side of operator<< make the compiler ignore member function of
    the base class?

    I have no idea what the answer is. I gave up on this kind of question a
    long time ago. A wise man said (I think it was Bjarne Stroustrup) 'stay
    away from the obscure corners of the language'. That would be my advice.

    I think it's perfectly acceptable to use macros for logging.

    #define LOG(X) std::cout << "The log contains: " << X << std::endl

    LOG("String here " << 12.33 << " and a number")

    That would also be my advice.

    john

    Comment

    • John Harrison

      #3
      Re: ostringstream derived problem

      >
      I think it's perfectly acceptable to use macros for logging.
      >
      #define LOG(X) std::cout << "The log contains: " << X << std::endl
      >
      LOG("String here " << 12.33 << " and a number")
      >
      Small tpyo, missing semi-colon

      LOG("String here " << 12.33 << " and a number");

      john

      Comment

      • iu2

        #4
        Re: ostringstream derived problem

        On Jul 1, 8:40 am, John Harrison <john_androni.. .@hotmail.comwr ote:
        I think it's perfectly acceptable to use macros for logging.
        >
        #define LOG(X) std::cout << "The log contains: " << X << std::endl
        >
        LOG("String here " << 12.33 << " and a number")
        >
        Small tpyo, missing semi-colon
        >
        LOG("String here " << 12.33 << " and a number");
        >
        john
        thanks, I'll use your macro suggestion.

        Comment

        • Kai-Uwe Bux

          #5
          Re: ostringstream derived problem

          iu2 wrote:
          Hi all, can someone help me with this?
          >
          I'm trying to shorthen some logging function in our project, i.e.,
          instead of
          >
          LogString("...) ;
          >
          use a more convenient
          >
          log() << "String here " << 12.33 << " and a number";
          >
          I create this syntax like this:
          >
          struct log : public ostringstream {
          virtual ~log() { cout << "The log contains: " << str().c_str() <<
          endl;}
          };
          >
          The trick I'm trying to do is that log() creates an object of type
          log, then it fills it using the ostringstream's parent '<<' and right
          after the statement finishes the destructor is called and the message
          is printed onto the screen.
          The problem is that it doesn't:
          It prints something like "The log contains: " 4129000
          But if the first element after "log() << " is not a string, e.g., a
          number, dec, hex or whatever, the scheme succeeds.
          >
          Why does this happen? I've got a feeling that the destructor might be
          called before the first "<< " but I don't understand why.
          The problem is that in C++ temporaries do not bind to non-const references.
          The line

          log() << "String here " << 12.33 << " and a number";

          creates a temporary log-object. When the compiler interprets the following
          operator<<, it has to chose between the freestanding versions

          ostream& operator<< ( ostream &, whatever)

          and the member functions in ostringstream. The freestanding versions cannot
          match since the temporary log() will not bind to the non-const stream&
          parameter. Thus, the compiler only considered member functions. The best
          match is

          operator<<( void* )

          and thus the char* argument is converted to a void* and then all bets are
          off as to what happens.

          I learned this here, too:




          Best

          Kai-Uwe Bux

          Comment

          • Kai-Uwe Bux

            #6
            Re: ostringstream derived problem

            John Harrison wrote:
            iu2 wrote:
            >Hi all, can someone help me with this?
            >>
            >I'm trying to shorthen some logging function in our project, i.e.,
            >instead of
            >>
            >LogString("... );
            >>
            >use a more convenient
            >>
            >log() << "String here " << 12.33 << " and a number";
            >>
            >I create this syntax like this:
            >>
            >struct log : public ostringstream {
            > virtual ~log() { cout << "The log contains: " << str().c_str() <<
            >endl;}
            >};
            >>
            >The trick I'm trying to do is that log() creates an object of type
            >log, then it fills it using the ostringstream's parent '<<' and right
            >after the statement finishes the destructor is called and the message
            >is printed onto the screen.
            >The problem is that it doesn't:
            >It prints something like "The log contains: " 4129000
            >But if the first element after "log() << " is not a string, e.g., a
            >number, dec, hex or whatever, the scheme succeeds.
            >>
            >Why does this happen? I've got a feeling that the destructor might be
            >called before the first "<< " but I don't understand why.
            >>
            >Thanks
            >>
            >
            Very subtle. I can't tell you the answer, although I can give you a few
            pointers.
            >
            Firstly it's nothing to do with destructors. Destructors for temporaries
            are called at the end of the containing expression.
            >
            The number you are seeing is the address of the string "String here ".
            Remember that there are several sersions of operator<< some of them are
            member functions and some are global functions. The one you want to be
            called is this one, which prints a string
            >
            ostream& ostream::operat or<<(const char*)
            >
            the one that is being called is this one, which prints a pointer address
            >
            ostream& operator<<(ostr eam&, const void*)
            >
            In other words your expression is being interpretted like this
            >
            operator<<(log( ), "String here ") << 12.33 << " and a number";
            >
            when you would prefer to have it intepreted like this
            >
            log().operator< <("String here ") << 12.33 << " and a number";
            >
            Both these expression are legal,
            Have you tested whether your compiler accepts both? I get an error for

            operator<<(log( ), "String here ")

            and so should you. log() is a temporary and that does not bind as the first
            parameter in operator<<().

            and if you write either one you get the
            expected output (pointer in the first case, string in the second
            case).
            I think your expectations are swapped. You get the void* conversion when the
            member function is called.

            Also if you write this
            >
            >
            log myLog;
            myLog << "String here " << 12.33 << " and a number";
            >
            you get the desired string output.
            >
            So the question is why does having a temporary of a derived class on the
            left hand side of operator<< make the compiler ignore member function of
            the base class?
            Actually, a temporary forces the compiler to use the member function since
            the freestanding version does not match.
            >
            I have no idea what the answer is. I gave up on this kind of question a
            long time ago. A wise man said (I think it was Bjarne Stroustrup) 'stay
            away from the obscure corners of the language'. That would be my advice.
            >
            I think it's perfectly acceptable to use macros for logging.
            >
            #define LOG(X) std::cout << "The log contains: " << X << std::endl
            >
            LOG("String here " << 12.33 << " and a number")
            >
            That would also be my advice.
            One could also provide a better matching member function:

            #include <sstream>
            #include <iostream>
            #include <ostream>

            struct log
            : public std::ostringstr eam
            {

            std::ostream& operator<< ( char const * msg ) {
            *this << std::string( msg );
            return ( *this );
            }

            virtual ~log() {
            std::cout
            << "The log contains: "
            << this->str().c_str( )
            << std::endl;
            }

            };


            int main ( void ) {
            log() << "String here ";
            }


            Best

            Kai-Uwe Bux

            Comment

            • John Harrison

              #7
              Re: ostringstream derived problem

              >
              Have you tested whether your compiler accepts both? I get an error for
              >
              operator<<(log( ), "String here ")
              >
              and so should you. log() is a temporary and that does not bind as the first
              parameter in operator<<().
              That's a good point. I did test it, but clearly not with extensions
              disabled.
              >
              >
              >and if you write either one you get the
              >expected output (pointer in the first case, string in the second
              >case).
              >
              I think your expectations are swapped. You get the void* conversion when the
              member function is called.
              That's what I said, I think.
              >
              >
              >Also if you write this
              >>
              >>
              >log myLog;
              >myLog << "String here " << 12.33 << " and a number";
              >>
              >you get the desired string output.
              >>
              >So the question is why does having a temporary of a derived class on the
              >left hand side of operator<< make the compiler ignore member function of
              >the base class?
              >
              Actually, a temporary forces the compiler to use the member function since
              the freestanding version does not match.
              >
              >I have no idea what the answer is. I gave up on this kind of question a
              >long time ago. A wise man said (I think it was Bjarne Stroustrup) 'stay
              >away from the obscure corners of the language'. That would be my advice.
              >>
              >I think it's perfectly acceptable to use macros for logging.
              >>
              >#define LOG(X) std::cout << "The log contains: " << X << std::endl
              >>
              >LOG("String here " << 12.33 << " and a number")
              >>
              >That would also be my advice.
              >
              One could also provide a better matching member function:
              >
              Good idea.

              john

              Comment

              • John Harrison

                #8
                Re: ostringstream derived problem

                John Harrison wrote:
                >>
                >Have you tested whether your compiler accepts both? I get an error for
                >>
                > operator<<(log( ), "String here ")
                >>
                >and so should you. log() is a temporary and that does not bind as the
                >first
                >parameter in operator<<().
                >
                That's a good point. I did test it, but clearly not with extensions
                disabled.
                >
                Maybe if I had tested it with extensions disabled the penny would have
                dropped. Lesson learned I hope.

                john

                Comment

                • James Kanze

                  #9
                  Re: ostringstream derived problem

                  On Jul 1, 7:43 am, iu2 <isra...@elbit. co.ilwrote:
                  Hi all, can someone help me with this?
                  I'm trying to shorthen some logging function in our project, i.e.,
                  instead of
                  LogString("...) ;
                  use a more convenient
                  log() << "String here " << 12.33 << " and a number";
                  I create this syntax like this:
                  struct log : public ostringstream {
                  virtual ~log() { cout << "The log contains: " << str().c_str() <<
                  endl;}
                  };
                  The trick I'm trying to do is that log() creates an object of type
                  log, then it fills it using the ostringstream's parent '<<' and right
                  after the statement finishes the destructor is called and the message
                  is printed onto the screen.
                  There are a couple of subtle issues involved here.
                  The problem is that it doesn't:
                  It prints something like "The log contains: " 4129000
                  But if the first element after "log() << " is not a string, e.g., a
                  number, dec, hex or whatever, the scheme succeeds.
                  Why does this happen? I've got a feeling that the destructor might be
                  called before the first "<< " but I don't understand why.
                  The destructor is called at the appropriate time; a much more
                  subtle issue is involved. Basically, some of the functions of
                  ostream are members, .e.g.:

                  ostream& ostream::operat or<<( int ) ;
                  ostream& ostream::operat or<<( void * ) ;

                  and others are free functions:

                  ostream& operator<<( ostream&, char const* ) ;

                  You're expression log() is a temporary: an rvalue, in the terms
                  of the standard. And temporaries of class type have a very
                  peculiar rule: you can call member functions on them, including
                  non-const member functions, but you cannot bind them to a
                  non-const reference. Given something like "log() << x", the
                  compiler first creates a list of callable functions, then
                  applies overload resolution. Once you've gotten over the
                  hurdle of the first function, you have an ostream&, not a
                  temporary log, by definition, a reference is an lvalue, and
                  since it is not const, all further operator << consider the full
                  set of visible operators.

                  In the classical IO streams, the operator << for char const* was
                  a member, and the "standard" work around was simply to start by
                  outputting a (possibly empty) string, e.g.:
                  log() << "" << myObjWithGlobal InjectionOperat or ;
                  The standards committee changed the char const* operator to a
                  free function, and broke this idiom. There are currently two
                  solutions:

                  -- use the member function flush(), e.g.:

                  log().flush() << "String here " << ...

                  -- or provide the operators in your log class, as member
                  templates:

                  class log
                  {
                  public:
                  // ...
                  template< typename T >
                  log&
                  operator<<( T const& object )
                  {
                  // Here, I'm not const! so...
                  myCollector << object ;
                  return *this ;
                  }

                  private:
                  std::ostringstr eam myCollector ;
                  } ;

                  In practice, the second solution is a lot friendlier to your
                  users, but will require a bit more work on your part. (In
                  practice, you'll generally have to provide some specializations
                  for the operator<< as well, to ensure that overload resolution
                  will work correctly in the expected cases.)

                  --
                  James Kanze (Gabi Software) email: james.kanze@gma il.com
                  Conseils en informatique orientée objet/
                  Beratung in objektorientier ter Datenverarbeitu ng
                  9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                  Comment

                  • iu2

                    #10
                    Re: ostringstream derived problem

                    On Jul 1, 9:01 am, Kai-Uwe Bux <jkherci...@gmx .netwrote:
                    iu2 wrote:
                    Hi all, can someone help me with this?
                    >
                    I'm trying to shorthen some logging function in our project, i.e.,
                    instead of
                    >
                    LogString("...) ;
                    >
                    use a more convenient
                    >
                    log() << "String here " << 12.33 << " and a number";
                    >
                    I create this syntax like this:
                    >
                    struct log : public ostringstream {
                    virtual ~log() { cout << "The log contains: " << str().c_str() <<
                    endl;}
                    };
                    >
                    The trick I'm trying to do is that log() creates an object of type
                    log, then it fills it using the ostringstream's parent '<<' and right
                    after the statement finishes the destructor is called and the message
                    is printed onto the screen.
                    The problem is that it doesn't:
                    It prints something like "The log contains: " 4129000
                    But if the first element after "log() << " is not a string, e.g., a
                    number, dec, hex or whatever, the scheme succeeds.
                    >
                    Why does this happen? I've got a feeling that the destructor might be
                    called before the first "<< " but I don't understand why.
                    >
                    The problem is that in C++ temporaries do not bind to non-const references.
                    The line
                    >
                    log() << "String here " << 12.33 << " and a number";
                    >
                    creates a temporary log-object. When the compiler interprets the following
                    operator<<, it has to chose between the freestanding versions
                    >
                    ostream& operator<< ( ostream &, whatever)
                    >
                    and the member functions in ostringstream. The freestanding versions cannot
                    match since the temporary log() will not bind to the non-const stream&
                    parameter. Thus, the compiler only considered member functions. The best
                    match is
                    >
                    operator<<( void* )
                    >
                    and thus the char* argument is converted to a void* and then all bets are
                    off as to what happens.
                    >
                    I learned this here, too:
                    >
                    http://groups.google.com/group/comp..../thread/9f553d...
                    >
                    Best
                    >
                    Kai-Uwe Bux- Hide quoted text -
                    >
                    - Show quoted text -

                    I've read your reply and James Kanze's reply, tried both solutions and
                    they worked. But I still don't get something, and I'll appreciate your
                    help:
                    You said that the best match is operator<<( void* );
                    Why is that? Isn't there any operator<<(char *) or operator<<(cons t
                    char *) member?

                    Comment

                    • James Kanze

                      #11
                      Re: ostringstream derived problem

                      On Jul 2, 9:38 am, iu2 <isra...@elbit. co.ilwrote:
                      On Jul 1, 9:01 am, Kai-Uwe Bux <jkherci...@gmx .netwrote:
                      [...]
                      I've read your reply and James Kanze's reply, tried both solutions and
                      they worked. But I still don't get something, and I'll appreciate your
                      help:
                      You said that the best match is operator<<( void* );
                      Why is that? Isn't there any operator<<(char *) or operator<<(cons t
                      char *) member?
                      Not any more. (There was in the classical iostream.)

                      A binary operator can be overloaded as either a member with a
                      single parameter (the this being the left operand, and the
                      parameter the right), or a free function taking two parameters.
                      According to the standard, the overload of << for void const*
                      (and the numeric types) is a member, the overload of << for char
                      const* a free function.

                      Because the rules for calling a member function are different
                      than those for binding to the reference argument of a free
                      function, if you have a temporary as the left argument, only the
                      member functions will be considered.

                      --
                      James Kanze (GABI Software) email:james.kan ze@gmail.com
                      Conseils en informatique orientée objet/
                      Beratung in objektorientier ter Datenverarbeitu ng
                      9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                      Comment

                      • iu2

                        #12
                        Re: ostringstream derived problem

                        On Jul 2, 11:37 am, James Kanze <james.ka...@gm ail.comwrote:
                        ....
                        Not any more. (There was in the classical iostream.)
                        >
                        A binary operator can be overloaded as either a member with a
                        single parameter (the this being the left operand, and the
                        parameter the right), or a free function taking two parameters.
                        According to the standard, the overload of << for void const*
                        (and the numeric types) is a member, the overload of << for char
                        const* a free function.
                        >
                        Because the rules for calling a member function are different
                        than those for binding to the reference argument of a free
                        function, if you have a temporary as the left argument, only the
                        member functions will be considered.
                        >
                        --
                        James Kanze (GABI Software) email:james.ka. ..@gmail.com
                        Conseils en informatique orientée objet/
                        Beratung in objektorientier ter Datenverarbeitu ng
                        9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
                        Thanks, all is clear now.

                        Comment

                        • iu2

                          #13
                          Re: ostringstream derived problem

                          On Jul 2, 1:08 pm, iu2 <isra...@elbit. co.ilwrote:
                          On Jul 2, 11:37 am, James Kanze <james.ka...@gm ail.comwrote:
                          ...
                          >
                          >
                          >
                          >
                          >
                          Not any more. (There was in the classical iostream.)
                          >
                          A binary operator can be overloaded as either a member with a
                          single parameter (the this being the left operand, and the
                          parameter the right), or a free function taking two parameters.
                          According to the standard, the overload of << for void const*
                          (and the numeric types) is a member, the overload of << for char
                          const* a free function.
                          >
                          Because the rules for calling a member function are different
                          than those for binding to the reference argument of a free
                          function, if you have a temporary as the left argument, only the
                          member functions will be considered.
                          >
                          --
                          James Kanze (GABI Software) email:james.ka. ..@gmail.com
                          Conseils en informatique orientée objet/
                          Beratung in objektorientier ter Datenverarbeitu ng
                          9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
                          >
                          Thanks, all is clear now.- Hide quoted text -
                          >
                          - Show quoted text -
                          Sorry, but there still one more thing...
                          Doing
                          log() << "" << "The string";
                          outputs "4129900TheStri ng"
                          Why?
                          Same thing for the following (a temprorary ostringstream)
                          cout << ((ostringstream &)(ostringstrea m() << " Hello!" << "
                          world")).str(). c_str() << endl;
                          which outputs
                          4219134 world

                          What does the first string output actually do? It seems that after
                          putputting the first string the binding of ostream to the free
                          operator <<(ostream &, const char *) occures.

                          I think the same thing happens with your first solution -
                          log().flush() << "the string"; Why does it work?
                          Thanks

                          Comment

                          • James Kanze

                            #14
                            Re: ostringstream derived problem

                            On Jul 3, 7:40 am, iu2 <isra...@elbit. co.ilwrote:
                            Sorry, but there still one more thing...
                            Doing
                            log() << "" << "The string";
                            outputs "4129900TheStri ng"
                            Why?
                            A priory, the first "" invokes the member operator<<( void
                            const* ), which outputs the actual address. Like all
                            operator<<, it returns a non-const reference to the stream, so
                            the second << can bind to the non-member function, which takes a
                            char const*, and outputs it as text.
                            Same thing for the following (a temprorary ostringstream)
                            cout << ((ostringstream &)(ostringstrea m() << " Hello!" << "
                            world")).str(). c_str() << endl;
                            which outputs
                            4219134 world
                            As above.
                            What does the first string output actually do?
                            The first << uses the temporary as its right argument, which
                            means that it cannot bind to the non-member functions. The best
                            member function is operator<<( void const* ), so that is what
                            operator overloading calls. The second << uses the return value
                            of the first << as its right hand operand. Since this is an
                            ostream&, it has no problem binding with the free functions as
                            well as the members; the free function operator<<(
                            std::ostream&, char const* ) is a better match, so it gets
                            chosen.
                            It seems that after
                            putputting the first string the binding of ostream to the free
                            operator <<(ostream &, const char *) occures.
                            Exactly.
                            I think the same thing happens with your first solution -
                            log().flush() << "the string"; Why does it work?
                            Because the left operand is NOT the temporary, but the return
                            value of the preceding function (e.g. the member operator<<, or
                            ostream::flush( )). Since the return value is a non-const
                            reference, it binds to the non-const reference of the free
                            function without any problems.

                            IMHO: given that some of the operator<< must be free functions
                            (those provided by the user for his types), it probably would
                            have been better to have made them all free functions, to avoid
                            such confusion. An even better solution would have been to make
                            the operator<< a member template, which the user specialized for
                            his own types, but the idiom was developped before member
                            templates, or even templates in general, existed, so this option
                            wasn't available.

                            --
                            James Kanze (GABI Software) email:james.kan ze@gmail.com
                            Conseils en informatique orientée objet/
                            Beratung in objektorientier ter Datenverarbeitu ng
                            9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


                            Comment

                            • iu2

                              #15
                              Re: ostringstream derived problem

                              On Jul 3, 2:14 pm, James Kanze <james.ka...@gm ail.comwrote:
                              ...
                              The first << uses the temporary as its right argument, which
                              means that it cannot bind to the non-member functions. The best
                              member function is operator<<( void const* ), so that is what
                              operator overloading calls. The second << uses the return value
                              of the first << as its right hand operand. Since this is an
                              ostream&, it has no problem binding with the free functions as
                              well as the members; the free function operator<<(
                              std::ostream&, char const* ) is a better match, so it gets
                              chosen.
                              Thanks, that gave me this idea that works too (it is not convenient to
                              use, but I tried it to see if I understood this thing):
                              ((ostream &)log()) << "The string";

                              iu2

                              Comment

                              Working...