Transforming a string_view in-place - c++

std::transform, as of C++20, is declared constexpr. I have a bunch of string utility functions that take std::string arguments, but a lot of the usage ends up just passing in small, short, character literal sequences at compile-time. I thought I would leverage this fact and declare versions that are constexpr and take std::string_views instead of creating temporary std::string variables just to throw them away...
ORIGINAL STD::STRING VERSION:
[[nodiscard]] std::string ToUpperCase(std::string string) noexcept {
std::transform(string.begin(), string.end(), string.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c, std::locale("")); });
return string;
}
NEW STD::STRING_VIEW VERSION:
[[nodiscard]] constexpr std::string_view ToUpperCase(std::string_view stringview) noexcept {
std::transform(stringview.begin(), stringview.end(), stringview.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c, std::locale("")); });
return stringview;
}
But MSVC complains:
error C3892: '_UDest': you cannot assign to a variable that is const
Is there a way to call std::transform with a std::string_view and put it back into the std::string_view or am I going to have to create a local string and return that, thereby defeating the purpose of using std::string_view in the first place?
[[nodiscard]] constexpr std::string ToUpperCase(std::string_view stringview) noexcept {
std::string copy{stringview};
std::transform(stringview.begin(), stringview.end(), copy.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c, std::locale("")); });
return copy;
}

You can't in-place transform a std::string_view - what if it has been constructed from char const*?
a lot of the usage ends up just passing in small, short, character literal sequences at compile-time.
...but you can lift string literals to the type level
namespace impl {
template<std::size_t n> struct Str {
std::array<char, n> raw{};
constexpr Str(char const (&src)[n + 1]) { std::copy_n(src, n, raw.begin()); }
};
template<std::size_t n> Str(char const (&)[n]) -> Str<n - 1>;
}
template<char... cs> struct Str { static char constexpr value[]{cs..., '\0'}; };
template<impl::Str s>
auto constexpr str_v = []<std::size_t... is>(std::index_sequence<is...>) {
return Str<s.raw[is]...>{};
}(std::make_index_sequence<s.raw.size()>{});
...and add a special case. In general, this hack can be avoided with range/tuple polymorphic algorithms.
[[nodiscard]] constexpr auto ToUpperCase(auto str) {
for (auto&& c: str) c = ConstexprToUpper(c); // std::toupper doesn't seem constexpr
return str;
}
template<char... cs> [[nodiscard]] constexpr auto ToUpperCase(Str<cs...>) {
return Str<ConstexprToUpper(cs)...>{};
}
So, to use that transformation optimized for character literal sequences, now write ToUpperCase(str_v<"abc">) instead of ToUpperCase("abc"sv). If you always want string_view as output, return std::string_view{Str<ConstexprToUpper(cs)...>::value} in that overload.

As said in one comment, span is a better vocabulary type for this because individual elements can be modified.
Also I wouldn't make it nodiscard, because it can be useful even without assigning the result:
#include<algorithm>
#include<cassert>
#include<cctype>
#include<locale>
#include<string_view>
#include<span>
constexpr std::span<char> ToUpperCase(std::span<char> stringview) noexcept {
std::transform(stringview.begin(), stringview.end(), stringview.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); });
return stringview;
}
int main() {
std::string a = "compiler";
std::string b = ToUpperCase(a);
assert( a == "COMPILER");
assert( b == "COMPILER");
}
https://godbolt.org/z/Toz8Y9bj9
Somewhat departing from your original aim...
I think this is more elegant, although subject to bloating and ugly compilation errors.
It has the same effect in the cases provided.
Also I don't like the design of span (or string_view for that matter)
(Exercise: add Concepts)
template<class StringRange>
constexpr StringRange&& ToUpperCase(StringRange&& stringview) noexcept {
std::transform(stringview.begin(), stringview.end(), stringview.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); });
return std::forward<StringRange>(stringview);
}
https://godbolt.org/z/e9aWKMerE
I find myself using this idiom quite a bit lately.

Related

Is it possible to union variable conversion function?

For example, there are three variable conversion functions.
//Int
int toInt(std::string input)
{
int ret = strtol(input.c_str(), 0, 10);
return ret;
}
//Double
double toDouble(std::string input)
{
double ret = strtod(input.c_str(), 0);
return ret;
}
//Const char*
const char* toChar(std::string input)
{
return input.c_str();
}
I want to combine these functions like below:
~~ toConvert(std::string input)
{
if ( Variable type is Int )
return strtol(~~~)
else if ( Varibale type is Double )
return strtod(~~~)
...
// Using
int i = toConvert<int>(input);
double d = toConvert<double>(input);
const char* c = toConvert<const char*>(input);
Is it possible?
Please help for implemet above function.
Your "using" code is passing a template argument to toConvert(), so make sure toConvert() is actually a template, and then you can specialize it for each type you want, eg:
template<typename T>
T toConvert(std::string &input) { return T{}; /* or, throw an exception... */ }
template<>
int toConvert<int>(std::string &input)
{
return strtol(input.c_str(), 0, 10);
}
template<>
double toConvert<double>(std::string &input)
{
return strtod(input.c_str(), 0);
}
template<>
const char* toConvert<const char*>(std::string &input)
{
return input.c_str();
}
Or, if you are using C++17 or later, you can instead use if constexpr in a single template:
#include <type_traits>
template<typename T>
T toConvert(std::string &input)
{
if constexpr (std::is_same_v<T, int>)
return strtol(input.c_str(), 0, 10);
else if constexpr (std::is_same_v<T, double>)
return strtod(input.c_str(), 0);
else if constexpr (std::is_same_v<T, const char*>)
return input.c_str();
else
return T{}; // or, throw an exception, or static_assert a compiler error...
}
Notice in either case that I changed the input parameter to std::string&. Your original code was taking in the std::string by value, which means it takes in a copy of the caller's string, and so the const char* conversion would return a dangling pointer to invalid memory when the copied std::string is freed upon return. By taking a reference instead, you avoid that copy.
You might be tempted to take in a const std::string& reference instead, but that would allow calls like toConvert<const char*>("string") to compile but still return a dangling pointer, since the compiler would have to create a temporary std::string to bind to the const reference. But a string literal can't bind to a non-const reference.
That's possible using C++17's if constexpr syntax. For example:
template <typename T>
constexpr bool dependent_false = false;
template <typename T>
T convertTo(const std::string& input)
{
if constexpr (std::is_same_v<T, int>) {
return std::stoi(input);
} else if constexpr (std::is_same_v<T, double>) {
return std::stod(input);
} else {
static_assert(dependent_false<T>, "Can't convert to the specified type");
}
}
Live Demo
The whole dependent_false thing exists to make the static_assertion dependent on the template parameter so that it only gets checked when the function template is instantiated. Just an odd quirk of the C++ template rules.
Note that I left the const char* case out. It has very different semantics from the others, since the pointer returned by c_str points to memory owned by the std::string object.

C++ How can I improve this bit of template meta-program to give back the array including the size?

I've got a utility called choose_literal which chooses a literal string encoded as char*, wchar_*, char8_t*, char16_t*, char32_t* depending on the desired type (the choice).
It looks like this:
template <typename T>
constexpr auto choose_literal(const char * psz, const wchar_t * wsz, const CHAR8_T * u8z, const char16_t * u16z, const char32_t * u32z) {
if constexpr (std::is_same_v<T, char>)
return psz;
if constexpr (std::is_same_v<T, wchar_t>)
return wsz;
#ifdef char8_t
if constexpr (std::is_same_v<T, char8_t>)
return u8z;
#endif
if constexpr (std::is_same_v<T, char16_t>)
return u16z;
if constexpr (std::is_same_v<T, char32_t>)
return u32z;
}
I supply a little preprocessor macro to make this work w/o having to type each of those string encodings manually:
// generates the appropriate character literal using preprocessor voodoo
// usage: LITERAL(char-type, "literal text")
#define LITERAL(T,x) details::choose_literal<T>(x, L##x, u8##x, u##x, U##x)
This of course only works for literal strings which can be encoded in the target format by the compiler - but something like an empty string can be, as can ASCII characters (i.e. a-z, 0-9, etc., which have representations in all of those encodings).
e.g. here's a trivial bit of code that will return the correct empty-string given a valid character type 'T':
template <typename T>
constexpr const T * GetBlank() {
return LITERAL(T, "");
}
This is great as far as it goes, and it works well enough in my code.
What I'd like to do is to refactor this such that I get back the character-array including its size, as if I'd written something like:
const char blank[] = "";
or
const wchar_t blank[] = L"";
Which allows the compiler to know the length of the string-literal, not just its address.
My choose_literal<T>(str) returns only the const T * rather than the const T (&)[size] which would be ideal.
In general I'd love to be able to pass such entities around intact - rather than have them devolve into just a pointer.
But in this specific case, is there a technique you might point me towards that allows me to declare a struct with a data-member for the desired encoding which then also knows its array-length?
A little bit of constexpr recursion magic allows you to return a string_view of the appropriate type.
#include <string_view>
#include <type_traits>
#include <iostream>
template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
using const_char_type = Choice;
using char_type = std::remove_const_t<const_char_type>;
if constexpr (std::is_same_v<T, char_type>)
{
constexpr auto extent = N;
return std::basic_string_view<char_type>(choice, extent - 1);
}
else
{
return choose_literal<T>(rest...);
}
}
int main()
{
auto clit = choose_literal<char>("hello", L"hello");
std::cout << clit;
auto wclit = choose_literal<wchar_t>("hello", L"hello");
std::wcout << wclit;
}
https://godbolt.org/z/4roZ_O
If it were me, I'd probably want to wrap this and other functions into a constexpr class which offers common services like printing the literal in the correct form depending on the stream type, and creating the correct kind of string from the literal.
For example:
#include <string_view>
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
using const_char_type = Choice;
using char_type = std::remove_const_t<const_char_type>;
if constexpr (std::is_same_v<T, char_type>)
{
constexpr auto extent = N;
return std::basic_string_view<char_type>(choice, extent - 1);
}
else
{
return choose_literal<T>(rest...);
}
}
template<class...Choices>
struct literal_chooser
{
constexpr literal_chooser(Choices&...choices)
: choices_(choices...)
{}
template<class T>
constexpr auto choose()
{
auto invoker = [](auto&...choices)
{
return choose_literal<T>(choices...);
};
return std::apply(invoker, choices_);
}
std::tuple<Choices&...> choices_;
};
template<class Char, class...Choices>
std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os, literal_chooser<Choices...> chooser)
{
return os << chooser.template choose<Char>();
}
template<class Char, class...Choices>
std::basic_string<Char> to_string(literal_chooser<Choices...> chooser)
{
auto sview = chooser.template choose<Char>();
return std::basic_string<Char>(sview.data(), sview.size());
}
int main()
{
auto lit = literal_chooser("hello", L"hello");
std::cout << lit << std::endl;
std::wcout << lit << std::endl;
auto s1 = to_string<char>(lit);
auto s2 = to_string<wchar_t>(lit);
std::cout << s1 << std::endl;
std::wcout << s2 << std::endl;
}
The use of the reference argument type Choices& is important. C++ string literals are references to arrays of const Char. Passing by value would result in the literal being decayed into a pointer, which would lose information about the extent of the array.
we can add other services, written in terms of the literal_chooser:
template<class Char, class...Choices>
constexpr std::size_t size(literal_chooser<Choices...> chooser)
{
auto sview = chooser.template choose<Char>();
return sview.size();
}
We're going to change the function so that it takes a const T (&)[size] for each input, and the return type is going to be decltype(auto). Using decltype(auto) prevents the return from decaying into a value, preserving things like references to arrays.
Updated function:
template <typename T, size_t N1, size_t N2, size_t N3, size_t N4>
constexpr decltype(auto) choose_literal(const char (&psz)[N1], const wchar_t (&wsz)[N2], const char16_t (&u16z)[N3], const char32_t (&u32z)[N4]) {
if constexpr (std::is_same<T, char>())
return psz;
if constexpr (std::is_same<T, wchar_t>())
return wsz;
if constexpr (std::is_same<T, char16_t>())
return u16z;
if constexpr (std::is_same<T, char32_t>())
return u32z;
}
In main, we can assign the result to something of type auto&&:
#define LITERAL(T,x) choose_literal<T>(x, L##x, u##x, U##x)
int main() {
constexpr auto&& literal = LITERAL(char, "hello");
return sizeof(literal); // Returns 6
}
Potential simplification
We can simplify the choose_literal function by making it recursive, that way it can be expanded for any number of types. This works without any changes to the LITERAL macro.
template<class T, class Char, size_t N, class... Rest>
constexpr decltype(auto) choose_literal(const Char(&result)[N], Rest const&... rest) {
if constexpr(std::is_same_v<T, Char>)
return result;
else
return choose_literal<T>(rest...);
}

constexpr parametrized function pointer

I have the following third-party API:
using StatisticsFunc = double (*)(const std::vector<double> &)
libraryClass::ComputeStatistics(StatisticsFunc sf);
Which I'm using like this:
obj1->ComputeStatistics([](const auto& v) {return histogram("obj1", v);};
obj2->ComputeStatistics([](const auto& v) {return histogram("obj2", v);};
But all those lambdas are just repeated code. I'd rather have it like this:
obj1->ComputeStatistics(getHistogramLambda("obj1"));
So I need to define:
constexpr auto getHistogramLambda(const char* name) {
return [](const auto& v) {return histogram(name, v);};
}
But it won't work, because name is not captured. Neither will this work:
constexpr auto getHistogramLambda(const char* name) {
return [name](const auto& v) {return histogram(name, v);};
}
Because capturing lambda is not stateless anymore and cannot be cast to function pointer.
Ofc one can do it as a macro, but I want a modern C++ 17 solution.
Passing string as template argument seems an option as well:
https://stackoverflow.com/a/28209546/7432927 , but I'm curious if there's a constexpr way of doing it.
Sort of.
This:
obj1->ComputeStatistics(getHistogramLambda("obj1"));
Won't work for the reasons you point out - you need to capture state. And then, we can't write this:
obj1->ComputeStatistics(getHistogramLambda<"obj1">());
Because while we can have template parameters of type const char* we can't have them bind to string literals. You could do it this way:
template <const char* name>
constexpr auto getHistogramLambda() {
return [](const auto& v) {return histogram(name, v);};
}
const char p[] = "obj1";
obj1->ComputeStatistics(getHistogramLambda<p>());
Which is pretty awkward because you need to introduce the extra variable for each invocation. In C++20, we'll be able to write a class type that has as its template paramater a fixed string, which will allow getHistogramLambda<"obj1"> to work, just in a slightly different way.
Until then, the best way currently is probably to use a UDL to capture the individual characters as template parameters of some class type:
template <char... Cs>
constexpr auto getHistogramLambda(X<Cs...>) {
static constexpr char name[] = {Cs..., '\0'};
return [](const auto& v) { return histogram(name, v);};
}
obj->ComputeStatistic(getHistogramLambda("obj1"_udl));
The intent here is that "obj"_udl is an object of type X<'o', 'b', 'j', '1'> - and then we reconstruct the string within the body of the function template in a way that still does not require capture.
Is this worth it to avoid the duplication? Maybe.
Different answer, courtesy of Michael Park. We can encode the value we want in a type - not passing the string literal we want as a function argument or a template argument, but as an actual type - and that way we don't need to capture it:
#define CONSTANT(...) \
union { static constexpr auto value() { return __VA_ARGS__; } }
#define CONSTANT_VALUE(...) \
[] { using R = CONSTANT(__VA_ARGS__); return R{}; }()
template <typename X>
constexpr auto getHistogramLambda(X) {
return [](const auto& v) { return histogram(X::value(), v);};
}
obj->ComputeStatistic(getHistogramLambda(CONSTANT_VALUE("obj1")));
obj->ComputeStatistic(getHistogramLambda(CONSTANT_VALUE("obj2")));
Not sure this is better than the UDL approach in this particular case, but it's an interesting technique for sure.
Not sure to understand what do you exactly need but... what about declaring a global constexpr array of char const pointers
constexpr std::array<char const *, 3u> arrStr {{"obj0", "obj1", "obj2"}};
then receiving in getHistogramLambda() the index of the required string as template parameter?
template <std::size_t N>
constexpr auto getHistogramLambda () {
return [](const auto& v) {return histogram(arrStr.at(N), v);};
}
This way you can call ComputeStatistic() as follows
obj1->ComputeStatistics(getHistogramLambda<1u>());

How to define string literal with character type that depends on template parameter?

template<typename CharType>
class StringTraits {
public:
static const CharType NULL_CHAR = '\0';
static constexpr CharType* WHITESPACE_STR = " ";
};
typedef StringTraits<char> AStringTraits;
typedef StringTraits<wchar_t> WStringTraits;
I know I could do it with template specialization, but this would require some duplication (by defining string literals with and without L prefix).
Is there a simpler way to define const/constexpr char/wchar_t and char*/wchar_t* with same string literal in a template class?
There are several ways to do this, depending on the available version of the C++ standard.
If you have C++17 available, you can scroll down to Method 3, which is the most elegant solution in my opinion.
Note: Methods 1 and 3 assume that the characters of the string literal will be restricted to 7-bit ASCII. This requires that characters are in the range [0..127] and the execution character set is compatible with 7-bit ASCII (e. g. Windows-1252 or UTF-8). Otherwise the simple casting of char values to wchar_t used by these methods won't give the correct result.
Method 1 - aggregate initialization (C++03)
The simplest way is to define an array using aggregate initialization:
template<typename CharType>
class StringTraits {
public:
static const CharType NULL_CHAR = '\0';
static constexpr CharType WHITESPACE_STR[] = {'a','b','c',0};
};
Method 2 - template specialization and macro (C++03)
(Another variant is shown in this answer.)
The aggregate initialization method can be cumbersome for long strings. For more comfort, we can use a combination of template specialization and macros:
template< typename CharT > constexpr CharT const* NarrowOrWide( char const*, wchar_t const* );
template<> constexpr char const* NarrowOrWide< char >( char const* c, wchar_t const* )
{ return c; }
template<> constexpr wchar_t const* NarrowOrWide< wchar_t >( char const*, wchar_t const* w )
{ return w; }
#define TOWSTRING1(x) L##x
#define TOWSTRING(x) TOWSTRING1(x)
#define NARROW_OR_WIDE( C, STR ) NarrowOrWide< C >( ( STR ), TOWSTRING( STR ) )
Usage:
template<typename CharType>
class StringTraits {
public:
static constexpr CharType const* WHITESPACE_STR = NARROW_OR_WIDE( CharType, " " );
};
Live Demo at Coliru
Explanation:
The template function NarrowOrWide() returns either the first (char const*) or the second (wchar_t const*) argument, depending on template parameter CharT.
The macro NARROW_OR_WIDE is used to avoid having to write both the narrow and the wide string literal. The macro TOWSTRING simply prepends the L prefix to the given string literal.
Of course the macro will only work if the range of characters is limited to basic ASCII, but this is usually sufficient. Otherwise one can use the NarrowOrWide() template function to define narrow and wide string literals separately.
Notes:
I would add a "unique" prefix to the macro names, something like the name of your library, to avoid conflicts with similar macros defined elsewhere.
Method 3 - array initialized via template parameter pack (C++17)
C++17 finally allows us to get rid of the macro and use a pure C++ solution. The solution uses template parameter pack expansion to initialize an array from a string literal while static_casting the individual characters to the desired type.
First we declare a str_array class, which is similar to std::array but tailored for constant null-terminated string (e. g. str_array::size() returns number of characters without '\0', instead of buffer size). This wrapper class is necessary, because a plain array cannot be returned from a function. It must be wrapped in a struct or class.
template< typename CharT, std::size_t Length >
struct str_array
{
constexpr CharT const* c_str() const { return data_; }
constexpr CharT const* data() const { return data_; }
constexpr CharT operator[]( std::size_t i ) const { return data_[ i ]; }
constexpr CharT const* begin() const { return data_; }
constexpr CharT const* end() const { return data_ + Length; }
constexpr std::size_t size() const { return Length; }
// TODO: add more members of std::basic_string
CharT data_[ Length + 1 ]; // +1 for null-terminator
};
So far, nothing special. The real trickery is done by the following str_array_cast() function, which initializes the str_array from a string literal while static_casting the individual characters to the desired type:
#include <utility>
namespace detail {
template< typename ResT, typename SrcT >
constexpr ResT static_cast_ascii( SrcT x )
{
if( !( x >= 0 && x <= 127 ) )
throw std::out_of_range( "Character value must be in basic ASCII range (0..127)" );
return static_cast<ResT>( x );
}
template< typename ResElemT, typename SrcElemT, std::size_t N, std::size_t... I >
constexpr str_array< ResElemT, N - 1 > do_str_array_cast( const SrcElemT(&a)[N], std::index_sequence<I...> )
{
return { static_cast_ascii<ResElemT>( a[I] )..., 0 };
}
} //namespace detail
template< typename ResElemT, typename SrcElemT, std::size_t N, typename Indices = std::make_index_sequence< N - 1 > >
constexpr str_array< ResElemT, N - 1 > str_array_cast( const SrcElemT(&a)[N] )
{
return detail::do_str_array_cast< ResElemT >( a, Indices{} );
}
The template parameter pack expansion trickery is required, because constant arrays can only be initialized via aggregate initialization (e. g. const str_array<char,3> = {'a','b','c',0};), so we have to "convert" the string literal to such an initializer list.
The code triggers a compile time error if any character is outside of basic ASCII range (0..127), for the reasons given at the beginning of this answer. There are code pages where 0..127 doesn't map to ASCII, so this check does not give 100% safety though.
Usage:
template< typename CharT >
struct StringTraits
{
static constexpr auto WHITESPACE_STR = str_array_cast<CharT>( "abc" );
// Fails to compile (as intended), because characters are not basic ASCII.
//static constexpr auto WHITESPACE_STR1 = str_array_cast<CharT>( "äöü" );
};
Live Demo at Coliru
Here is a refinement of the now-common template-based solution which
preserves the array[len] C++ type of the C strings rather than decaying them to pointers, which means you can call sizeof() on the result and get the size of the string+NUL, not the size of a pointer, just as if you had the original string there.
Works even if the strings in different encodings have different length in code units (which is virtually guaranteed if the strings have non-ASCII text).
Does not incur any runtime overhead nor does it attempt/need to do encoding conversion at runtime.
Credit: This refinement starts with the original template idea from Mark Ransom and #2 from zett42 and borrows some ideas from, but fixes the size limitations of, Chris Kushnir's answer.
This code does char and wchar_t but it is trivial to extend it to char8_t+char16_t+char32_t
// generic utility for C++ pre-processor concatenation
// - avoids a pre-processor issue if x and y have macros inside
#define _CPP_CONCAT(x, y) x ## y
#define CPP_CONCAT(x, y) _CPP_CONCAT(x, y)
// now onto stringlit()
template<size_t SZ0, size_t SZ1>
constexpr
auto _stringlit(char c,
const char (&s0) [SZ0],
const wchar_t (&s1) [SZ1]) -> const char(&)[SZ0]
{
return s0;
}
template<size_t SZ0, size_t SZ1>
constexpr
auto _stringlit(wchar_t c,
const char (&s0) [SZ0],
const wchar_t (&s1) [SZ1]) -> const wchar_t(&)[SZ1]
{
return s1;
}
#define stringlit(code_unit, lit) \
_stringlit(code_unit (), lit, CPP_CONCAT(L, lit))
Here we are not using C++ overloading but rather defining one function per char encoding, each function with different signatures. Each function returns the original array type with the original bounds. The selector that chooses the appropriate function is a single character in the desired encoding (value of that character not important). We cannot use the type itself in a template parameter to select because then we'd be overloading and have conflicting return types. This code also works without the constexpr. Note we are returning a reference to an array (which is possible in C++) not an array (which is not allowed in C++). The use of trailing return type syntax here is optional, but a heck of a lot more readable than the alternative, something like const char (&stringlit(...params here...))[SZ0] ugh.
I compiled this with clang 9.0.8 and MSVC++ from Visual Studio 2019 16.7 (aka _MSC_VER 1927 aka pdb ver 14.27). I had c++2a/c++latest enabled, but I think C++14 or 17 is sufficient for this code.
Enjoy!
Here's an alternative implementation based on #zett42 's answer. Please advise me.
#include <iostream>
#include <tuple>
#define TOWSTRING_(x) L##x
#define TOWSTRING(x) TOWSTRING_(x)
#define MAKE_LPCTSTR(C, STR) (std::get<const C*>(std::tuple<const char*, const wchar_t*>(STR, TOWSTRING(STR))))
template<typename CharType>
class StringTraits {
public:
static constexpr const CharType* WHITESPACE_STR = MAKE_LPCTSTR(CharType, "abc");
};
typedef StringTraits<char> AStringTraits;
typedef StringTraits<wchar_t> WStringTraits;
int main(int argc, char** argv) {
std::cout << "Narrow string literal: " << AStringTraits::WHITESPACE_STR << std::endl;
std::wcout << "Wide string literal : " << WStringTraits::WHITESPACE_STR << std::endl;
return 0;
}
I've just came up with a compact answer, which is similar to other C++17 versions. Similarly, it relies on implementation defined behavior, specifically on the environment character types. It supports converting ASCII and ISO-8859-1 to UTF-16 wchar_t, UTF-32 wchar_t, UTF-16 char16_t and UTF-32 char32_t. UTF-8 input is not supported, but more elaborate conversion code is feasible.
template <typename Ch, size_t S>
constexpr auto any_string(const char (&literal)[S]) -> const array<Ch, S> {
array<Ch, S> r = {};
for (size_t i = 0; i < S; i++)
r[i] = literal[i];
return r;
}
Full example follows:
$ cat any_string.cpp
#include <array>
#include <fstream>
using namespace std;
template <typename Ch, size_t S>
constexpr auto any_string(const char (&literal)[S]) -> const array<Ch, S> {
array<Ch, S> r = {};
for (size_t i = 0; i < S; i++)
r[i] = literal[i];
return r;
}
int main(void)
{
auto s = any_string<char>("Hello");
auto ws = any_string<wchar_t>(", ");
auto s16 = any_string<char16_t>("World");
auto s32 = any_string<char32_t>("!\n");
ofstream f("s.txt");
f << s.data();
f.close();
wofstream wf("ws.txt");
wf << ws.data();
wf.close();
basic_ofstream<char16_t> f16("s16.txt");
f16 << s16.data();
f16.close();
basic_ofstream<char32_t> f32("s32.txt");
f32 << s32.data();
f32.close();
return 0;
}
$ c++ -o any_string any_string.cpp -std=c++17
$ ./any_string
$ cat s.txt ws.txt s16.txt s32.txt
Hello, World!
A variation of zett42 Method 2 above.
Has the advantage of supporting all char types (for literals that can be represented as char[]) and preserving the proper string literal array type.
First the template functions:
template<typename CHAR_T>
constexpr
auto LiteralChar(
char A,
wchar_t W,
char8_t U8,
char16_t U16,
char32_t U32
) -> CHAR_T
{
if constexpr( std::is_same_v<CHAR_T, char> ) return A;
else if constexpr( std::is_same_v<CHAR_T, wchar_t> ) return W;
else if constexpr( std::is_same_v<CHAR_T, char8_t> ) return U8;
else if constexpr( std::is_same_v<CHAR_T, char16_t> ) return U16;
else if constexpr( std::is_same_v<CHAR_T, char32_t> ) return U32;
}
template<typename CHAR_T, size_t SIZE>
constexpr
auto LiteralStr(
const char (&A) [SIZE],
const wchar_t (&W) [SIZE],
const char8_t (&U8) [SIZE],
const char16_t (&U16)[SIZE],
const char32_t (&U32)[SIZE]
) -> const CHAR_T(&)[SIZE]
{
if constexpr( std::is_same_v<CHAR_T, char> ) return A;
else if constexpr( std::is_same_v<CHAR_T, wchar_t> ) return W;
else if constexpr( std::is_same_v<CHAR_T, char8_t> ) return U8;
else if constexpr( std::is_same_v<CHAR_T, char16_t> ) return U16;
else if constexpr( std::is_same_v<CHAR_T, char32_t> ) return U32;
}
Then the macros:
#define CMK_LC(CHAR_T, LITERAL) \
LiteralChar<CHAR_T>( LITERAL, L ## LITERAL, u8 ## LITERAL, u ## LITERAL, U ## LITERAL )
#define CMK_LS(CHAR_T, LITERAL) \
LiteralStr<CHAR_T>( LITERAL, L ## LITERAL, u8 ## LITERAL, u ## LITERAL, U ## LITERAL )
Then use:
template<typename CHAR_T>
class StringTraits {
public:
struct LC { // literal character
static constexpr CHAR_T Null = CMK_LC(CHAR_T, '\0');
static constexpr CHAR_T Space = CMK_LC(CHAR_T, ' ');
};
struct LS { // literal string
// can't seem to avoid having to specify the size
static constexpr CHAR_T Space [2] = CMK_LS(CHAR_T, " ");
static constexpr CHAR_T Ellipsis [4] = CMK_LS(CHAR_T, "...");
};
};
auto char_space { StringTraits<char>::LC::Space };
auto wchar_space { StringTraits<wchar_t>::LC::Space };
auto char_ellipsis { StringTraits<char>::LS::Ellipsis }; // note: const char*
auto wchar_ellipsis { StringTraits<wchar_t>::LS::Ellipsis }; // note: const wchar_t*
auto (& char_space_array) [4] { StringTraits<char>::LS::Ellipsis };
auto (&wchar_space_array) [4] { StringTraits<wchar_t>::LS::Ellipsis };
? syntax to get a local copy ?
Admittedly, the syntax to preserve the string literal array type is a bit of a burden, but not overly so.
Again, only works for literals that have the same # of code units in all char type representations.
If you want LiteralStr to support all literals for all types would likely need to pass pointers as param and return CHAR_T* instead of CHAR_T(&)[SIZE]. Don't think can get LiteralChar to support multibyte char.
[EDIT]
Applying Louis Semprini SIZE support to LiteralStr gives:
template<typename CHAR_T,
size_t SIZE_A, size_t SIZE_W, size_t SIZE_U8, size_t SIZE_U16, size_t SIZE_U32,
size_t SIZE_R =
std::is_same_v<CHAR_T, char> ? SIZE_A :
std::is_same_v<CHAR_T, wchar_t> ? SIZE_W :
std::is_same_v<CHAR_T, char8_t> ? SIZE_U8 :
std::is_same_v<CHAR_T, char16_t> ? SIZE_U16 :
std::is_same_v<CHAR_T, char32_t> ? SIZE_U32 : 0
>
constexpr
auto LiteralStr(
const char (&A) [SIZE_A],
const wchar_t (&W) [SIZE_W],
const char8_t (&U8) [SIZE_U8],
const char16_t (&U16) [SIZE_U16],
const char32_t (&U32) [SIZE_U32]
) -> const CHAR_T(&)[SIZE_R]
{
if constexpr( std::is_same_v<CHAR_T, char> ) return A;
else if constexpr( std::is_same_v<CHAR_T, wchar_t> ) return W;
else if constexpr( std::is_same_v<CHAR_T, char8_t> ) return U8;
else if constexpr( std::is_same_v<CHAR_T, char16_t> ) return U16;
else if constexpr( std::is_same_v<CHAR_T, char32_t> ) return U32;
}
It is also possible to use a simpler syntax to create variables;
for example, in StringTraits::LS can change to constexpr auto &
so
static constexpr CHAR_T Ellipsis [4] = CMK_LS(CHAR_T, "...");
becomes
static constexpr auto & Ellipsis { CMK_LS(CHAR_T, "...") };
When using CMK_LS(char, "literal") any invalid char in literal are converted to '?' by VS 2019, not sure what other compilers do.

How to concatenate static strings at compile time?

I am trying to use templates to create an analogue of the type_info::name() function which emits the const-qualified name. E.g. typeid(bool const).name() is "bool" but I want to see "bool const". So for generic types I define:
template<class T> struct type_name { static char const *const _; };
template<class T> char const *const type_name<T>::_ = "type unknown";
char const *const type_name<bool>::_ = "bool";
char const *const type_name<int>::_ = "int";
//etc.
Then type_name<bool>::_ is "bool". For non-const types obviously I could add a separate definition for each type, so char const *const type_name<bool const>::_ = "bool const"; etc. But I thought I would try a partial specialization and a concatenation macro to derive in one line the const-qualified name for any type which has its non-const-qualified name previously defined. So
#define CAT(A, B) A B
template<class T> char const *const type_name<T const>::_
= CAT(type_name<T>::_, " const"); // line [1]
But then type_name<bool const>::_ gives me error C2143: syntax error: missing ';' before 'string' for line [1]. I think that type_name<bool>::_ is a static string known at compile time, so how do I get it concatenated with " const" at compile time?
I tried more simple example but same problem:
char str1[4] = "int";
char *str2 = MYCAT(str1, " const");
I recently revisited this problem, and found that the previous answer I gave produced ridiculously long compile times when concatenating more than a handful of strings.
I have produced a new solution which leverages constexpr functions to remove the recursive templates responsible for the long compilation time.
#include <array>
#include <iostream>
#include <string_view>
template <std::string_view const&... Strs>
struct join
{
// Join all strings into a single std::array of chars
static constexpr auto impl() noexcept
{
constexpr std::size_t len = (Strs.size() + ... + 0);
std::array<char, len + 1> arr{};
auto append = [i = 0, &arr](auto const& s) mutable {
for (auto c : s) arr[i++] = c;
};
(append(Strs), ...);
arr[len] = 0;
return arr;
}
// Give the joined string static storage
static constexpr auto arr = impl();
// View as a std::string_view
static constexpr std::string_view value {arr.data(), arr.size() - 1};
};
// Helper to get the value out
template <std::string_view const&... Strs>
static constexpr auto join_v = join<Strs...>::value;
// Hello world example
static constexpr std::string_view hello = "hello";
static constexpr std::string_view space = " ";
static constexpr std::string_view world = "world";
static constexpr std::string_view bang = "!";
// Join them all together
static constexpr auto joined = join_v<hello, space, world, bang>;
int main()
{
std::cout << joined << '\n';
}
This gives much quicker compile times, even with a large quantity of strings to concatenate.
I personally find this solution easier to follow as the constexpr impl function is akin to how this could be solved at runtime.
Edited with improvements thanks to #Jarod42
EDIT - See my new, improved answer here.
Building on #Hededes answer, if we allow recursive templates, then concatenation of many strings can be implemented as:
#include <string_view>
#include <utility>
#include <iostream>
namespace impl
{
/// Base declaration of our constexpr string_view concatenation helper
template <std::string_view const&, typename, std::string_view const&, typename>
struct concat;
/// Specialisation to yield indices for each char in both provided string_views,
/// allows us flatten them into a single char array
template <std::string_view const& S1,
std::size_t... I1,
std::string_view const& S2,
std::size_t... I2>
struct concat<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>>
{
static constexpr const char value[]{S1[I1]..., S2[I2]..., 0};
};
} // namespace impl
/// Base definition for compile time joining of strings
template <std::string_view const&...> struct join;
/// When no strings are given, provide an empty literal
template <>
struct join<>
{
static constexpr std::string_view value = "";
};
/// Base case for recursion where we reach a pair of strings, we concatenate
/// them to produce a new constexpr string
template <std::string_view const& S1, std::string_view const& S2>
struct join<S1, S2>
{
static constexpr std::string_view value =
impl::concat<S1,
std::make_index_sequence<S1.size()>,
S2,
std::make_index_sequence<S2.size()>>::value;
};
/// Main recursive definition for constexpr joining, pass the tail down to our
/// base case specialisation
template <std::string_view const& S, std::string_view const&... Rest>
struct join<S, Rest...>
{
static constexpr std::string_view value =
join<S, join<Rest...>::value>::value;
};
/// Join constexpr string_views to produce another constexpr string_view
template <std::string_view const&... Strs>
static constexpr auto join_v = join<Strs...>::value;
namespace str
{
static constexpr std::string_view a = "Hello ";
static constexpr std::string_view b = "world";
static constexpr std::string_view c = "!";
}
int main()
{
constexpr auto joined = join_v<str::a, str::b, str::c>;
std::cout << joined << '\n';
return 0;
}
I used c++17 with std::string_view as the size method is handy, but this could easily be adapted to use const char[] literals as #Hedede did.
This answer is intended as a response to the title of the question, rather than the more niche problem described.