I have this code:
std::wstringstream outstream;
outstream << (prop.m_pwszOriginalVolumeName
? prop.m_pwszOriginalVolumeName
: L"null") << L";"
<< (prop.m_pwszSnapshotDeviceObject
? prop.m_pwszSnapshotDeviceObject
: L"null") << L";"
<< (prop.m_pwszOriginatingMachine
? prop.m_pwszOriginatingMachine
: L"null") << L";"
<< ... // some more strings here
Is there a way to avoid code duplication and still have concise code?
You could define a small function:
whatever_t strOrNull(whatever_t str) {
return str ? str : L"null";
}
Then your code becomes
std::wstringstream outstream;
outstream << strOrNull(prop.m_pwszOriginalVolumeName) << L";"
<< strOrNull(prop.m_pwszSnapshotDeviceObject) << L";"
<< strOrNull(prop.m_pwszOriginatingMachine) << L";"
<< ... // some more strings here
Or if you wanted to be even more concise, you could do this (depending on what whatever_t is; if wstringstream already has an operator<< overload for that type, this won't work):
wstringstream& operator<<(wstringstream& out, whatever_t str) {
if (str)
out << str;
else
out << L"null";
return out;
}
Then your code becomes
std::wstringstream outstream;
outstream << prop.m_pwszOriginalVolumeName << L";"
<< prop.m_pwszSnapshotDeviceObject << L";"
<< prop.m_pwszOriginatingMachine << L";"
<< ... // some more strings here
A function, or a lambda:
auto foo = [](const wchar * p) { return p ? p : L"null;" };
outstream << foo(prop.m_pwszOriginalVolumeName) << L";"
<< foo(prop.m_pwszSnapshotDeviceObject) << L";"
<< ...etc...
The other examples are really good. There is another option, though I wouldn't recommend it (only mentioning for completeness).
GCC has an extension called "Conditionals with Omitted Operands" Which basically looks like this:
x = a ?: b;
which is the same as (in simple cases like yours, see below for more info):
x = a ? a : b;
Just less portable. So you could write:
std::wstringstream outstream;
outstream << (prop.m_pwszOriginalVolumeName ?: L"null") << L";"
<< (prop.m_pwszSnapshotDeviceObject ?: L"null") << L";"
<< (prop.m_pwszOriginatingMachine ?: L"null") << L";"
But like I said, I would not recommend this, I would use a helper function like the other answers mention.
There actually is a case where it performs differently than a regular ternary if, and that's if evaluating a has side effects. From the page:
In this simple case, the ability to omit the middle operand is not
especially useful. When it becomes useful is when the first operand
does, or may (if it is a macro argument), contain a side effect. Then
repeating the operand in the middle would perform the side effect
twice. Omitting the middle operand uses the value already computed
without the undesirable effects of recomputing it.
See http://gcc.gnu.org/onlinedocs/gcc/Conditionals.html
A simple function should do the trick.
wchar_t* filterNullString(wchar_t* str)
{
static wchar const* nullStr = L"null";
return str ? str : nullStr;
}
std::wstringstream outstream;
outstream << filterNullString(prop.m_pwszOriginalVolumeName) << L";"
<< filterNullString(prop.m_pwszSnapshotDeviceObject)<< L";"
<< filterNullString(prop.m_pwszOriginatingMachine)<< L";" ;
You could use a helper function:
const wchar_t *SafeOutput(const wchar_t *str)
{
return str ? str : L"null";
}
// Analogous function for ANSI strings
...
outstream << SafeOutput(prop.m_pwszOriginalVolumeName) << L";"
<< SafeOutput(prop.m_pwszSnapshotDeviceObject) << L";"
<< SafeOutput(prop.m_pwszOriginatingMachine) << L";"
<< ... // more strings here
Related
I have written a function MakeHashKeyFromFileKey16_FIRST() which works fine. Then I replaced Context_ws in line 6 of MakeHashKeyFromFileKey16_SECOND(). Suddenly my code does not work correctly.
Can anybody give me a hint as to why that is?
typedef wchar_t KMX_WCHART;
typedef uint16_t* PKMX_WCHAR ;
std::wstring MakeHashKeyFromFileKey16_FIRST(PFILE_KEY kp) {
std::wstringstream Key_16;
std::wstring Context_ws = u16fmt((const PKMX_WCHAR) kp->dpContext);
Key_16 << conv_KMX_WCHAR__TO__WCHAR(kp->Key) << "," << (DWORD) kp->ShiftFlags << ",";
if ((KMX_WCHART*) Context_ws.c_str()) Key_16 << (KMX_WCHART*) Context_ws.c_str();
return Key_16.str();
};
std::wstring MakeHashKeyFromFileKey16_SECOND(PFILE_KEY kp) {
std::wstringstream Key_16;
std::wstring Context_ws = u16fmt((const PKMX_WCHAR) kp->dpContext);
Key_16 << conv_KMX_WCHAR__TO__WCHAR(kp->Key) << "," << (DWORD) kp->ShiftFlags << ",";
if ((KMX_WCHART*) u16fmt((const PKMX_WCHAR) kp->dpContext).c_str()) Key_16 << (KMX_WCHART*) u16fmt((const PKMX_WCHAR) kp->dpContext).c_str();
return Key_16.str();
};
I have a method to log with the following definition:
void log(std::string s) {
std::string tag = "main";
std::cout << tag << " :" << s << std::endl;
}
I'm trying to call this method like this:
log("direction" << std::to_string(direction) << ", count: " << std::to_string(count));
direction and count are integers.
I'm getting this following error with << underlined in red:
no operator << matches these operands.
operand types are const char [10] << std::string
I have #include<string> in my header to make sure my strings are working as they should.
I tried std::string("direction") and still the issue was same.
Beginner in C++. Help would be appreciated.
operator<< isn't used for arbitrary string concatenation - it is called an "output stream operator", and it is only used in the context of std::ostream.
When you say...
std::cout << tag << " :" << s << std::endl;
...you're actually writing code roughly equivalent to:
std::cout.operator<<(tag).operator<<(" :").operator<<(s).operator<<(std::endl);
As you can see operator<< knows how to work with std::cout and std::string, but not between strings.
In order to concatenate std::string instances, you can simply use operator+:
log("direction" + std::to_string(direction) + ", count: " + std::to_string(count));
Please note that this concatenation technique is not the most efficient: you might want to look into std::stringstream or simply use std::string::reserve to avoid unnecessary memory allocations.
Substitute the << with the + operator as you are manipulating the string, not the stream:
log("direction" + std::to_string(direction) + ", count: " + std::to_string(count));
If you're determined to use the operator<< notation you need an object that understands it.
Here's such an object (I make no claims that this is a good idea):
#include <string>
#include <sstream>
#include <iostream>
void log(std::string s) {
std::string tag = "main";
std::cout << tag << " :" << s << std::endl;
}
struct string_accumulator
{
std::ostringstream ss;
template<class T>
friend string_accumulator& operator<<(string_accumulator& sa, T const& value)
{
sa.ss << value;
return sa;
}
template<class T>
friend string_accumulator& operator<<(string_accumulator&& sa, T const& value)
{
return operator<<(sa, value);
}
operator std::string () { return ss.str(); }
};
inline auto collect() -> string_accumulator
{
return string_accumulator();
}
int main()
{
int direction = 1;
int count = 1;
log(collect() << "direction" << std::to_string(direction) << ", count: " << std::to_string(count));
}
The prototype of your function is void log(std::string s);. It awaits for an std::string. So you need to pass a string to it, not a stream!
So, change this:
log("direction" << std::to_string(direction) << ", count: " << std::to_string(count));
to this:
log("direction" + std::to_string(direction) + ", count: " + std::to_string(count));
where I only changed the << operator to + operator. It will now concatenate everything inside the parentheses to a single std::string.
Your attempt implies that you wanted to pass std::ostream as the parameter. Maybe you want to read C++ Passing ostream as parameter. However, if I were you, I would just overload <<.
why don't you use:
// just include thisusing namespace std;
I often end up writing code like this:
SomeStreamableType x;
std::stringstream ss;
ss << "Value is: " << x;
log(ss.str());
The extra line needed to generate the stringstream feels superfulous. I can do this, but it's equally cumbersom:
SomeStreamableType x;
const std::string str = "Value is: " + boost::lexical_cast<std::string>(x);
log(str);
I want to be able to do this:
SomeStreamableType x;
log(std::stringstream() << "Value is: " << x);
Have others encountered this issue and come up with a workaround? I don't want to create any helper functions or classes.
Your code will work without modifications, as long as log accepts an ostream& reference:
void log(ostream& o) {
stringstream* s = dynamic_cast<stringstream*>(&o);
if (s) {
cout << s->str() << endl;
}
}
int main() {
int x = 5, y = 6;
log(stringstream() << "x=" << x << ", y=" << y);
return 0;
}
Demo.
To solve this problem I have often simply done something like this:
#define LOG(m) do{std::ostringstream oss;oss<<m;std::cout<<oss.str()<<'\n';}while(0)
// ...
LOG("some text: " << value1 << ' ' << value2); // no need for '\n'
Now I tend to use a more sophisticated class based solution that has an even nicer interface and doesn't use a horrible macro.
When you're overloading the << operator for a class (pretend this is defined as a friend in SomeClass), why do you both take a reference to the ostream and return that ostream?
ostream& operator<<(ostream& s, const SomeClass& c) {
//whatever
return s;
}
What benefit can returning the ostream be when it was already directly modifiable by reference? This seems redundant to me - though I'm sure it's not :)
It allows to "chain" output together. As in :
std::cout << someObj << someValue;
This is equivalent to something like :
operator<<(operator<<(std::cout, someObj), someValue);
This is not redundant, but useful for chaining calls. It's easier to see with functions like std::string::append, so I'll start with that:
std::string mystring("first");
mystring.append(" second");
mystring.append(" third");
can be rewritten as:
std::string mystring("first").append(" second").append(" third");
This is possible because .append() returns a reference to the string it modified, so we can keep adding .append(...) to the end. The code correlating to what you are doing is changing from this:
std::cout << "first";
std::cout << " second";
std::cout << " third";
into this. Since operator<< returns the stream, we can also chain these!
std::cout << "first" << " second" << " third";
see the similarity, and usefulness?
So that you can write chained-invocation of operator<< as:
stream << s1 << s2 << s3 ;
If you don't return ostream&, then you cannot write it more than once.
You can think of that either as:
operator<<(operator<<(operator<<(stream, s1), s2), s3);
Or as,
((stream << s1) << s2) << s3 ;
First (stream << s1) returns stream& on which you again invoke << and it becomes stream << s2 which returns stream& on which you again invoke << and it becomes stream << s3.
Ah, this is so linked output, like
cout << "this " << "is " << "a pen" << endl;
will still work.
I have an application in which I need to combine strings within a variable like so:
int int_arr[4];
int_arr[1] = 123;
int_arr[2] = 456;
int_arr[3] = 789;
int_arr[4] = 10;
std::string _string = "Text " + int_arr[1] + " Text " + int_arr[2] + " Text " + int_arr[3] + " Text " + int_arr[4];
It gives me the compile error
Error C2210: '+' Operator cannot add pointers" on the second string of the expression.
As far as I can tell I am combining string literals and integers, not pointers.
Is there another concatenation operator that I should be using? Or is the expression just completely wrong and should figure out another way to implement this?
BTW I am using Visual Studio 2010
Neither C nor C++ allow concatenation of const char * and int. Even C++'s std::string, doesn't concatenate integers. Use streams instead:
std::stringstream ss;
ss << "Text " << int_arr[1] << " Text " << int_arr[2] << " Text " << int_arr[3] << " Text " << int_arr[4];
std::string _string = ss.str();
You can do this in Java since it uses the toString() method automatically on each part.
If you want to do it the same way in C++, you'll have to explicitly convert those integer to strings in order for this to work.
Something like:
#include <iostream>
#include <sstream>
std::string intToStr (int i) {
std::ostringstream s;
s << i;
return s.str();
}
int main (void) {
int var = 7;
std::string s = "Var is '" + intToStr(var) + "'";
std::cout << s << std::endl;
return 0;
}
Of course, you can just use:
std::ostringstream os;
os << "Var is '" << var << "'";
std::string s = os.str();
which is a lot easier.
A string literal becomes a pointer in this context. Not a std::string. (Well, to be pedantically correct, string literals are character arrays, but the name of an array has an implicit conversion to a pointer. One predefined form of the + operator takes a pointer left-argument and an integral right argument, which is the best match, so the implicit conversion takes place here. No user-defined conversion can ever take precedence over this built-in conversion, according to the C++ overloading rules.).
You should study a good C++ book, we have a list here on SO.
A string literal is an expression returning a pointer const char*.
std::stringstream _string_stream;
_string_stream << "Text " << int_arr[1] << " Text " << int_arr[2] << " Text " << int_arr[3] << " Text " << int_arr[4];
std::string _string = _string_stream.str();