Conveniently Declaring Compile-Time Strings in C++

Conveniently Declaring Compile-Time Strings in C++

I haven't seen anything to match the elegance of Scott Schurr's str_const presented at C++ Now 2012. It does require constexpr though.

Here's how you can use it, and what it can do:

int
main()
{
constexpr str_const my_string = "Hello, world!";
static_assert(my_string.size() == 13, "");
static_assert(my_string[4] == 'o', "");
constexpr str_const my_other_string = my_string;
static_assert(my_string == my_other_string, "");
constexpr str_const world(my_string, 7, 5);
static_assert(world == "world", "");
// constexpr char x = world[5]; // Does not compile because index is out of range!
}

It doesn't get much cooler than compile-time range checking!

Both the use, and the implementation, is free of macros. And there is no artificial limit on string size. I'd post the implementation here, but I'm respecting Scott's implicit copyright. The implementation is on a single slide of his presentation linked to above.

Update C++17

In the years since I posted this answer, std::string_view has become part of our tool chest. Here is how I would rewrite the above using string_view:

#include <string_view>

int
main()
{
constexpr std::string_view my_string = "Hello, world!";
static_assert(my_string.size() == 13);
static_assert(my_string[4] == 'o');
constexpr std::string_view my_other_string = my_string;
static_assert(my_string == my_other_string);
constexpr std::string_view world(my_string.substr(7, 5));
static_assert(world == "world");
// constexpr char x = world.at(5); // Does not compile because index is out of range!
}

C++ string concatenation in compile time

This would work without allocating, and also compile time

#include <iostream>

#define STR_(X) #X
#define STR(X) STR_(X)

int main()
{
//constexpr std::string_view(const char*) doesn't work in some versions of gcc, but is a better alternative if the compiler supports it
constexpr const char* str = __FILE__ ":" STR(__LINE__);
std::cout << str << std::endl;
}

Turn C string literal into std::integer_sequence at compile-time with C++17

Something like this might do it:

template <size_t N, typename F, size_t... indexes>
constexpr auto make_seq_helper(F f, std::index_sequence<indexes...> is) {
return std::integer_sequence<char, (f()[indexes])...>{};
}

template <typename F>
constexpr auto make_seq(F f) {
constexpr size_t N = f().size();
using indexes = std::make_index_sequence<N>;
return make_seq_helper<N>(f, indexes{});
};


template<const char* str>
struct IntegerSequenceFromString {

private:
constexpr static auto value = make_seq([](){return std::string_view{str}; });

public:
using type = decltype(value);
};

Usage would then be:

 constexpr static const char str[] = "lala";
IntegerSequenceFromString<str>::type i{};

Here is a live example of it working.

I am sure, there is a way to reduce some of this extra stuff as well, but from the assembly, it looks like we don't generate any real runtime variables: https://godbolt.org/z/f65cjGfzn

Generating compile time functions string for formatting strings with libfmt

The type of the format string and the return type of the function cannot be string_view since the format string is constructed dynamically, using string_view will result in a dangling pointer.

In addition, fmt::format requires that the format string must be a constant expression. Instead, you need to use fmt::vformat. This should work

static std::string
headerCenter(const std::string& text, const int width, const char fill) {
// build fmt string
std::string format = fmt::format("|{{0:{}^{}}}|", fill, width);
return fmt::vformat(format, fmt::make_format_args(text));
}

Demo



Related Topics



Leave a reply



Submit