Home > Mobile >  In C is there a way to call a function with a std::stringstream& argument without the caller using
In C is there a way to call a function with a std::stringstream& argument without the caller using

Time:01-30

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());
      |

CodePudding user response:

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)

CodePudding user response:

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;
}

CodePudding user response:

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

  • Related