C++ cout and enum representations - c++

I have an enum that I'm doing a cout on, as in: cout << myenum.
When I debug, I can see the value as an integer value but when cout spits it out, it shows up as the text representation.
Any idea what cout is doing behind the scenes? I need that same type of functionality, and there are examples out there converting enum values to string but that seems like we need to know what those values are ahead of time. In my case, I don't. I need to take any ol' enum and get its text representation. In C# it's a piece of cake; C++.. not easy at all.
I can take the integer value if I need to and convert it appropriately, but the string would give me exactly what I need.
UPDATE:
Much thanks to everyone that contributed to this question. Ultimately, I found my answer in some buried code. There was a method to convert the enum value to a string representing the actual move like "exd5" what have ya. In this method though they were doing some pretty wild stuff which I'm staying away form at the moment. My main goal was to get to the string representation.

Enum.hpp:
enum Enum {
FOO,
BAR,
BAZ,
NUM_ENUMS
};
extern const char* enum_strings[];
Enum.cpp:
const char* enum_strings[] = {
"FOO",
"BAR",
"BAZ",
"NUM_ENUMS",
0 };
Then when I want to output the symbolic representation of the enum, I use std::cout << enum_strings[x].
Thus, you do need to know the string values, but only in one place—not everywhere you use this.

This functionality comes from the IOStreams library. std::cout is an std::ostream.
std::stringstream is an std::ostream too.
int x = 5;
std::stringstream ss;
ss << x;
// ss.str() is a string containing the text "5"

Related

Defining a character array in header file in C++

I am working on a project where a question is displayed in a game window.
Since this question will need to change a lot, I figure it would be easier to have 5 defined lines of text (1 for question, 4 for MC answers) that are simply edited every time the question changes.
I have tried this in the header file:
struct tagQuestion{
int x, y;
const char* qLine[150];
char ansA[150];
char ansB[150];
char ansC[150];
char ansD[150];
}question[1];
then in my main.cpp
question.qLine[150] = "TEST PHRASE";
but it is returning the error "qLine" in "question", which is of non-class type "tagQuestion[1]"
I have tried both char and const char* to no success.
I am trying to follow an example in my textbook and I think I'm misunderstanding it.
Once I declare the character array in the header file, can't I edit its contents in the main file?
That [1] after question doesn't seem to be making any sense. Remove it.
Your answers are arrays of char, but your question is an array of char pointers. I am pretty sure you don't want your question to consist of 150 char pointers.
If you want to change qLine, don't make it const.
qLine[150] = ... This assigns a value to the 151st element of an array with 150 elements. Neither what you want, nor legal code. You probably want qLine =, but that doesn't work with an array, instead you need to use a function like strcpy that writes data into the array, but I highly advise against that. (see next point)
Don't do C in C++. Use std::string, not arrays of char.
If you have strings that regularly change, create a file where your strings are stored, load them into a map, and pull them from the map wherever and whenever you need them, instead of putting hardcoded strings in your code. That way you don't need to change any code, when you change text.
I suggest something like this:
struct Question {
std::string text;
std::vector<std::string> answers;
};
std::map<std::string, Question> questions;
And then something like this:
ifstream questionsFile("questions.txt");
if (questionsFile.is_open())
{
std::string line;
while (std::getline(questionsFile, line))
{
// Split line into some id, text and answers, how exactly depends on the format you chose
std::string id = ...;
Question question;
question.text = ...;
question.answers.push_back(...);
questions[id] = question;
}
questionsFile.close();
}
else
{
// Handle error
}
And wherever in your code:
// Print question with id "q1":
std::cout << questions["q1"].text << std::endl;
// Print answer #2 for question with id "q1":
std::cout << questions["q1"].answers[1] << std::endl;
Even better would be to make Question a class that hides its internals and has accessor methods and a constructor or static method to create new instances from valid lines with proper error handling.

How to cleanly use: const char* and std::string?

tl:dr
How can I concatenate const char* with std::string, neatly and
elegantly, without multiple function calls. Ideally in one function
call and have the output be a const char*. Is this impossible, what
is an optimum solution?
Initial Problem
The biggest barrier I have experienced with C++ so far is how it handles strings. In my opinion, of all the widely used languages, it handles strings the most poorly. I've seen other questions similar to this that either have an answer saying "use std::string" or simply point out that one of the options is going to be best for your situation.
However this is useless advice when trying to use strings dynamically like how they are used in other languages. I cannot guaranty to always be able to use std::string and for the times when I have to use const char* I hit the obvious wall of "it's constant, you can't concatenate it".
Every solution to any string manipulation problem I've seen in C++ requires repetitive multiple lines of code that only work well for that format of string.
I want to be able to concatenate any set of characters with the + symbol or make use of a simple format() function just how I can in C# or Python. Why is there no easy option?
Current Situation
Standard Output
I'm writing a DLL and so far I've been output text to cout via the << operator. Everything has been going fine so far using simple char arrays in the form:
cout << "Hello world!"
Runtime Strings
Now it comes to the point where I want to construct a string at runtime and store it with a class, this class will hold a string that reports on some errors so that they can be picked up by other classes and maybe sent to cout later, the string will be set by the function SetReport(const char* report). So I really don't want to use more than one line for this so I go ahead and write something like:
SetReport("Failure in " + __FUNCTION__ + ": foobar was " + foobar + "\n"); // __FUNCTION__ gets the name of the current function, foobar is some variable
Immediately of course I get:
expression must have integral or unscoped enum type and...
'+': cannot add two pointers
Ugly Strings
Right. So I'm trying to add two or more const char*s together and this just isn't an option. So I find that the main suggestion here is to use std::string, sort of weird that typing "Hello world!" doesn't just give you one of those in the first place but let's give it a go:
SetReport(std::string("Failure in ") + std::string(__FUNCTION__) + std::string(": foobar was ") + std::to_string(foobar) + std::string("\n"));
Brilliant! It works! But look how ugly that is!! That's some of the ugliest code I've every seen. We can simplify to this:
SetReport(std::string("Failure in ") + __FUNCTION__ + ": foobar was " + std::to_string(foobar) + "\n");
Still possibly the worst way I've every encounter of getting to a simple one line string concatenation but everything should be fine now right?
Convert Back To Constant
Well no, if you're working on a DLL, something that I tend to do a lot because I like to unit test so I need my C++ code to be imported by the unit test library, you will find that when you try to set that report string to a member variable of a class as a std::string the compiler throws a warning saying:
warning C4251: class 'std::basic_string<_Elem,_Traits,_Alloc>' needs to have dll-interface to be used by clients of class'
The only real solution to this problem that I've found other than "ignore the warning"(bad practice!) is to use const char* for the member variable rather than std::string but this is not really a solution, because now you have to convert your ugly concatenated (but dynamic) string back to the const char array you need. But you can't just tag .c_str() on the end (even though why would you want to because this concatenation is becoming more ridiculous by the second?) you have to make sure that std::string doesn't clean up your newly constructed string and leave you with garbage. So you have to do this inside the function that receives the string:
const std::string constString = (input);
m_constChar = constString.c_str();
Which is insane. Because now I traipsed across several different types of string, made my code ugly, added more lines than should need and all just to stick some characters together. Why is this so hard?
Solution?
So what's the solution? I feel that I should be able to make a function that concatenates const char*s together but also handle other object types such as std::string, int or double, I feel strongly that this should be capable in one line, and yet I'm unable to find any examples of it being achieved. Should I be working with char* rather than the constant variant, even though I've read that you should never change the value of char* so how would this help?
Are there any experienced C++ programmers who have resolved this issue and are now comfortable with C++ strings, what is your solution? Is there no solution? Is it impossible?
The standard way to build a string, formatting non-string types as strings, is a string stream
#include <sstream>
std::ostringstream ss;
ss << "Failure in " << __FUNCTION__ << ": foobar was " << foobar << "\n";
SetReport(ss.str());
If you do this often, you could write a variadic template to do that:
template <typename... Ts> std::string str(Ts&&...);
SetReport(str("Failure in ", __FUNCTION__, ": foobar was ", foobar, '\n'));
The implementation is left as an exercise for the reader.
In this particular case, string literals (including __FUNCTION__) can be concatenated by simply writing one after the other; and, assuming foobar is a std::string, that can be concatenated with string literals using +:
SetReport("Failure in " __FUNCTION__ ": foobar was " + foobar + "\n");
If foobar is a numeric type, you could use std::to_string(foobar) to convert it.
Plain string literals (e.g. "abc" and __FUNCTION__) and char const* do not support concatenation. These are just plain C-style char const[] and char const*.
Solutions are to use some string formatting facilities or libraries, such as:
std::string and concatenation using +. May involve too many unnecessary allocations, unless operator+ employs expression templates.
std::snprintf. This one does not allocate buffers for you and not type safe, so people end up creating wrappers for it.
std::stringstream. Ubiquitous and standard but its syntax is at best awkward.
boost::format. Type safe but reportedly slow.
cppformat. Reportedly modern and fast.
One of the simplest solution is to use an C++ empty string. Here I declare empty string variable named _ and used it in front of string concatenation. Make sure you always put it in the front.
#include <cstdio>
#include <string>
using namespace std;
string _ = "";
int main() {
char s[] = "chararray";
string result =
_ + "function name = [" + __FUNCTION__ + "] "
"and s is [" + s + "]\n";
printf( "%s", result.c_str() );
return 0;
}
Output:
function name = [main] and s is [chararray]
Regarding __FUNCTION__, I found that in Visual C++ it is a macro while in GCC it is a variable, so SetReport("Failure in " __FUNCTION__ "; foobar was " + foobar + "\n"); will only work on Visual C++. See: https://msdn.microsoft.com/en-us/library/b0084kay.aspx and https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
The solution using empty string variable above should work on both Visual C++ and GCC.
My Solution
I've continued to experiment with different things and I've got a solution which combines tivn's answer that involves making an empty string to help concatenate long std::string and character arrays together and a function of my own which allows single line copying of that std::string to a const char* which is safe to use when the string object leaves scope.
I would have used Mike Seymour's variadic templates but they don't seem to be supported by the Visual Studio 2012 I'm running and I need this solution to be very general so I can't rely on them.
Here is my solution:
Strings.h
#ifndef _STRINGS_H_
#define _STRINGS_H_
#include <string>
// tivn's empty string in the header file
extern const std::string _;
// My own version of .c_str() which produces a copy of the contents of the string input
const char* ToCString(std::string input);
#endif
Strings.cpp
#include "Strings.h"
const std::string str = "";
const char* ToCString(std::string input)
{
char* result = new char[input.length()+1];
strcpy_s(result, input.length()+1, input.c_str());
return result;
}
Usage
m_someMemberConstChar = ToCString(_ + "Hello, world! " + someDynamicValue);
I think this is pretty neat and works in most cases. Thank you everyone for helping me with this.
As of C++20, fmtlib has made its way into the ISO standard but, even on older iterations, you can still download and use it.
It gives similar capabilities as Python's str.format()(a), and your "ugly strings" example then becomes a relatively simple:
#include <fmt/format.h>
// Later on, where code is allowed (inside a function for example) ...
SetReport(fmt::format("Failure in {}: foobar was {}\n", __FUNCTION__, foobar));
It's much like the printf() family but with extensibility and type safety built in.
(a) But, unfortunately, not its string interpolation feature (use of f-strings), which has the added advantage of putting the expressions in the string at the place where they're output, something like:
set_report(f"Failure in {__FUNCTION__}: foobar was {foobar}\n");
If fmtlib ever got that capability, I'd probably wet my pants in excitement :-)

A story of stringstream, hexadecimal and characters

I have a string containing hexadecimal values (two characters representing a byte). I would like to use std::stringstream to make the conversion as painless as possible, so I came up with the following code:
std::string a_hex_number = "e3";
{
unsigned char x;
std::stringstream ss;
ss << std::hex << a_hex_number;
ss >> x;
std::cout << x << std::endl;
}
To my biggest surprise this prints out "e" ... Of course I don't give up so easily, and I modify the code to be:
{
unsigned short y;
std::stringstream ss;
ss << std::hex << a_hex_number;
ss >> y;
std::cout << y << std::endl;
}
This, as expected, prints out 227 ...
I looked at http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/ and http://www.cplusplus.com/reference/ios/hex/ but I just could not find a reference which tells me more about why this behaviour comes ...(yes, I feel that it is right because when extracting a character it should take one character, but I am a little bit confused that std:hex is ignored for characters). Is there a mention about this situation somewhere?
(http://ideone.com/YHt7Fz)
Edit I am specifically interested if this behaviour is mentioned in any of the STL standards.
If I understand correctly, you're trying to convert a string in
hex to an unsigned char. So for starters, since this is
"input", you should be using std::istringstream:
std::istringstream ss( a_hex_number );
ss >> std::hex >> variable;
Beyond that, you want the input to parse the string as an
integral value. Streams do not consider character types as
numeric values; they read a single character into them (after
skipping leading white space). To get a numeric value, you
should input to an int, and then convert that to unsigned
char. Characters don't have a base, so std::hex is
irrelevant for them. (The same thing holds for strings, for
example, and even for floating point.)
With regards to the page you site: the page doesn't mention
inputting into a character type (strangely enough, because it
does talk about all other types, including some very special
cases). The documentation for the std::hex manipulator is
also weak: in the running text, it only says that "extracted
values are also expected to be in hexadecimal base", which isn't
really correct; in the table, however, it clearly talks about
"integral values". In the standard, this is documented in
§27.7.2.2.3. (The >> operators for character types are not
member functions, but free functions, so are defined in
a different section.) What we are missing, however, is a good
document which synthesizes these sort of things: whether the
>> operator is a member or a free function doesn't really
affect the user much; you want to see all of the >> available,
with their semantics, in one place.
Let's put it simple: variable type is 'stronger' than 'hex'. That's why 'hex' is ignored for 'char' variable.
Longer story:
'Hex' modifies internal state of stringstream object telling it how to treat subsequent operations on integers. However, this does not apply to chars.
When you print out a character (i.e. unsigned char), it's printed as a character, not as a number.

Create a C++ string with given format

I'm a Objective-C dev and sometimes I have to deal with C/C++ code.
I have a function written in C++, it logs an event with name, for example, Level_12_Pack_10. I want to create a dynamic C++ string like that, then I can change level and pack numbers.
In Objective C, it's easy with some lines of code: [NSString stringwithformat] but in C++, it's a bit difficult for me.
Could anyone help me do it?
I don't think C++ supports strings with built-in changeable variables like that. It would be too over-the-top to make a class to format the string like that. Probably the closest thing you can get is to use stringstreams:
#include <sstream>
string makeMyString(int level, int pack) {
stringstream ss;
ss << "Level_" << level << "_Pack_" << pack;
return ss.str();
}
If you have a string that you need to read and change the values inside, a similar function could be used.
With c++11, it is drop dead simple just use std::to_string(level) etc to concatenate strings.
int level = 10;
int pack = 40;
std::string stuff = "Level_" + std::to_string(level) + "_Pack_" + std::to_string(pack);
//stuff is now "Level_10_Pack_40"
std::cout << stuff;

QSettings does not differentiate between string and int values

I am writing and reading string and int values using a file-backed QSettings object.
When I later try to read the values from a different process, the values are read as strings instead of int.
This is the code I am using to write values:
QSettings settings("TestQSettings.ini", QSettings::IniFormat);
settings.setValue("AAA",QString("111"));
settings.setValue("BBB",222);
This is the file created:
[General]
AAA=111
BBB=222
This is the code I am using to read values:
QVariant qvar = settings.value("AAA");
std::cout << "AAA type " << qvar.type() << std::endl;
qvar = settings.value("BBB");
std::cout << "BBB type " << qvar.type() << std::endl;
If I run this code from the same process:
AAA type 10
BBB type 2
If I run this code from a different process:
AAA type 10
BBB type 10
I know it's possible to convert the types after they have been read.
Unfortunately, this solution will require modifying Windows legacy cross-platform code which I prefer not to modify, for example multiple calls to RegQueryValueEx().
Is it possible to store and read the type information for strings and integers?
For example, Strings will have quotes "" and integers will not:
[General]
AAA="111"
BBB=222
This problem is present on both Qt 4 and Qt 5, on Linux.
Whoa whoa, are you using .ini files or the registry?
With .ini files it's obviously impossible to know what the type was, since it's all a string. You can attempt conversion of the variant to an integer (don't use canConvert!), and assume it's an integer if it converts into one.
With the registry, QSettings will work as you expect it to.
I really don't see what the problem is. Don't use .ini files if you wish to retain type information. You'd face exactly the same problems if you wrote the code by hand in a platform-dependent manner.
You can explicitly write quoted strings into the .ini files, and check for presence of quotes when reading them back. If the quotes are not present, you can try conversion to an integer.
I solved this problem for a component which needs to save and restore variants of arbitrary type, without knowing what its clients expect. The solution was to store the variant's typeName() alongside each value:
void store(QSettings& settings, const QString& key, const QVariant& value)
{
settings.setValue(key+"value", value);
settings.setValue(key+"type", value.typeName());
}
When reading back, we also read the type name, and convert() the variant if it's not already the correct type, before returning it.
QVariant retrieve(const QSettings& settings, const QString& key)
{
auto value = settings.value(key+"value");
const auto typeName = settings.value(key+"type").toString();
const bool wasNull = value.isNull(); // NOTE 1
const auto t = QMetaType::type(typeName.toUtf8()); // NOTE 2
if (value.userType() != t && !value.convert(t) && !wasNull) {
// restore value that was cleared by the failed convert()
value = settings.value(key+"value");
qWarning() << "Failed to convert value" << value << "to" << typeName;
}
return value;
}
Notes
The wasNull variable is in there because of this niggle of convert():
Warning: For historical reasons, converting a null QVariant results in a null value of the desired type (e.g., an empty string for QString) and a result of false.
In this case, we need to ignore the misleading return value, and keep the successfully-converted null variant of the correct type.
It's not clear that UTF-8 is the correct encoding for QMetaType names (perhaps local 8-bit is assumed?); my types are all ASCII, so I just use toLatin1() instead, which might be faster. If it were an issue, I'd use QString::fromLatin1 in the store() method (instead of implicit char* to QString conversion), to ensure a clean round-trip.
If the type name is not found, t will be QMetaType::UnknownType; that's okay, because convert() will then fail, and we'll return the unconverted variant (or a null). It's not great, but it's a corner case that won't happen in normal usage, and my system will recover reasonably quickly.
Turns out the solution was very simple.
When values are written to the INI file, the type is known.
I am appending to the value "\"STRING right before SetValue
When values are read back from the INI file.
I verify that string types have the above postfix.
If they do, I chop the postfix off.
If they don't I assume they are integers instead of strings.
Works like a charm!
Thanks to you all and especially #Kuba Ober for practically handing out the solution.