Reputation: 1856
When I built a function which gives the hexadecimal representation of a nibble (4 bits) and I looked at the binary file, for the lookuptable of the digits, there was an additional 0-char even if it was not used.
const char digits[] = "0123456789abcdef";
I know that you can write it in form of an array:
const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
But that would take a while to write and use more disk-space for other numerical-systems with more digits. But is there any way to write it as a literal, but without the null-character at the end?
(I am using Clang with -std=c++14
)
Upvotes: 0
Views: 639
Reputation: 54999
This might be overkill, but if it’s acceptable to use std::array
, then you can statically build one from a string literal using constexpr
functions:
#include <array>
#include <iostream>
// A type-level list of indices.
template <size_t... Xs>
struct indices { using type = indices<Xs..., sizeof...(Xs)>; };
// Generate indices from 0 to N.
template <std::size_t N>
struct upto { using type = typename upto<N - 1>::type::type; };
template <>
struct upto<0> { using type = indices<>; };
// Make an array by assigning each character
// from the corresponding index in the source.
template <std::size_t... X, typename A = std::array<char, sizeof...(X)>>
constexpr A make_array(char const *const source, const indices<X...>&) {
return A{{source[X]...}};
}
// A convenience function that deduces the size.
template <std::size_t N>
constexpr std::array<char, N - 1> string_constant(char const (&data)[N]) {
return make_array(&data[0], typename upto<N - 1>::type{});
}
int main() {
constexpr auto s = string_constant("123456");
std::cout << sizeof(s) << '\n';
}
Now sizeof(s)
is 6
, and if you look at the generated assembly, the string literal is stored as .ascii
, not .asciz
, so there is no trailing null character. You can use the member functions of std::array
such as size()
, begin()
, and end()
, and can cast &s[0]
to const char *
to access character data directly.
Upvotes: 0
Reputation: 16824
I have no idea why you'd want to do such a thing, but if you can cope with using a compiler extension, Clang and GCC will let you write a templated user-defined literal operator which will chop off the trailing null:
template <typename CharT, std::size_t N>
struct string_literal {
static constexpr std::size_t size = N;
const CharT str[N];
};
template <typename CharT, CharT... Str>
constexpr string_literal<CharT, sizeof...(Str)> operator"" _lit()
{
return {{ Str... }};
}
int main()
{
constexpr auto s = "test"_lit;
static_assert(s.size == 4);
static_assert(s.str[0] == 't'); // etc
}
(Returning a std::array<const char, N>
is another option.)
I've no idea whether this is what you're after, but then I don't really understand the motivation to be honest -- even back in the 70s the designers of C weren't too worried about "wasting" a single byte in string literal.
Upvotes: 2
Reputation: 35154
In C, you could write
const char digits[16] = "0123456789abcdef";
This is actually supported in C as defined in array initialization:
If the size of the array is known, it may be one less than the size of the string literal, in which case the terminating null character is ignored:
char str[3] = "abc"; // str has type char[3] and holds 'a', 'b', 'c'
For C++, I see no direct way; but one could come around this as follows:
char digits_t[16] = "0123456789abcde";
digits_t[15]='f';
const char* digits = digits_t;
Upvotes: 3