skip to Main Content

In C++, is it possible to call a function whose argument is std::stringstream& without the caller declaring an explicit named variable of type std::stringstream?

I.e. I would like to accomplish something along the lines of this:

#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>

void func(const std::stringstream& ss) {
  std::cout << ss.str() << std::endl;
}

int main() {
  func("Hello World " << errno << "(" << strerror(errno) << ")");

  return 0;
}

The above code generates this compile error:

g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c: In function ‘int main()’:
./main.c:11:23: error: invalid operands of types ‘const char [13]’ and ‘int’ to binary ‘operator<<’
   11 |   func("Hello World " << errno << "(" << strerror(errno) << ")");
      |        ~~~~~~~~~~~~~~ ^~
      |        |
      |        const char [13]

(I’m not sure if I properly understand the problem — I think it is because the const char* "Hello World" is not implicitly cast to anything for which the << operator is applicable — is that correct?)

I tried this next:

#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>

void func(const std::stringstream& ss) {
  std::cout << ss.str() << std::endl;
}

int main() {
  func(std::stringstream("Hello World ") << errno << "(" << strerror(errno) << ")");

  return 0;
}

…which had an interesting result: on my PC, with g++ I got the following compile error:

$ g++ -g ./main.c && ./a.out
./main.c: In function ‘int main()’:
./main.c:19:77: error: invalid initialization of reference of type ‘const stringstream&’ {aka ‘const std::__cxx11::basic_stringstream<char>&’} from expression of type ‘std::basic_ostream<char>’
   19 |   func(std::stringstream("Hello World ") << errno << "(" << strerror(errno) << ")");
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
./main.c:6:36: note: in passing argument 1 of ‘void func(const stringstream&)’
    6 | void func(const std::stringstream& ss) {
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~^~

If I understand the compile error correctly, the value being passed to func() is of type std::basic_ostream<char> which cannot be implicitly cast to std::stringstream. Is this because the return-value of operator<< is type std::basic_ostream? (https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt)
The interesting part, though, is that at randomly-chosen online C++ compiler https://www.programiz.com/cpp-programming/online-compiler/, the same code compiles, however what gets printed to stdout is:

0(Success)d

…which seems to show the string "Hello World" being overwritten by the the value of errno << "(" << strerror(errno) << ")"
I presume this difference of behavior is due to different compilers, and I would like to understand if/why this is the case — but this is tangential to the specific question asked here, and I may ask it as a separate, specific question.

tl;dr: in the following code, function main() invokes function func() by using an explicit local, named variable:

#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>

void func(const std::stringstream& ss) {
  std::cout << ss.str() << std::endl;
}

int main() {
  std::stringstream ss;

  ss << "Hello World " << errno << "(" << strerror(errno) << ")";
  func(ss);

  return 0;
}
$ g++ -g ./main.c && ./a.out
Hello World 0(Success)

is there a way to invoke function func() without the the caller using an explicit, named variable?


Update: I tried the following based on an answer-as-comment:

#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>

void func(const std::stringstream& ss) {
  std::cout << ss.str() << std::endl;
}

int main() {
  func(std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")");

  return 0;
}

…which resulted in this compile-error:

$ g++ -g ./main.c && ./a.out
./main.c: In function ‘int main()’:
./main.c:46:81: error: invalid initialization of reference of type ‘const stringstream&’ {aka ‘const std::__cxx11::basic_stringstream<char>&’} from expression of type ‘std::basic_ostream<char>’
   46 |   func(std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")");
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
./main.c:6:36: note: in passing argument 1 of ‘void func(const stringstream&)’
    6 | void func(const std::stringstream& ss) {
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~^~

…which looks the same as when I tried func(std::stringstream("Hello World ") << errno << "(" << strerror(errno) << ")");


Update: I tried the following based on an answer-as-comment:

#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>

void func(const std::stringstream& ss) {
  std::cout << ss.str() << std::endl;
}

int main() {
  func((std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")").str());

  return 0;
}

…which resulted in this compile error:

$ g++ -g ./main.c && ./a.out
./main.c: In function ‘int main()’:
./main.c:11:90: error: ‘class std::basic_ostream<char>’ has no member named ‘str’
   11 |   func((std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")").str());
      |

3

Answers


  1. One possibility would be to use a string instead of a stringstream:

    func(std::string("Hello World ") + std::to_string(errno) + "(" + strerror(errno) + ")");
    

    …or possibly, if you don’t mind one extra line elsewhere:

    using namespace std::literals;
    
    // ...
    
    func("Hello World "s + std::to_string(errno) + "(" + strerror(errno) + ")");
    

    Either way, you would need to rewrite func to receive an std::string (or a std::string const &) instead of an std::stringstream (but given that all func is doing with it is calling str() to get a string, that doesn’t seem like it should be a problem).

    Another possibility would be to rewrite func to to be a variadic template:

    template <typename... Args>
    void func(Args&&... args) {    
        ((std::cout << std::forward<Args>(args)), ...);
        // or if you want writing to the stream to be atomic, write them
        // to a stringstream then write that to std::cout:
    //    std::stringstream foo;
    //    ((foo << std::forward<Args>(args)), ...);
    //    std::cout << foo.str();
    }
    
    int main() { 
        func("Hello World ", errno, " (", strerror(errno), ")");
    }
    

    …which produces the expected result:

    Hello World 0 (Success)
    
    Login or Signup to reply.
  2. May be not what you are looking for, but sometimes it used this way:

    #include <cerrno>
    #include <cstring>
    #include <iostream>
    #include <sstream>
    
    namespace { // Just to hide from other modules
    class mystream : public std::stringstream
    {
    public:
        ~mystream() {
            std::cout << str() << std::endl;
        }
    };
    }
    
    mystream func() {
      return {};
    }
    
    int main() {
      func() << "Hello World " << errno << "(" << strerror(errno) << ")";
    
      return 0;
    }
    

    As a sidnote, C++ has its own mechanisms to handle errno, you can try to use it this way:

    #include <system_error>
    #include <iostream>
    #include <sstream>
    
    class mystream : public std::stringstream
    {
    public:
        ~mystream() {
            std::cout << str() << std::endl;
        }
    };
    
    mystream func() {
      return {};
    }
    
    int main() {
      // Capture error for possible transmission and later processing
      std::error_code err{errno, std::system_category()};
    
      // You can compare it to platform-independent enum
      if (err == std::errc::invalid_argument)
        return 1;
    
      // You can print it
      func() << "Hello World " << err.value() << "(" << err.message() << ")";
    
      return 0;
    }
    
    Login or Signup to reply.
  3. You can define a user defined literal.

    #include <cstring>
    #include <iostream>
    #include <sstream>
    #include <string>
    
    std::stringstream operator ""_ss(const char* str, size_t size) {
      std::stringstream ss;
      ss.write(str, size);
      return ss;
    }
    
    void func(const std::stringstream& ss) {
      std::cout << ss.str() << std::endl;
    }
    
    int main() {
      func("Hello World "_ss << errno << " (" << std::strerror(errno) << ")");
    }
    
    // Output:
    // Hello World 0 (Success)
    

    https://godbolt.org/z/bn7sq97Wq

    As for the compiler error in your second attempt

    func((std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")").c_str());
    //                                                                                     ^^^^^^^
    //                                                                      Absolutely unnecessary:
    func(std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")");
    

    It does not work until GCC 11.2. std::basic_stringstream inherits std::basic_ostream and the returned type of its std::basic_ostream<CharT,Traits>::operator<< is basic_ostream& that is not suitable for const std::stringstream&. The work around is the modified func():

    #include <cstring>
    #include <iostream>
    #include <sstream>
    #include <string>
    
    std::stringstream operator ""_ss(const char* str, size_t size) {
      std::stringstream ss;
      ss.write(str, size);
      return ss;
    }
    
    void func(const std::ostream& ss) {
      std::cout << ss.rdbuf() << std::endl;
    }
    
    int main() {
      func("Hello World "_ss << errno << " (" << std::strerror(errno) << ")");
    }
    

    https://godbolt.org/z/szqvrcbW8

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search