constexpr const char* in header file - c++

Is there a reason to not use "constexpr const char*" in header file?
The argument from a colleague is that every translation unit including this header file would have a copy.
My understanding was that since its compile-time constant, no memory is allocated and acts more like a "#define" macro with respect to memory usage.
Here is the source,
TestConstExpr.h
#include <string.h>
namespace TestConstExpr
{
constexpr const char* TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
constexpr const int TIME_FORMAT_SIZE = strlen(TIME_FORMAT) + 1;
class TestClass
{
char arr[TIME_FORMAT_SIZE];
}
}

The argument from a colleague is that every translation unit including this header file would have a copy.
You're colleague is technically correct. But it doesn't matter, since the redundant copies are thrown away when the units are linked together.
Although, I've seen comments about this not being necessarily the case on some non-standard-conforming systems when dynamic linking is involved.
constexpr const char* TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
.... sizeof(TIME_FORMAT)
This doesn't do what you probably think it does. It gives you the size of the pointer, not the size of the pointed string.
constexpr const int TIME_FORMAT_SIZE = strlen(TIME_FORMAT) + 1;
Your attempted fix also doesn't work, because strlen is not a constant expression.
You can fix the problems by using a reference to the string literal:
static constexpr auto& TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
constexpr const int TIME_FORMAT_SIZE = sizeof(TIME_FORMAT);

Related

Use the result of strlen as length of char*

My intention is to create a char[] from the length of a char* I've already created.
I asked myself why this is not valid:
void copyStringsV2() {
const char* source = "this is my string.";
const int length = strlen(source);
const char* dest[length];
}
The compiler gives me this hint:
Severity Code Description Project File Line Suppression State
Warning C4101 'str_b': unreferenced local variable CStrings
xxx\cstrings.cpp 46
Error C2131 expression did not evaluate to a constant CStrings
xxx\cstrings.cpp 161
Error (active) E0028 expression must have a constant value CStrings
xxx\CStrings.cpp 161
May you can help me out here?
You are trying to declare a variable-length array, which is not a standard feature in C++. A fixed-length array must have its length known at compile-time. That is what the compiler error is complaining about.
If you don't know the length until runtime, you will have to allocate the copied string dynamically instead, such as via new[]:
void copyStringsV2()
{
const char* source = "this is my string.";
const int length = strlen(source);
char* dest = new char[length+1];
strcpy(dest, source);
...
delete[] dest;
}
Or std::vector, so you don't need to use new[]/delete[] directly:
#include <vector>
void copyStringsV2()
{
const char* source = "this is my string.";
const int length = strlen(source);
std::vector<char> dest(length+1);
strcpy(dest.data(), source);
...
}
But, in this case, it would be better to use std::string instead:
#include <string>
void copyStringsV2()
{
const char* source = "this is my string.";
std::string dest = source;
...
}
For starters you should use the type size_t instead of the type int in this declaration
const int length = strlen(source);
^^^
This constant is a constant of the run-time. So you may not use it in the declaration
const char* dest[length];
because here is declared a variable length array and variable length arrays (VLA) is not a standard C++ feature.
Also it is unclear why the type of elements of the array is const char * instead const char.
And moreover a constant object shall be initialized.
My intention is to create a char[]
const char* dest[length];
That's not an array of char. That's an array of pointers to const char.
Also, if you want the array to be able to fit the original null termiated string, you must include the null terminator in the length of the array so that its size is length + 1.
I asked myself why this is not valid:
The compiler gives me this hint:
expression did not evaluate to a constant
May you can help me out here?
The size of an array must be a compile time constant. length is not a constant, therefore it cannot be used as length of an array.
Length of a string cannot be calculated at compile time through a pointer to element of the string. However, if you used a reference for example, and if you used a constexpr function to get the length, and used a constexpr (const works too) variable, then you could use it as the size of an array. There is such function in the C++ standard library:
auto& source = "this is my string.";
constexpr auto length = std::char_traits<char>::length(source);
char dest[length + 1];
why this is not compilable
Because this is C++, and C++ provide a wide variety of tools already1. Your best bet will be std::string. Strings can be copied and passed around with no additional code to write.
#include <string>
int main()
{
const std::string source = "this is my string.";
const std::string dest = source;
// do something with dest
}
1) So you don't need variable length arrays, which are not part of C++.

Detecting string literals at compile time [duplicate]

I have a class to wrap string literals and calculate the size at compile time.
The constructor looks like this:
template< std::size_t N >
Literal( const char (&literal)[N] );
// used like this
Literal greet( "Hello World!" );
printf( "%s, length: %d", greet.c_str(), greet.size() );
There is problem with the code however. The following code compiles and I would like to make it an error.
char broke[] = { 'a', 'b', 'c' };
Literal l( broke );
Is there a way to restrict the constructor so that it only accepts c string literals? Compile time detection is preferred, but runtime is acceptable if there is no better way.
There is a way to force a string literal argument: make a user defined literal operator. You can make the operator constexpr to get the size at compile time:
constexpr Literal operator "" _suffix(char const* str, size_t len) {
return Literal(chars, len);
}
I don't know of any compiler that implements this feature at this time.
Yes. You can generate compile time error with following preprocessor:
#define IS_STRING_LITERAL(X) "" X ""
If you try to pass anything other than a string literal, the compilation will fail. Usage:
Literal greet(IS_STRING_LITERAL("Hello World!")); // ok
Literal greet(IS_STRING_LITERAL(broke)); // error
With a C++11 compiler with full support for constexpr we can use a constexpr constructor using a constexpr function, which compiles to a non-const expression body in case the trailing zero character precondition is not fulfilled, causing the compilation to fail with an error. The following code expands the code of UncleBens and is inspired by an article of Andrzej's C++ blog:
#include <cstdlib>
class Literal
{
public:
template <std::size_t N> constexpr
Literal(const char (&str)[N])
: mStr(str),
mLength(checkForTrailingZeroAndGetLength(str[N - 1], N))
{
}
template <std::size_t N> Literal(char (&str)[N]) = delete;
private:
const char* mStr;
std::size_t mLength;
struct Not_a_CString_Exception{};
constexpr static
std::size_t checkForTrailingZeroAndGetLength(char ch, std::size_t sz)
{
return (ch) ? throw Not_a_CString_Exception() : (sz - 1);
}
};
constexpr char broke[] = { 'a', 'b', 'c' };
//constexpr Literal lit = (broke); // causes compile time error
constexpr Literal bla = "bla"; // constructed at compile time
I tested this code with gcc 4.8.2. Compilation with MS Visual C++ 2013 CTP failed, as it still does not fully support constexpr (constexpr member functions still not supported).
Probably I should mention, that my first (and preferred) approach was to simply insert
static_assert(str[N - 1] == '\0', "Not a C string.")
in the constructor body. It failed with a compilation error and it seems, that constexpr constructors must have an empty body. I don't know, if this is a C++11 restriction and if it might be relaxed by future standards.
No there is no way to do this. String literals have a particular type and all method overload resolution is done on that type, not that it's a string literal. Any method which accepts a string literal will end up accepting any value which has the same type.
If your function absolutely depends on an item being a string literal to function then you probably need to revisit the function. It's depending on data it can't guarantee.
A string literal does not have a separate type to distinguish it from a const char array.
This, however, will make it slightly harder to accidentally pass (non-const) char arrays.
#include <cstdlib>
struct Literal
{
template< std::size_t N >
Literal( const char (&literal)[N] ){}
template< std::size_t N >
Literal( char (&literal)[N] ) = delete;
};
int main()
{
Literal greet( "Hello World!" );
char a[] = "Hello world";
Literal broke(a); //fails
}
As to runtime checking, the only problem with a non-literal is that it may not be null-terminated? As you know the size of the array, you can loop over it (preferable backwards) to see if there's a \0 in it.
I once came up with a C++98 version that uses an approach similar to the one proposed by #k.st. I'll add this for the sake of completeness to address some of the critique wrt the C++98 macro.
This version tries to enforce good behavior by preventing direct construction via a private ctor and moving the only accessible factory function into a detail namespace which in turn is used by the "offical" creation macro. Not exactly pretty, but a bit more fool proof. This way, users have to at least explicitly use functionality that is obviously marked as internal if they want to misbehave. As always, there is no way to protect against intentional malignity.
class StringLiteral
{
private:
// Direct usage is forbidden. Use STRING_LITERAL() macro instead.
friend StringLiteral detail::CreateStringLiteral(const char* str);
explicit StringLiteral(const char* str) : m_string(str)
{}
public:
operator const char*() const { return m_string; }
private:
const char* m_string;
};
namespace detail {
StringLiteral CreateStringLiteral(const char* str)
{
return StringLiteral(str);
}
} // namespace detail
#define STRING_LITERAL_INTERNAL(a, b) detail::CreateStringLiteral(a##b)
/**
* \brief The only way to create a \ref StringLiteral "StringLiteral" object.
* This will not compile if used with anything that is not a string literal.
*/
#define STRING_LITERAL(str) STRING_LITERAL_INTERNAL(str, "")

Global Externs. Difference between string and char*

I have two questions:
I used to have a Constants.h containing
const std::string PATH("/ram/")
and it worked fine.
But when I added
const char* BLAH = "blah";
to the same file. I got a redefinition error during linking.
I get that it's because each compilation unit creates its own constants so this causes problem during linking, but why did it work for regular std::strings before and not for char*s?
So after that first issue, I decided to make everything extern and I now have a Constants.h and a Constants.cpp. But then I ran into another problem. I have another file Test.cpp, where a constant is defined as
const std::string FOO(PATH + "booyah");
This used to create "/ram/booyah" but now it's just "booyah". PATH has somehow become empty. I'm guessing it has something to do with the extern, and with constants being created during compile time. The constant PATH works okay during runtime.
Is this correct understanding? Is there a way to make all these work together nicely?
Thanks in advance!
When something is a const it is automatically made static as in internal linkage.
Therefore when you had const std::string it was actually static const std::string.
This means every compilation unit gets its own copy of the string, which is wasteful of memory, although usually not a big concern.
To fix that, make extern const std::string s; in the header file and extern const std::string s = ".."; in the source file.
To avoid requiring a source file, you can try this alternative, although it is potentially longer to compile. It will also allocate the string only once.
inline const std::string& GetStr()
{
static const std::string s = "..";
return s;
}
Now, why didn't const char* BLAH compile in the header? This is because BLAH is NOT a constant. Although it is a read-only pointer, the pointer is not constant, and can be changed to point to something else.
To make the code compile like it did with const std::string, you must use const char *const BLAH = "..";. Now it is a real constant and will compile as a static. This also suffers from the same problem that every compilation unit receives a copy of the pointer. It may also receive a copy of the string literal, depending on compiler optimizations. You can fix this by using the extern method above.
The order in which constants are initialized in the same compilation unit is the order in which they appear. However, the order in which constants are initialized between compilation units is NOT defined. This means an extern from one source file shouldn't depend on an extern from another source file.
You can fix this by defining all your constants in the same source file in the correct order, or using functions (possibly inline) to return the strings.
A global variable that is const has internal linkage. Your BLAH variable is not const, and so it defaults to external linkage. You could either declare it static, or simply make it const:
const char * const BLAH = "blah";
If you find it difficult to spell type names in C++, you can use type aliases to make your life easier:
using ccp = const char *;
const std::string foo = "foo";
const ccp bar = "bar";
Since the header file can be included ffrom multiple CPP files, you would get conflicts. You can use the static keyword.
I typically do:
static const char* const str= "blah";

const string vs. #define

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

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