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.
Related
Given my prototype for a simple hashing method:
template <typename _iterator>
constexpr uint32_t hash_impl(_iterator buf, size_t len);
Generating constexpr hashes for simple strings is pretty trivial:
template <char const* str>
constexpr uint32_t generate()
{
constexpr std::string_view strView = str;
return hash_impl(str, strView.size());
}
constexpr uint32_t generate(const std::string_view& str)
{
return hash_impl(str.begin(), str.size());
}
constexpr static char str1[] = "Hello World!";
constexpr uint32_t hash1 = generate<str1>();
constexpr std::string_view str2("Hello World!");
constexpr uint32_t hash2 = generate(str2);
I'd also like to generate constexpr hashes for a variety of simple (POD and trivial structs) types too. However, I'm not sure how to get the byte representation of these types in a constexpr-friendly way.
My naive implementation:
template <typename T, typename = std::enable_if_t< std::is_standard_layout_v<T> && !std::is_pointer_v<T> >>
constexpr uint32_t generate(const T& value)
{
return hash_impl(reinterpret_cast<const std::byte*>(&value), sizeof(T));
}
falls over because &value and reinterpret_cast<> breaks constexpr rules. I've searched for a workaround, but other answers on the site indicate that it's not possible.
Worst case scenario, I can manually check for and hash specific types as so:
template <typename T, typename = std::enable_if_t< std::is_standard_layout_v<T> && !std::is_pointer_v<T> >>
constexpr uint32_t generate(const T& value)
{
if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
{
char buf[] =
{
static_cast<char>(value >> 0),
static_cast<char>(value >> 8),
static_cast<char>(value >> 16),
static_cast<char>(value >> 24)
};
return generate(buf, 4);
}
else if constexpr (/* etc... */)
// ...
}
but this falls over as soon as I try to implement this for something like a float (which has no bitwise operators) or for trivial custom types (eg, struct foo { int a; }; unless I write an extra code block for foo).
I feel like I must be overlooking something simple, but I can't find any stl utility or think of any fancy template trick that would suit. Perhaps there's something in C++20 or C++23 that I'm not aware of?
It is not possible in C++17. In C++20 you can use std::bit_cast to get the object representation of a trivially copyable type:
auto object_representation = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
(Technically you can argue about whether or not it is guaranteed that std::array has no additional padding/members that would make this ill-formed, but in practice that is not a concern.)
You can then pass the array as a pointer/size pair to the hash implementation. You should probably also add
static_assert(std::has_unique_object_representations_v<T>);
If the assertion fails you will have no guarantee that objects with same value will have the same object representation and same hash.
Btw. std::is_standard_layout_v is not the property you need here. The property that you need to verify is std::is_trivially_copyable_v. Being standard-layout is neither sufficient nor necessary to be able to inspect and use the object representation like this.
I would like to check length of my string literals at compile time. For now I am thinking about the following construction, but can't complete it:
#define STR(s) (sizeof(s) < 10 ? s : /* somehow perform static_assert */)
void foo(const char* s) {}
int main() {
foo(STR("abc")); // foo("abc")
foo(STR("abcabcabcabc")); // compile time error: "String exceeds 10 bytes!"
}
This is C++, where there are superior options to macros. A template can give you the exact semantics your want.
template<std::size_t N>
constexpr auto& STR(char const (&s)[N]) {
static_assert(N < 10, "String exceeds 10 bytes!");
// < 11 if you meant 10 characters. There is a trailing `\0`
// in every literal, even if we don't explicitly specify it
return s;
}
The array reference argument will bind to string literals, not pointers (that can trip your macro), deduce their size, and perform the check in the body of the function. Then it will return the reference unchanged if everything checks out, allowing even for continued overload resolution.
I will add to #StoryTeller - Unslander Monica great answer,
If you need (like me) to pass and argument for string's max length, you can expand the implementation to be more generic :
template<const int MAX_LEN, std::size_t N>
constexpr auto& STR(char const (&s)[N])
{
static_assert(N < MAX_LEN, "String overflow!");
return s;
}
And if you need several known length's, you can use template specialization :
template<std::size_t N>
constexpr auto & STR16(char const (&s)[N])
{
return STR<16>(s);
}
The first function can be a generic version, where's the second can have access to project's consts.
I've read all the answers related with this issue but honestly I'm not sure if I've fully understand the solution. I'm using C++11.
Lets say I really would like to declare something like static constexpr char value[] = "foo".
If I use NetBeans/TDM_MINGW I get an error which I suppose is a link error reporting undefined reference to "variable_name".
Trying the same code in MS VS 2015 I get "expression did not evaluate to a constant".
A simple static constexpr char * solves the problem but I lost the ability of using expressions like sizeof.
Simple and straightforward questions (if possible straightforward anwsers) :
Is there a way to declare a static constexpr char [] inside struct/class?
If 1) is false is there a cleanest solution to overcome this? static constexpr char *????
Or the old static const char [] is still the best approach for this case?
I've tested a solution that works but far from being "clean" static constexpr array<char,50> getConstExpr(){
return array<char,50> {"Hell"}
}. It works fine but I have to declare the size of the char std::array :(
1) Is there a way to declare a static constexpr char [] inside struct/class?
Yes; it's simple.
The following is a full working example
struct bar
{ static constexpr char value[] = "foo"; };
constexpr char bar::value[];
int main ()
{
std::cout << bar::value << std::endl; // print foo
}
I suppose You forgot the bar::value[] row.
2) If 1) is false is there a cleanest solution to overcome this static constexpr char * ????
Not applicable.
3) Or the old static const char [] is still the best approach for this case?
Depend from the problem you have to solve; but usually I suggest to avoid C-style arrays and use the new C++11 std::array
4) I've tested a solution that works but far from being "clean" [...] It works fine but I have to declare the size of the char std::array :(
I propose you a solution (unfortunately work starting from C++14, but isn't too difficult make a C++11 version) to detect the correct size from "Hell" passed as parameter.
Observe the use of std::make_index_sequence and std::index_sequence to pass the single chars from the char[] variable to the std::array.
template <std::size_t Dim, std::size_t ... Is>
constexpr std::array<char, Dim> gceH (char const (&str)[Dim],
std::index_sequence<Is...> const &)
{ return { { str[Is]... } }; }
template <std::size_t Dim>
constexpr std::array<char, Dim> getConstExpr (char const (&str)[Dim])
{ return gceH(str, std::make_index_sequence<Dim>{}); }
int main ()
{
constexpr auto f = getConstExpr("Hell");
static_assert( 5U == f.size(), "!" );
}
-- EDIT --
As suggested by Swift (thanks!) using a template type, instead char, transform getConstExpr() in a more flexible function.
So getConstExpr() and the helper function (gceH()) can be written as follows
template <typename T, std::size_t Dim, std::size_t ... Is>
constexpr std::array<T, Dim> gceH (T const (&str)[Dim],
std::index_sequence<Is...> const &)
{ return { { str[Is]... } }; }
template <typename T, std::size_t Dim>
constexpr std::array<T, Dim> getConstExpr (T const (&str)[Dim])
{ return gceH(str, std::make_index_sequence<Dim>{}); }
If you want to avoid C-style arrays, there is also a C++17 solution using the std::string_view... which is much more simple. ;)
Here is a perm-link of the following code.
#include <iostream>
#include <string_view>
int main ()
{
constexpr std::string_view foo ( "Hell" );
static_assert ( 4U == foo.size (), "!" );
std::cout << "Done";
return EXIT_SUCCESS;
}
Since C++17 you don't need to do anything special, because static constexpr member variables are implicitly inline.
Following will work:
#include <iostream>
struct S
{
static constexpr char value[] = "Meow!\n";
};
int main()
{
std::cout << S::value;
}
For various reasons I am looking for a way to capture the constexpr-ness of arguments passed to a function. It's a bit tricky to explain so I think code best demonstrates what I like to achieve
#include <vector> // For std::size_t
#include <cstdio>
namespace
{
template<std::size_t N, typename ...TArgs>
constexpr int cstrlen (char const (&s) [N], std::size_t i = 0) noexcept
{
return i < N && s[i] != 0
? 1 + cstrlen (s, i + 1)
: 0
;
}
template<std::size_t N, typename ...TArgs>
inline void silly_printf (char const (&format) [N], TArgs && ...args) noexcept
{
static_assert (cstrlen (format) > 0, "format must not be empty string");
printf (format, std::forward<TArgs> (args)...);
}
}
#define SILLY_PRINTF(format, ...) \
static_assert (cstrlen (format) > 0, "format must not be empty string"); \
printf (format, ##__VA_ARGS__);
int main()
{
// This works but relies on macros
SILLY_PRINTF ("Hello: %d", 1);
// This doesn't work
silly_printf ("Hello: %d", 1);
return 0;
}
I can't get silly_printf to work as I want it to. The compiler complains that the expression doesn't evaluate to a constant. We know it's constexpr when calling silly_print with a string literal but the constexpr-ness gets lost (I am using VS2015 here by the way).
I was thinking perhaps I can add constexpr to parameters (much like const) but no success there.
I can work-around this using macros (demonstrated by SILLY_PRINTF macro) but that feels like a failure.
Any ideas most welcome.
PS. What I am really is trying to achieve is slightly less silly
There's a GNU extension (supported by g++ and clang) that allows user defined literals of the form:
template<typename CharT, CharT... Chars>
constexpr void operator"" _something() { }
With this, one can build a constexpr-string type without macros that could be used like this:
constexpr auto str = "testing\0length"_string;
static_assert(str.strlen() == 7, "!");
By encoding all the string's properties into the type, you can then static_assert on it anywhere, constexpr or not.
For example, in your silly_printf:
template<typename CharT, CharT... Chars, typename... Args>
void silly_printf(const constexpr_string<CharT, Chars...>& format_string, Args&&... args) {
static_assert(format_string.strlen() > 0, "format string must not be empty");
printf(format_string.c_str(), args...);
}
And use it like this:
silly_printf("testing %d %s %x embedded\0null"_string, 1, "2", nullptr);
You could also use another operator"" _silly_printf() returning a function object to get a syntax like "format string"_silly_printf(args...).
See it live on Coliru
You don't need to use a char array reference as the argument. Here is one that I use but you need to have c++14 relaxed constexpr rules:
using size_t=decltype(sizeof(int));
namespace util
{
template<typename char_t>
constexpr size_t str_size(const char_t*)noexcept;
}
template
<typename char_t>
constexpr auto
util::
str_size
(const char_t* const a_str)noexcept->size_t
{
const char_t* a_char=a_str;
while(*a_char!=char_t(0))
{
++a_char;
}
return size_t(a_char-a_str);
}
static_assert(util::str_size("hello")==size_t(5),"");
If you can't use c++14 a recursive version will work too. You still just use a char pointer as the argument rather than a char array reference.
const char* s1 = "teststirg";
const char s2[] = "teststirg";
I want a method tell me that s1 is "char*" and s2 is "char[]",how to write the method?
Use templates:
template<typename T, unsigned int SIZE>
bool IsArray (T (&a)[SIZE]) { return true; }
template<typename T>
bool IsArray (T *p) { return false; }
This will evaluate at runtime.
Usage:
if(IsArray(s1))
...
if(IsArray(s2))
...
If interested, you can use some advance techniques, which will tell you this as compile time.
Edit:
typedef char (&yes)[2];
template<typename T, unsigned int SIZE>
yes IsArray (T (&a)[SIZE]);
template<typename T>
char IsArray (T *p);
Usage:
if(sizeof(IsArray(s1)) == sizeof(yes))
...
if(sizeof(IsArray(s2)) == sizeof(yes))
...
If you have access to the original definition, then typeid can be used (but what for, I don't know). If you don't have access to the original definition... There's no way of knowing whether a char* was initialized from another char*, or from an array.
In the above context ( that is in the same method where we have the declaration),
/*1*/ s1[0]='\0';
/*2*/ s2=s1;
/*3 Only This is valid*/ s1=s2;
/*4*/ s2[0]='\0';
Your compiler wouldn't allow step 1,2,4 to pass, while step 3 would succeed. This clearly indicates the nature of the variables. Now, as regards the method (function call) to determine that, you will have to have the definition in the method signature anyways, so I dont see any purpose/utility/possiblity of this method.
determiner (const char* s1,const char *const s2)
You already have the definition in the signature.You need to bypass compiler, to get a use case for this.
I apologise , If I haven't got your requirement correct.