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