const string vs. #define - c++

i need to share some strings in my c++ program. should i use #define or const string? thanks
mystring1.h
#define str1 "str1"
#define str2 "str2"
Or
mystring2.h
extern const string str1;
extern const string str2;
mystring.cpp
const string str1 = "str1";
const string str2 = "str2";

Prefer the second option. If you use the first option (preprocessor), you are limiting your flexibility with the object.
Consider the following... You won't be able to compare strings this way:
if (str1 == "some string")
{
// ...
}

If it is C++ instead of C, you should really use some variable instead of a preprocessor macro. The former is clearer than the latter. Furthermore, if you use C++17, you can use inline variables:
inline const std::string str = "foobar";
or
// constexpr is implicitly inline
constexpr char str0[] = "foobar";
constexpr const char* str1 = "foobar";
constexpr std::string_view str2 = "foobar";
This is also clearer than using extern and can be used in header-only APIs as well.

If it's C++, you should use the C++ Standard Library's std::string. It's much more clear than a preprocessor macro, it will have a single location in memory when it's defined, and it has all the extra functionality of std::string instead of only pointer comparisons as is the case with the implicit const char* that are created with a preprocessor macro.

To take OO advantage of c++, I would say use struct/class.
header:
struct Constants {
static const string s1;
static const string s2;
};
cpp:
const string Constants::s1 = "blah1";
const string Constants::s2 = "blah2";
To reference:
cout << Constants::s1 << endl;

If you don't have to use the preprocessor don't!
If these strings are needed in a resource editor or a manifest or something you might have to.

You could also just use a const char* string for constant data and not a string object, since the object will need to be initialised at the start of the program with the constant data anyway. Do this if you're not going to be doing much with strings but just displaying them or printing them out as is.
So:
extern const char * str1;
and
const char * str1 = "str1";

I would suggest use of functions.
extern const std::string& str1();
extern const std::string& str2();
This gives you more flexibility in how you get those strings in the .cpp file.

Also consider the issue of non-POD static construction and destruction order, as described in the Google C++ style guide.
An alternative is to use:
const char str1[] = "str1";
const char str1[] = "str2";

Related

How to declare constexpr C string?

I think i quite understand how to use the keyword constexpr for simple variable types, but i'm confused when it comes to pointers to values.
I would like to declare a constexpr C string literal, which will behave like
#define my_str "hello"
That means the compiler inserts the C string literal into every place where i enter this symbol, and i will be able to get its length at compile-time with sizeof.
Is it constexpr char * const my_str = "hello";
or const char * constexpr my_str = "hello";
or constexpr char my_str [] = "hello";
or something yet different?
Is it constexpr char * const my_str = "hello";
No, because a string literal is not convertible to a pointer to char. (It used to be prior to C++11, but even then the conversion was deprecated).
or const char * constexpr my_str = "hello";
No. constexpr cannot go there.
This would be well formed:
constexpr const char * my_str = "hello";
but it does not satify this:
So that i will be able to get its length at compile-time with sizeof, etc.
or constexpr char my_str [] = "hello";
This is well formed, and you can indeed get the length at compile time with sizeof. Note that this size is the size of the array, not the length of the string i.e. the size includes the null terminator.
In C++17, you can use std::string_view and string_view_literals
using namespace std::string_view_literals;
constexpr std::string_view my_str = "hello, world"sv;
Then,
my_str.size() is compile time constant.

How to append const char* to a const char*

I am trying to append three different const char* variables into one. This is because a function from windows library takes the parameter LPCTSTR.
I have the following code:
const char* path = "C:\\Users\\xxx\\Desktop\\";
const char* archivo = "vectors";
const char* extension = ".txt";
const char* fullPath =+ path;
fullPath =+ archivo;
fullPath =+ extension;
When I run it I get only the last (extension) added to to FullPath.
You need to allocate some space to hold the concatenated strings. Fortunately, C++ has the std::string class to do this for you.
std::string fullPath = path;
fullPath += archivo;
fullPath += extension;
const char *foo = fullPath.c_str();
Be aware that the space containing the concatenated strings is owned by fullPath and the pointer foo will only remain valid so long as fullPath is in scope and unmodified after the call to c_str.
If you want to construct at compile-time a bunch of string literals, some of which are concatenations of other string literals, the most basic idiomatic low-maintenance technique is based on the good-old preprocessor
#define PATH "C:\\Users\\xxx\\Desktop\\"
#define NAME "vectors"
#define EXT ".txt"
const char *path = PATH;
const char *archivo = NAME;
const char *extension = EXT;
const char *fullPath = PATH NAME EXT;
However, the same thing can be achieved in more moden way by using some constexpr and template meta-programming magic (see C++ concat two `const char` string literals).
Otherwise, you will have to resort to run-time concatenation (like std::string and such). But again, if the input is known at compile time, then a run-time solution is a "loser's way out" :)
When you use a const char* you can't change the chars to which are pointing.
So append const char* to a const char* is not possible!

Why does "auto" declare strings as const char* instead of std::string?

I made a template which adds the data it is given. If I use it like this, the compiler declares in_1 and in_2 as const char *, and the code doesn't compile.
#include <iostream>
using namespace std;
template <class T>
T addstuff(T part_1, T part_2){
return(part_1+part_2);
}
int main(int argc, char const *argv[])
{
auto in_1="Shut ";
auto in_2="up.";
cout<<addstuff(in_1, in_2)<<endl;
return 0;
}
If I declare in_1 and in_2 std::string, it works like a charm.
Why can't (or doesn't) the compiler declare those strings automatically std::string?
The reason you can't "write" to your auto variable is that it's a const char * or const char [1], because that is the type of any string constant.
The point of auto is to resolve to the simplest possible type which "works" for the type of the assignment. The compiler does not "look forward to see what you are doing with the variable", so it doesn't understand that later on you will want to write into this variable, and use it to store a string, so std::string would make more sense.
You code could be made to work in many different ways, here's one that makes some sense:
std::string default_name = "";
auto name = default_name;
cin >> name;
If you use string literals, auto will work as expected.
In C++14, C++17 or C++20, you can place an s after the quotes, and it will create a std::string instead of a const char* string.
This can be used together with auto to create a std::string:
auto hello = "hello"s;
String literals are not enabled by default. One way of enabling string literals is to place the following at the top of the source file:
#include <string>
using namespace std::string_literals;
As an example, this loop works for std::string (with s added to the string literal), but not for const char* type string literals:
for (auto &x : hello) {
std::cout << "letter: " << x << std::endl;
}
Here is the cppreference page for the ""s operator.
Because string literals have type const char[N+1], not std::string.
This is just a fact of the language.
They could have made it so that auto has a special case for string literals, but that would be inconsistent, surprising and of very little benefit.
auto will declare the variable as the compile-time type of the expression you initialize it to.
String literals are of type const char*, not std::string.

C++ deprecated conversion from string constant to 'char*'

I have a class with a private char str[256];
and for it I have an explicit constructor:
explicit myClass(char *func)
{
strcpy(str,func);
}
I call it as:
myClass obj("example");
When I compile this I get the following warning:
deprecated conversion from string constant to 'char*'
Why is this happening?
This is an error message you see whenever you have a situation like the following:
char* pointer_to_nonconst = "string literal";
Why? Well, C and C++ differ in the type of the string literal. In C the type is array of char and in C++ it is constant array of char. In any case, you are not allowed to change the characters of the string literal, so the const in C++ is not really a restriction but more of a type safety thing. A conversion from const char* to char* is generally not possible without an explicit cast for safety reasons. But for backwards compatibility with C the language C++ still allows assigning a string literal to a char* and gives you a warning about this conversion being deprecated.
So, somewhere you are missing one or more consts in your program for const correctness. But the code you showed to us is not the problem as it does not do this kind of deprecated conversion. The warning must have come from some other place.
The warning:
deprecated conversion from string constant to 'char*'
is given because you are doing somewhere (not in the code you posted) something like:
void foo(char* str);
foo("hello");
The problem is that you are trying to convert a string literal (with type const char[]) to char*.
You can convert a const char[] to const char* because the array decays to the pointer, but what you are doing is making a mutable a constant.
This conversion is probably allowed for C compatibility and just gives you the warning mentioned.
As answer no. 2 by fnieto - Fernando Nieto clearly and correctly describes that this warning is given because somewhere in your code you are doing (not in the code you posted) something like:
void foo(char* str);
foo("hello");
However, if you want to keep your code warning-free as well then just make respective change in your code:
void foo(char* str);
foo((char *)"hello");
That is, simply cast the string constant to (char *).
There are 3 solutions:
Solution 1:
const char *x = "foo bar";
Solution 2:
char *x = (char *)"foo bar";
Solution 3:
char* x = (char*) malloc(strlen("foo bar")+1); // +1 for the terminator
strcpy(x,"foo bar");
Arrays also can be used instead of pointers because an array is already a constant pointer.
Update: See the comments for security concerns regarding solution 3.
A reason for this problem (which is even harder to detect than the issue with char* str = "some string" - which others have explained) is when you are using constexpr.
constexpr char* str = "some string";
It seems that it would behave similar to const char* str, and so would not cause a warning, as it occurs before char*, but it instead behaves as char* const str.
Details
Constant pointer, and pointer to a constant. The difference between const char* str, and char* const str can be explained as follows.
const char* str : Declare str to be a pointer to a const char. This means that the data to which this pointer is pointing to it constant. The pointer can be modified, but any attempt to modify the data would throw a compilation error.
str++ ; : VALID. We are modifying the pointer, and not the data being pointed to.
*str = 'a'; : INVALID. We are trying to modify the data being pointed to.
char* const str : Declare str to be a const pointer to char. This means that point is now constant, but the data being pointed too is not. The pointer cannot be modified but we can modify the data using the pointer.
str++ ; : INVALID. We are trying to modify the pointer variable, which is a constant.
*str = 'a'; : VALID. We are trying to modify the data being pointed to. In our case this will not cause a compilation error, but will cause a runtime error, as the string will most probably will go into a read only section of the compiled binary. This statement would make sense if we had dynamically allocated memory, eg. char* const str = new char[5];.
const char* const str : Declare str to be a const pointer to a const char. In this case we can neither modify the pointer, nor the data being pointed to.
str++ ; : INVALID. We are trying to modify the pointer variable, which is a constant.
*str = 'a'; : INVALID. We are trying to modify the data pointed by this pointer, which is also constant.
In my case the issue was that I was expecting constexpr char* str to behave as const char* str, and not char* const str, since visually it seems closer to the former.
Also, the warning generated for constexpr char* str = "some string" is slightly different from char* str = "some string".
Compiler warning for constexpr char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *const'
Compiler warning for char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.
Tip
You can use C gibberish ↔ English converter to convert C declarations to easily understandable English statements, and vice versa. This is a C only tool, and thus wont support things (like constexpr) which are exclusive to C++.
In fact a string constant literal is neither a const char * nor a char* but a char[]. Its quite strange but written down in the c++ specifications; If you modify it the behavior is undefined because the compiler may store it in the code segment.
Maybe you can try this:
void foo(const char* str)
{
// Do something
}
foo("Hello")
It works for me
I solve this problem by adding this macro in the beginning of the code, somewhere. Or add it in <iostream>, hehe.
#define C_TEXT( text ) ((char*)std::string( text ).c_str())
I also got the same problem. And what I simple did is just adding const char* instead of char*. And the problem solved. As others have mentioned above it is a compatible error. C treats strings as char arrays while C++ treat them as const char arrays.
For what its worth, I find this simple wrapper class to be helpful for converting C++ strings to char *:
class StringWrapper {
std::vector<char> vec;
public:
StringWrapper(const std::string &str) : vec(str.begin(), str.end()) {
}
char *getChars() {
return &vec[0];
}
};
The following illustrates the solution, assign your string to a variable pointer to a constant array of char (a string is a constant pointer to a constant array of char - plus length info):
#include <iostream>
void Swap(const char * & left, const char * & right) {
const char *const temp = left;
left = right;
right = temp;
}
int main() {
const char * x = "Hello"; // These works because you are making a variable
const char * y = "World"; // pointer to a constant string
std::cout << "x = " << x << ", y = " << y << '\n';
Swap(x, y);
std::cout << "x = " << x << ", y = " << y << '\n';
}

c++ constant in library; does not work

anyone knows why this does not work when I try to include a library with the following declarations:
namespace wincabase
{
const char* SOMESTRING = "xx";
}
While this is perfectly fine:
namespace wincabase
{
const int X = 30;
}
I get a "multiple definitions" error with gcc for the first case when I link the lib. Thanks!
const char* means pointer to const char. This means the pointer itself is not constant.
Hence it's a normal variable, so you'd need to use
extern const char* SOMESTRING;
in the header file, and
const char* SOMESTRING = "xx";
in one compilation unit of the library.
Alternatively, if it's meant to be a const pointer to a const char, then you should use:
const char* const SOMESTRING = "xx";
You're declaring the pointer as const, and then pointing it to a string literal defined in the compilation unit, so you'd be duplicating the string literal if you used this in a header file. What you need to do is declare pointer in the header file, and define the string in a source file in the library.
Header:
extern const char* SOMESTRING;
In some source file in the library:
const char* SOMESTRING = "xx";
Besides the approach Tobi pointed out:
const char* const SOMESTRING = "xx";
another alternative is to declare it as a const character array:
const char SOMESTRING[] = "xx";
This approach potentially provides the compiler with additional optimization opportunities, such as placing the string in the read-only section of the resulting binary; although it's conceivable the compiler may be able to perform similar optimizations with the first approach.
You need to declare and define them seprately:
Plop.h
======
namespace wincabase
{
extern const char* SOMESTRING; // declare
}
Plop.cpp
========
const char* wincabase::SOMESTRING = "xx"; // define