Mixing DEFINEs with Literal Strings in C++ - c++

I've created a #define which points to a particular directory. I would then like to use this definition in combination with a string literal:
#define PATH_RESOURCES "/path/to/resources/"
std::ifstream datafile(PATH_RESOURCES + "textures.dat");
However, the compiler complains about adding char types using the + operator:
error: invalid operands of types ‘const char [11]’ and ‘const char [13]’ to binary ‘operator+’
So how can I combine a #define with a string literal? Or, is there a better way of doing this altogether? I imagine using a const variable would be an alternative, but this would mean having to pass around yet another parameter which I'd rather prefer to keep as a global definition.

You can combine two string literals by writing them one after the other with no + plus between them:
std::ifstream datafile(PATH_RESOURCES "textures.dat");
The fact that one of the string literals happens to be defined through the preprocessor does not change much: you can do it like this as well:
std::ifstream datafile(PATH_"/path/to/resources/" "textures.dat");
Here is a demo on ideone.

Try
std::ifstream datafile(PATH_RESOURCES "textures.dat");
Two string literals adjacent concatenate.

Use std::ifstream datafile(PATH_RESOURCES "textures.data");
Note the lack of the + operator.
You could also do
std::ifstream datafile(std::string(PATH_RESOURCES) + std::string("textures.data")); if you really wanted.

Create a std::string, assign your #define string to it and add the second literal. Afterwards use the string.
std::string str(PATH_RESOURCES);
str = str + "textures.dat";
std::ifstream datafile(str);

Related

how adding string and numbers in cpp works using + operator?

I've used cpp for quite a while, I was known that we cannot add string and numbers(as + operator is not overloaded for that). But , I saw a code like this.
#include <iostream>
using namespace std;
int main() {
string a = "";
a += 97;
cout << a;
}
this outputs 'a' and I also tried this.
string a ="";
a=a+97;
The second code gives a compilation error(as invalid args to + operator, std::string and int).
I don't want to concatenate the string and number.
What is the difference? Why does one work but not the other?
I was expecting that a+=97 is the same as a=a+97 but it appears to be different.
The first snippet works because std::string overrides operator+= to append a character to a string. 97 is the ASCII code for 'a', so the result is "a".
The second snippet does not work because there is no + operator defined that accepts a std::string and an int, and no conversion constructor to make a std::string out of an int or char. There two overloads of the + operator that take a char, but the compiler cannot tell which one to use. The match is ambiguous, so an error is reported.

Macro string concatenation

I use macros to concatenate strings, such as:
#define STR1 "first"
#define STR2 "second"
#define STRCAT(A, B) A B
which having STRCAT(STR1 , STR2 ) produces "firstsecond".
Somewhere else I have strings associated to enums in this way:
enum class MyEnum
{
Value1,
Value2
}
const char* MyEnumString[] =
{
"Value1String",
"Value2String"
}
Now the following does not work:
STRCAT(STR1, MyEnumString[(int)MyEnum::Value1])
I was just wondering whether it possible to build a macro that concatenate a #defined string literal with a const char*? Otherwise, I guess I'll do without macro, e.g. in this way (but maybe you have a better way):
std::string s = std::string(STR1) + MyEnumString[(int)MyEnum::Value1];
The macro works only on string literals, i.e. sequence of characters enclosed in double quotes. The reason the macro works is that C++ standard treats adjacent string literals like a single string literal. In other words, there is no difference to the compiler if you write
"Quick" "Brown" "Fox"
or
"QuickBrownFox"
The concatenation is performed at compile time, before your program starts running.
Concatenation of const char* variables needs to happen at runtime, because character pointers (or any other pointers, for that matter) do not exist until the runtime. That is why you cannot do it with your CONCAT macro. You can use std::string for concatenation, though - it is one of the easiest solutions to this problem.
It's only working for char literals that they can be concatenated in this way:
"A" "B"
This will not work for a pointer expression which you have in your sample, which expands to a statement like
"STR1" MyEnumString[(int)MyEnum::Value1];
As for your edit:
Yes I would definitely go for your proposal
std::string s = std::string(STR1) + MyEnumString[(int)MyEnum::Value1];
Your macro is pretty unnecessary, as it can only work with string literals of the same type. Functionally it does nothing at all.
std::string s = STRCAT("a", "b");
Is exactly the same as:
std::string s = "a" "b";
So I feel that it's best to just not use the macro at all. If you want a runtime string concatenating function, a more C++-canonical version is:
inline std::string string_concat(const std::string& a, const std::string& b)
{
return a + b;
}
But again, it seems almost pointless to have this function when you can just do:
std::string a = "a string";
std::string ab = a + "b string";
I can see limited use for a function like string_concat. Maybe you want to work on arbitrary string types or automatic conversion between UTF-8 and UTF-16...

how can i add this variable to my path for ifstream?

I'm trying to append my path and contain a variable as part of the path but I'm getting an error.
What's wrong with it?
fstream fin("E:\\Games\\maps\\" + this->MapNumber + ".map", ios::in|ios::binary|ios::ate);
this->MapNumber is a USHORT
error: 13 IntelliSense: expression must have integral or unscoped enum type
In C++ you can't use + to concatenate literal strings. You can use + with std::strings to concatenate them, but that won't work with integer or other types. You need to use a stream instead. Insertion and extraction into a stream will cause the types that support it to represent themselves as text, but you probably already knew this from general I/O.
Try with something like this:
std::stringstream filename;
filename << "E:\\Games\\maps\\" << this->MapNumber << ".map";
std::fstream fin(filename.str().c_str(), ios::in|ios::binary|ios::ate);
Just like with everything else, to use something you need to include the header that declares it first. In order to use std::stringstream you need to include <sstream>.
You can't use operator+ on a string and another type like string or so you can either:
Option1: turn all variables into strings to append them
string s = string("E:\\Games\\maps\\") + string(itoa(this->MapNumber)) + string(".map");
option2: use stringstream as #k-ballo explained
option3: use the good old C fprintf (my personal favourite)
char str[100];
fprintf(str, "E:\\Games\\maps\\ %d .map", this->MapNumber);

Concatenate write parameters in C++

I need to concatenate the parameters in the write function. How would I do this?
Example: ofstream write(newFolder concat "/newfile.txt");
mkdir(newFolder);
ofstream write (wantTo "concat here");
write << "Sup mo fo";
write.close();
If newFolder is a std::string, you can simply use newFolder + "/newfile.txt". You'd have to make sure newFolder does not end with a / or a \ though. You may need to use the c_str() function on your std::string if you require a char* in your write function.
While using std::string is the preferred way, be careful because older versions of the standard library doesn't support an open method that takes a std::string as argument.
If you get an error in the call to open when using std::string, then you have to do it in two steps:
std::string newFolder = "/path/to/folder";
mkdir(newFolder.c_str(), 0644); // The 'mkdir' function wants 'const char *'
// Has to concatenate strings here if your standard library is too old
std::string folderAndFile = newFilder + "/filename";
std::ofstream output(folderAndfile.c_str());

How convert type from const char * to char *

I'm trying create simple application in C++. This application has to read from file and displays data. I've written function:
std::vector <AndroidApplication> AndroidApplication::getAllApp(){
std::vector<AndroidApplication> allApp;
std::fstream f;
f.open("freeApps.txt");
std::string line;
if(f.is_open()){
while(getline(f, line)) {
std::string myLine = "";
char * line2 = line.c_str();
myLine = strtok(line2,"\t");
AndroidApplication * tmpApp = new AndroidApplication(myLine[1], myLine[2], myLine[4]);
tmpApp->Developer = myLine[0];
tmpApp->Pop = myLine[3];
tmpApp->Type = myLine[5];
allApp->pushBack(tmpApp);
}
}
return allApp;
}
It throws me an error in line:
myLine = strtok(line2,"\t");
An error:
cannot convert from 'const char *' to 'char *'
Could you tell me how can I deal with it?
Don't use strtok. std::string has its own functions for string-scanning, e.g., find.
To use strtok, you'll need a writeable copy of the string. c_str() returns a read-only pointer.
You can't just "convert it" and forget about it. The pointer you get from .c_str() is to a read-only buffer. You need to copy it into a new buffer to work with: ideally, by avoiding using antiquated functions like strtok in the first place.
(I'm not quite sure what you're doing with that tokenisation, actually; you're just indexing into characters in the once-tokenised string, not indexing tokens.)
You're also confusing dynamic and automatic storage.
std::vector<AndroidApplication> AndroidApplication::getAllApp()
{
std::vector<AndroidApplication> allApp;
// Your use of fstreams can be simplified
std::fstream f("freeApps.txt");
if (!f.is_open())
return allApp;
std::string line;
while (getline(f, line)) {
// This is how you tokenise a string in C++
std::istringstream split(line);
std::vector<std::string> tokens;
for (std::string each;
std::getline(split, each, '\t');
tokens.push_back(each));
// No need for dynamic allocation here,
// and I'm assuming you wanted tokens ("words"), not characters.
AndroidApplication tmpApp(tokens[1], tokens[2], tokens[4]);
tmpApp.Developer = tokens[0];
tmpApp.Pop = tokens[3];
tmpApp.Type = tokens[5];
// The vector contains objects, not pointers
allApp.push_back(tmpApp);
}
return allApp;
}
I suspect the error is actually on the previous line,
char * line2 = line.c_str();
This is because c_str() gives a read-only pointer to the string contents. There is no standard way to get a modifiable C-style string from a C++ string.
The easiest option to read space-separated words from a string (assuming that's what you're tying to do) is to use a string stream:
std::vector<std::string> words;
std::istringstream stream(line);
std::copy(std::istream_iterator<std::string>(stream),
std::istream_iterator<std::string>(),
back_inserter(words));
If you really want to use strtok, then you'll need a writable copy of the string, with a C-style terminator; one way to do this is to copy it into a vector:
std::vector<char> writable(line.c_str(), line.c_str() + line.length() + 1);
std::vector<char *> words;
while (char * word = strtok(words.empty() ? &writable[0] : NULL, " ")) {
words.push_back(word);
}
Bear in mind that strtok is quite difficult to use correctly; you need to call it once for each token, not once to create an array of tokens, and make sure nothing else (such as another thread) calls it until you've finished with the string. I'm not sure that my code is entirely correct; I haven't tried to use this particular form of evil in a long time.
Since you asked for it:
Theoretically you could use const_cast<char*>(line.c_str()) to get a char*. However giving the result of this to strtok (which modifies its parameter) is IIRC not valid c++ (you may cast away constness, but you may not modify a const object). So it might work for your specific platform/compiler or not (and even if it works it might break anytime).
The other way is to create a copy, which is filled with the contents of the string (and modifyable):
std::vector<char> tmp_str(line.begin(), line.end());
myLine = strtok(&tmp_str[0],"\t");
Of course as the other answers tell you in great detail, you really should avoid using functions like strtok in c++ in favour of functionality working directly on std::string (at least unless you have a firm grasp on c++, high performance requirements and know that using the c-api function is faster in your specific case (through profiling)).