I want to have a method that can be called only with lvalues so I've done the following:
template <typename T>
MyClass& cache(T&) {}
template <typename T>
MyClass& cache(const T&&) = delete;
And this works fine - I can even pass C string literals since they are also lvalues.
The reason I need lvalues is because I'm caching pointers to the passed objects - and that means they cannot be temporaries.
The following code works:
MyClass a;
a.cache("string literal");
int temp = 6;
a.cache(temp);
And the following code (as desired) does not work:
int getInt(); // fwd decl
a.cache(getInt());
But I also want to be able to pass other literals - but they all seem to be rvalues...
The following code does not work (but I wish it could):
MyClass a;
a.cache(6);
Is there a way to distinguish between such literals and non-literal rvalues?
Is there a way to easily turn such literals into lvalues? I found this answer on Stack Overflow mentioning something like unless you write 1L but even if I give an L suffix to a literal it is still a temporary.
Even a macro is OK - something like this: a.cache(TURN_TO_LVALUE(6)); (or CONSTANTIZE(6))
You may be confused by "hello"; it is a literal, but it (in a sense) is also an lvalue (if a const one). "hello" creates an object that doesn't go away when the line ends, an array of const characters consisting of {'h', 'e', 'l', 'l', 'o', '\0'}. Two different "hello" could refer to the same object or not.
6 doesn't do the same thing; there is no persistant 6 in a C++ program with the constant 6 in it, there is a persistant "hello" in a C++ program with the string constant "hello" in it.
The literal 6 can cause a temporary to be instantiated. The lifetime of this temporary is until the end of the expression it is in (the "end of the line" as it where).
You cannot distinguish between the temporary created by 6 and the temporary returned from a function during a function call. This is fortunate, because both are temporaries with the same advantages and disadvantages.
A pointer to that temporary is going to be invalid at that point; even == or < on that pointer is undefined behavior.
So a.cache(6) is a bad plan.
Now, we could do something horrible.
unsigned long long const& operator""_lvalue(unsigned long long x) {
thread_local unsigned long long value;
value = x;
return value;
}
live example.
This creates a static storage duration lvalue value and copies x into it. So 6_lvalue + 4_lvalue is going to be either 8 or 12, never 10, as the single lvalue is overwritten.
We can remove that overwriting problem with more template abuse.
template<int P>
constexpr unsigned long long pow_( unsigned x, std::size_t tens ) {
if (tens == 0) return x;
return P*pow_<P>(x, tens-1);
}
template<int base>
constexpr unsigned long long ucalc(std::integer_sequence<char>) {
return 0;
}
constexpr unsigned digit( char c ) {
if (c >= '0' && c <= '9') return c-'0';
if (c >= 'a' && c <= 'z') return c-'a'+10;
if (c >= 'A' && c <= 'Z') return c-'A'+10;
exit(-1);
}
template<int base, char c0, char...chars>
constexpr unsigned long long ucalc(std::integer_sequence<char, c0, chars...>) {
return pow_<base>( digit(c0), sizeof...(chars) ) + ucalc<base>( std::integer_sequence<char, chars...>{} );
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, chars...>) {
return ucalc<10>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'x', chars...>) {
return ucalc<16>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'X', chars...>) {
return ucalc<16>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'b', chars...>) {
return ucalc<2>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'B', chars...>) {
return ucalc<2>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', chars...>) {
return ucalc<8>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc() {
return calc( std::integer_sequence<char, chars...>{} );
}
template<class T, T x>
constexpr T lvalue = x;
template <char... chars>
unsigned long long const& operator "" _lvalue() {
return lvalue<unsigned long long, calc<chars...>()>;
}
live example. About half of this is 0b and 0x and 0 binary/hex/octal support.
Use:
a.cache(6_lvalue);
Alternatively, in C++17 you can do this:
template<auto x>
constexpr auto lvalue = x;
or in C++14
template<class T, T x>
constexpr T lvalue = x;
with
#define LVALUE(...) lvalue<std::decay_t<decltype(__VA_ARGS__)>, __VA_ARGS__>
In C++17 it is lvalue<7> and in C++14 it is LVALUE(7).
Related
I have very big code-base, which uses __FILE__ extensively for logging. However, it includes full path, which is (1) not needed, (2) might case security violations.
I'm trying to write compile-time sub-string expression. Ended up with this solution
static constexpr cstr PastLastSlash(cstr str, cstr last_slash)
{
return *str == '\0' ? last_slash : *str == '/' ? PastLastSlash(str + 1, str + 1) : PastLastSlash(str + 1, last_slash);
}
static constexpr cstr PastLastSlash(cstr str)
{
return PastLastSlash(str, str);
}
// usage
PastLastSlash(__FILE__);
This works good, I've checked assembly code, line is trimmed in compile time, only file name is present in binary.
However, this notation is too verbose. I would like to use macro for this, but failed. Proposed example from the link above
#define __SHORT_FILE__ ({constexpr cstr sf__ {past_last_slash(__FILE__)}; sf__;})
doesn't work for MSVC compiler (I'm using MSVC 2017). Is there any other method do to so using c++17?
UPD1: clang trimmed by function https://godbolt.org/z/tAU4j7
UPD2: looks like it's possible to do trim on compile time using functions, but full string is swill be present in binary.
The idea is to create truncated array of characters, but it needs to use only compile time features. Generating data array through variadic template with pack of char forces compiler to generate data without direct relation to passed string literal. This way compiler cannot use input string literal, especially when this string is long.
Godbolt with clang: https://godbolt.org/z/WdKNjB.
Godbolt with msvc: https://godbolt.org/z/auMEIH.
The only problem is with template depth compiler settings.
First we define int variadic template to store sequence of indexes:
template <int... I>
struct Seq {};
Pushing int to Seq:
template <int V, typename T>
struct Push;
template <int V, int... I>
struct Push<V, Seq<I...>>
{
using type = Seq<V, I...>;
};
Creating sequence:
template <int From, int To>
struct MakeSeqImpl;
template <int To>
struct MakeSeqImpl<To, To>
{
using type = Seq<To>;
};
template <int From, int To>
using MakeSeq = typename MakeSeqImpl<From, To>::type;
template <int From, int To>
struct MakeSeqImpl : Push<From, MakeSeq<From + 1, To>> {};
Now we can make sequence of compile time ints, meaning that MakeSeq<3,7> == Seq<3,4,5,6,7>. Still we need something to store selected characters in array, but using compile time representation, which is variadic template parameter with characters:
template<char... CHARS>
struct Chars {
static constexpr const char value[] = {CHARS...};
};
template<char... CHARS>
constexpr const char Chars<CHARS...>::value[];
Next we something to extract selected characters into Chars type:
template<typename WRAPPER, typename IDXS>
struct LiteralToVariadicCharsImpl;
template<typename WRAPPER, int... IDXS>
struct LiteralToVariadicCharsImpl<WRAPPER, Seq<IDXS...> > {
using type = Chars<WRAPPER::get()[IDXS]...>;
};
template<typename WRAPPER, typename SEQ>
struct LiteralToVariadicChars {
using type = typename LiteralToVariadicCharsImpl<WRAPPER, SEQ> :: type;
};
WRAPPER is a type that contain our string literal.
Almost done. The missing part is to find last slash. We can use modified version of the code found in the question, but this time it returns offset instead of pointer:
static constexpr int PastLastOffset(int last_offset, int cur, const char * const str)
{
if (*str == '\0') return last_offset;
if (*str == '/') return PastLastOffset(cur + 1, cur + 1, str + 1);
return PastLastOffset(last_offset, cur + 1, str + 1);
}
Last util to get string size:
constexpr int StrLen(const char * str) {
if (*str == '\0') return 0;
return StrLen(str + 1) + 1;
}
Combining everything together using define:
#define COMPILE_TIME_PAST_LAST_SLASH(STR) \
[](){ \
struct Wrapper { \
constexpr static const char * get() { return STR; } \
}; \
using Seq = MakeSeq<PastLastOffset(0, 0, Wrapper::get()), StrLen(Wrapper::get())>; \
return LiteralToVariadicChars<Wrapper, Seq>::type::value; \
}()
Lambda function is to have nice, value-like feeling when using this macro. It also creates a scope for defining Wrapper structure. Generating this structure with inserted string literal using macro, leads to situation when the string literal is bounded to type.
Honestly I would not use this kind of code in production. It is killing compilers.
Both, in case of security reasons and memory usage, I would recommend using docker with custom, short paths for building.
You can using std::string_view:
constexpr auto filename(std::string_view path)
{
return path.substr(path.find_last_of('/') + 1);
}
Usage:
static_assert(filename("/home/user/src/project/src/file.cpp") == "file.cpp");
static_assert(filename("./file.cpp") == "file.cpp");
static_assert(filename("file.cpp") == "file.cpp");
See it compile (godbolt.org).
For Windows:
constexpr auto filename(std::wstring_view path)
{
return path.substr(path.find_last_of(L'\\') + 1);
}
With C++17, you can do the following (https://godbolt.org/z/68PKcsPzs):
#include <cstdio>
#include <array>
namespace details {
template <const char *S, size_t Start = 0, char... C>
struct PastLastSlash {
constexpr auto operator()() {
if constexpr (S[Start] == '\0') {
return std::array{C..., '\0'};
} else if constexpr (S[Start] == '/') {
return PastLastSlash<S, Start + 1>()();
} else {
return PastLastSlash<S, Start + 1, C..., (S)[Start]>()();
}
}
};
}
template <const char *S>
struct PastLastSlash {
static constexpr auto a = details::PastLastSlash<S>()();
static constexpr const char * value{a.data()};
};
int main() {
static constexpr char f[] = __FILE__;
puts(PastLastSlash<f>::value);
return 0;
}
With C++14, it's a bit more complicated because of the more limited constexpr (https://godbolt.org/z/bzGec5GMv):
#include <cstdio>
#include <array>
namespace details {
// Generic form: just add the character to the list
template <const char *S, char ch, size_t Start, char... C>
struct PastLastSlash {
constexpr auto operator()() {
return PastLastSlash<S, S[Start], Start + 1, C..., ch>()();
}
};
// Found a '/', reset the character list
template <const char *S, size_t Start, char... C>
struct PastLastSlash<S, '/', Start, C...> {
constexpr auto operator()() {
return PastLastSlash<S, S[Start], Start + 1>()();
}
};
// Found the null-terminator, ends the search
template <const char *S, size_t Start, char... C>
struct PastLastSlash<S, '\0', Start, C...> {
constexpr auto operator()() {
return std::array<char, sizeof...(C)+1>{C..., '\0'};
}
};
}
template <const char *S>
struct PastLastSlash {
const char * operator()() {
static auto a = details::PastLastSlash<S, S[0], 0>()();
return a.data();
}
};
static constexpr char f[] = __FILE__;
int main() {
puts(PastLastSlash<f>{}());
return 0;
}
With C++20, it should be possible to pass __FILE__ directly to the template instead of needing those static constexpr variables
I am currently writing some template code where the template parameter is the char type to use. This causes a problem when referring to literal strings. I can, of course, make a struct with the strings I use but I was thinking if it would be possible to make something like:
template<typename chartype, char32_t... chars>
struct tr {
/* some magic here */
};
so that tr<char32_t,U"hello world"> would result in U"hello world"
and tr<char16_t,U"Hello world"> would result in u"hello world"
and tr<char,U"hello world"> would result in "hello world" (in UTF-8).
The magic should of course correctly translate codes above 0x10000 to lead code and follow code for char16_t and to proper 2, 3 and 4 byte codes for UTF-8
at compile time.
Problem is: how do you define a constant C-style string of a given char type using the char32_t... chars template argument? You can extract the characters
from it but how do you rebuild a new string based on the chars of the input string in template code?
Note, the preprocessor can correctly define a string such as "hello world" with suitable prefix u or U if you like but it cannot access the individual characters of the string to properly translate it.
EDIT:
Strings as template arguments are definitely possible in new C++, however,
the template argument is NOT declared as const char * or something like that:
template <char... txt>
struct foo { ... }
allows you to write foo<"hello"> as a type with the string "hello" as template argument. The problem is how to build the string from those characters.
I mean at some point you want the struct to contain a string value to return:
template <char32_t... txt>
struct foo;
template <>
struct foo<> {
static const char16_t * txt() { return ""; }
};
template <char32_t a, char32_t... rest>
struct foo<a, rest...> {
static const char16_t * txt()
{
char16_t buf[100];
int k = 0;
if (a < 0x10000) buf[k++] = a;
else {
buf[k++] = 0xd800 | ((a - 0x10000) >> 10);
buf[k++] = 0xdc00 | ((a-0x10000) & 0x3ff);
}
// copy rest of string into buf + 2..99
u16strcpy(buf + k, foo<rest...>::txt());
return buf;
}
}
Several obvious problems with this "solution", one problem is that buf only have room for 100 characters which will fail if the string is longer. but the main problem is that I wanted this to happen in compile time and this looks very much like run time code to me and is not at all what I wanted to do.
Basically I wanted something that works this way:
foo<char, "hello"> results in something that is effectively a string literal
"hello" or u8"hello".
foo<char16_t, "hello"> results in something that is effectively a string literal u"hello" and foo<char32_t, "hello"> results in something that is effectively a string literal U"hello".
The problem is when writing template code to handle various character formats and then having string literals involved. Yes, you can write a simple struct:
template <typename ChT> struct bar;
template <>
struct bar<char32_t> {
static const char32_t * txta = U"AAAA";
static const char32_t * txtb = U"BBBB";
};
and so on and bar<char16_t> has txta = u"AAAA" etc. Then refer to the strings
in your templated code by bar<T>::txta etc. However, I wish there was a way that you could specify those strings directly in templated code and the compiler would do the right thing. A templated string literal in other words.
Maybe it should be added as a feature to the language that
T<char32_t> string-literal is the same as U string-literal etc
so that you could write
template <typename ChT>
struct foo {
static const ChT * txta = T<ChT> "AAAAA";
};
and the compiler would do the right thing.
This would appear to simply not be legal, even the following is rejected (vs2017, with standard set to latest):
template<char const * ptr>
struct test
{};
void bar()
{
test<"testing"> t;
}
with the error: invalid expression as a template argument for 'ptr', and if that's not going to work trying to convert it at compile-time is a non-starter. And honestly this doesn't seem all that surprising that a pointer-to-data isn't constant enough. to be a template argument.
Here are some tools to make it work in C++17 (might be portable to C++11 and C++14):
static constexpr data member of templated class
The output literal you wish to work with needs some "storage". I suggest to instantiate a unique class template for each literal, e.g., Literal<char, 'f', 'o', 'o', '\0'>. That class can hold the data as astatic constexpr` member.
template<class C, C... cs>
struct Literal {
static_assert(sizeof...(cs) >= 1);
static constexpr C data[] = {cs...};// or use `std::array<C, sizeof...(cs)>`
};
template<class C, C... cs>
constexpr C Literal<C, cs...>::data[];
user-defined string literal to simplify syntax
Of course you wish to avoid typing, e.g., Literal<char, 'f', 'o', 'o', '\0'>. A useful tool to achieve that is the following overload for user-defined string literals.
template<class C, C... cs>
constexpr Literal<C, cs..., C('\0')> operator""_c() {// or use `auto`
return Literal<C, cs..., C('\0')>{};
}
Note how the input literal is passed as non-type template parameters to that overload. That way, it is possible to "carry the value as a type".
constexpr algorithms for re-encoding
So far, you can type "foo"_c to obtain a Literal<char, 'f', 'o', 'o', '\0'> which has a static constexpr data member yielding the same as "foo". Next you can pass that Literal<char, 'f', 'o', 'o', '\0'> to a function which returns a const char16_t(&)[4] to data of the corresponding Literal<char16_t, ..., '\0'>. The syntax could read tr<char16_t>("foo"_c).
The code that transforms a Literal<char, ...> into the corresponding Literal<char16_t, ...> may be based on constexpr algorithms as shown below.
template<
class OutChar, class InChar, InChar... cs,
std::size_t... input_indices, std::size_t... output_indices
>
constexpr auto& tr_impl(// called by `tr` with appropriate `index_sequence`s
Literal<InChar, cs...>,
std::index_sequence<input_indices...>,
std::index_sequence<output_indices...>
) {
constexpr std::size_t outsize = sizeof...(output_indices);
using Buffer = std::array<OutChar, outsize>;
constexpr Buffer buf = encode_as<OutChar, outsize>({cs...});
return Literal<OutChar, buf[output_indices]...>::data;
}
template<class OutChar, class InChar, InChar... cs>
constexpr auto& tr(Literal<InChar, cs...> literal) {
constexpr std::size_t outsize = count_as<OutChar>({cs...});
return tr_impl<OutChar>(
literal,
std::make_index_sequence<sizeof...(cs)>{},// input indices
std::make_index_sequence<outsize>{}// output indices
);
}
The remaining part would be to implement those functions count_as and encode_as.
assign to constexpr auto& in final usage
Finally you can assign to constexpr auto& to verify that type and value are equivalent to the plain string literal based on the desired character type.
static_assert(std::size(U"ππ") == 3);
static_assert(std::size(u"ππ") == 5);
constexpr auto& test = tr<char16_t>(U"ππ"_c);
static_assert(std::is_same<decltype(test), const char16_t(&)[5]>{});
for(std::size_t i=0; i<std::size(u"ππ"); ++i) {
assert(test[i] == u"ππ"[i]);
std::cout << i << ": " << test[i] << std::endl;
}
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)
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;
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.
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.
Why isn't the following literal operator template recognised?
template<char... Chars>
constexpr int operator"" _p(){
return 0;
}
int main()
{
int fs1 = "123"_p;
}
Update:
Thanks to the answer I use now:
#include <avr/pgmspace.h>
template<typename C, C... CC>
struct PgmString {
static constexpr const char* str(){
return &data[0];
}
static constexpr const char data[] PROGMEM = {CC..., '\0'};
};
template<typename C, C... CC>
constexpr const char PgmString<C, CC...>::data[] PROGMEM;
template<typename C, C... CC>
constexpr PgmString<C, CC...> operator"" _pgm(){
return PgmString<C, CC...>();
}
This puts a string into flash on a AVR microcontroller.
But if I increase the literal string length by one character the used flash memory size increases by two(!) bytes.
gcc/clang has extension to allow:
template<typename Char, Char... Cs>
constexpr int operator"" _p(){
return 0;
}
and then
int main()
{
int fs1 = "123"_p;
}
The form of literal you expect doesn't exist. Only integer and floating literals have template forms; string and character literals do not.
I got this compile time string comparison from another thread using constexpr and C++11 (http://stackoverflow.com/questions/5721813/compile-time-assert-for-string-equality). It works with constant strings like "OK"
constexpr bool isequal(char const *one, char const *two) {
return (*one && *two) ? (*one == *two && isequal(one + 1, two + 1))
: (!*one && !*two);
}
I am trying to use it in the following context:
static_assert(isequal(boost::mpl::c_str<boost::mpl::string<'ak'>>::value, "ak"), "should not fail");
But it gives me an compilation error of static_assert expression is not an constant integral expression.
Can I do this?
The problem is that the value member of mpl::c_str is not marked as constexpr. Until the library authors decide to include support for constexpr, you are pretty much screwed, unless you are willing to modify your Boost code (or create your own version of c_str). If you decide to do so, the modification is quite simple: you just need to locate BOOST_ROOT/boost/mpl/string.hpp and replace this
template<typename Sequence>
struct c_str
{
...
static typename Sequence::value_type const value[BOOST_MPL_LIMIT_STRING_SIZE+1]
};
template<typename Sequence>
typename Sequence::value_type const c_str<Sequence>::value[BOOST_MPL_LIMIT_STRING_SIZE+1] =
{
#define M0(z, n, data) \
mpl::aux_::deref_unless<BOOST_PP_CAT(i, n), iend>::type::value,
BOOST_PP_REPEAT(BOOST_MPL_LIMIT_STRING_SIZE, M0, ~)
#undef M0
'\0'
};
by this
template<typename Sequence>
struct c_str
{
...
static constexpr typename Sequence::value_type value[BOOST_MPL_LIMIT_STRING_SIZE+1] =
{
#define M0(z, n, data) \
mpl::aux_::deref_unless<BOOST_PP_CAT(i, n), iend>::type::value,
BOOST_PP_REPEAT(BOOST_MPL_LIMIT_STRING_SIZE, M0, ~)
#undef M0
'\0'
};
};
// definition still needed
template<typename Sequence>
constexpr typename Sequence::value_type c_str<Sequence>::value[BOOST_MPL_LIMIT_STRING_SIZE+1];
Hmm, after digging a bit more, it turns out the problem is more complex than I thought. In fact, static constants can be used in constexpr; the true problem is that c_str<T>::value is an array, and your function takes pointers as parameters. As a consequence, the compiler needs to decay the array, which boils down to taking the address of its first element. Since addresses are a runtime concept, it is not possible to take the address of an object in a constexpr.
To solve the issue, I tried to write a second version of isequal that operates on arrays rather than on pointers:
template <int N, int M>
constexpr bool isequal(char const (&one)[N], char const (&two)[M], int index)
{
return (one[index] && two[index]) ?
(one[index] == two[index] && isequal(one, two, index + 1)) :
(!one[index] && !two[index]);
}
template <int N, int M>
constexpr bool isequal(char const (&one)[N], char const (&two)[M])
{
// note: we can't check whether N == M since the size of the array
// can be greater than the actual size of the null-terminated string
return isequal(one, two, 0);
}
constexpr char hello[] = "hello";
static_assert(isequal(hello, hello), "hello == hello");
constexpr char zello[] = "zello";
static_assert(!isequal(hello, zello), "hello != zello");
constexpr char hel[] = "hel";
static_assert(!isequal(hello, hel), "hello != hel");
Unfortunately, this code does not work with mpl::c_str; in fact, the problem is that static const arrays are not compile-time values, unlike integral constants. So we're back the beginning: unless value is marked as constexpr, there is no way to use it in a constant expression.
As to why the code I gave initially fails, I can't answer right now as my version of gcc (4.6) fails to compile it altogether...
After updating gcc, it turns out value needs to be defined outside the class, even though it is declared and initialized in the class (see this question). I edited the code above with the correction.