The idea is from https://stackoverflow.com/a/15775519/1277762
I added a '\0' at end of string_view so we can use data() for printf, spdlog, et al.
I use this macro to print function name with class name.
However, I find that the compiler is not smart enough to inline the string, but requires a memory copy to stack first:
https://godbolt.org/z/bqob37G3z
See the difference between CALL and CALLFUNC in main function.
Is it possible to tell compiler just put the string in some RO section, like const char *?
template<std::size_t N>
consteval const std::array<char, N> __get_function_name(const char * arr)
{
std::array<char, N> data {};
std::string_view prettyFunction(arr);
size_t bracket = prettyFunction.rfind("(");
size_t space = prettyFunction.rfind(" ", bracket) + 1;
size_t i;
for (i = 0; i < bracket - space; i += 1) {
data[i] = arr[space + i];
}
data[i] = '\0';
return data;
}
#define __METHOD_NAME__ __get_function_name<strlen(__PRETTY_FUNCTION__)>(__PRETTY_FUNCTION__).data()
Thanks #n.m. and user17732522. I finally get a workable version:
https://godbolt.org/z/sof1j3Md4
Still not perfect as this solution needs search '(' and ' ' twice, which might increase compile time.
Updated: only call rfind(" ") once: https://godbolt.org/z/zYcajqaje
#include <array>
#include <string_view>
constexpr size_t my_func_end(const char * arr)
{
std::string_view prettyFunction(arr);
return prettyFunction.rfind("(");
}
constexpr size_t my_func_start(const char * arr, const size_t end)
{
std::string_view prettyFunction(arr);
return prettyFunction.rfind(" ", end) + 1;
}
template<std::size_t S, std::size_t E>
constexpr const std::array<char, E - S + 1> my_get_function_name(const char * arr)
{
std::array<char, E - S + 1> data {};
size_t i;
for ( i = 0; i < E - S; i += 1) {
data[i] = arr[S + i];
}
data[i] = '\0';
return data;
}
template<auto tofix>
struct fixme
{
static constexpr decltype(tofix) fixed = tofix;
};
#define __METHOD_NAME_ARRAY__(x) my_get_function_name<my_func_start(x, my_func_end(x)), my_func_end(x)>(x)
#define __METHOD_NAME__ (fixme<__METHOD_NAME_ARRAY__(__PRETTY_FUNCTION__)>::fixed.data())
Not sure if compiler can cache the calculation. If yes, it is good enough.
The compiler doesn't realize or take into account the specific behavior of puts, namely that it doesn't store the pointer it is passed and doesn't call its caller again.
The problem is that without this knowledge the compiler has to take into account the possibility that puts will store the pointer it is passed in e.g. a global variable, then calls its caller again, and then compares the new pointer argument with the old one stored in the global variable. These must compare unequal because they are pointers into different temporary objects, both in their lifetime. So the compiler can't reuse the same read-only static memory location as the argument to the puts call.
So you need to tell the compiler explicitly to use a static memory location:
#define METHOD_NAME get_function_name<my_strlen(__PRETTY_FUNCTION__)>(__PRETTY_FUNCTION__)
#define CALL() { static constexpr auto v = METHOD_NAME; puts(v.data()); }
Technically the constexpr on v is redundant with consteval on the function. Just constexpr on both would also be sufficient.
If you don't add constexpr on v you might want to add const though to make sure that the compiler won't need to consider the possibility that puts will modify the contents of the string, although it seems that GCC in your example is aware of that. (That puts takes a const char* as argument is not sufficient to establish this.)
You can't use strlen there by the way (assuming you want this to be portable to some other compiler beyond GCC that is supporting __PRETTY_FUNCTION__ in the way you are using it, e.g. Clang with libc++). That GCC is allowing it without diagnostic is not standard-conforming and it is not guaranteed to work on other compilers. std::strlen is not marked constexpr per standard. You can use std::char_traits<char>::length instead, which is constexpr since C++17.
Related
I'm writing a hashing function to help speed up string comparisons.
My codebase compares strings against a lot of const char[] constants, and it would be ideal if I could work with hashes instead. I went ahead and translated xxHash to modern C++, and I have a working prototype that does work at compile time, but I'm not sure what the function definition should be for the main hashing function.
At the moment, I have this:
template <size_t arr_size>
constexpr uint64_t xxHash64(const char(data)[arr_size])
{...}
This does work, and I am able to do a compile time call like this
constexpr char myString[] = "foobar";
constexpr uint64_t hashedString = xxHash64<sizeof myString>(myString);
[Find a minimal example here]
All good so far, but I would like to add a user-defined literal wrapper function for some eye candy, and this is where the problem lies.
UDLs come with a fixed prototype, as specified here
The Microsoft doc stipulates "Also, any of these operators can be defined as constexpr".
But when I try to call my hashing function from a constexpr UDL:
constexpr uint64_t operator "" _hashed(const char *arr, size_t size) {
return xxHash64<size>(arr);
}
function "xxHash64" cannot be called with the given argument list
argument types are: (const char*)
And the error does make sense. My function expects a character array, and instead it gets a pointer.
But if I were to modify the definition of my xxHash64 function to take a const char *, I can no longer work in a constexpr context because the compiler needs to resolve the pointer first, which happens at runtime.
So am I doing anything wrong here, or is this a limitation of UDLs or constexpr functions as a whole?
Again, I'm not 100% sure the templated definition at the top is the way to go, but I'm not sure how else I could read characters from a string at compile time.
I'm not limited by any compiler version or library. If there is a better way to do this, feel free to suggest.
there is no problem to call constexpr function with constexpr pointer as constant expression
constexpr uint64_t xxHash64(const char* s){return s[0];}
constexpr uint64_t operator "" _g(const char *arr,std::size_t){
return xxHash64(arr);
}
int main()
{
xxHash64("foo");
constexpr auto c = "foobar"_g;
return c;
}
would just work fine.
with c++20, you can also get the size as constant expression with string literal operator template.
#include <cstdint>
template <std::size_t arr_size>
constexpr std::uint64_t xxHash64(const char(&data)[arr_size]){
return data[0];
}
// template <std::size_t N> // can also be full class template (with CTAD)
struct hash_value{
std::uint64_t value;
template <std::size_t N>
constexpr hash_value(const char(&p)[N]):value(xxHash64(p)){}
};
template < hash_value v >
constexpr std::uint64_t operator ""_hashed() { return v.value; }
int main()
{
constexpr auto v = "foobar"_hashed;
return v;
}
Let's say I have a function like:
int test(std::array<char, 8>* data) {
char buffer[data->size() * 2];
[... some code ...]
}
clearly the size of the buffer can be evaluated at compile time: data has a constexpr size of 8 elements, 8 * 2 = 16 bytes.
However, when compiling with -Wall, -pedantic and -std=c++11 I get the infamous error:
warning: variable length arrays are a C99 feature [-Wvla-extension]
which I believe makes sense: array::size() is constexpr, but it is still a method, and in the function above we still have to dereference a pointer, which is not constexpr.
If I try something like:
int test(std::array<char, 8>& data) {
char buffer[data.size() * 2];
[...]
}
gcc (tried version 5.2.0) seems happy: there is no warning.
But with clang++ (3.5.1) I still get a warning complaining about variable length arrays.
In my case, I can't easily change the signature of test, it has to take a pointer. So... a few questions:
What is the best / most standard way to get the size of a std::array pointer in constexpr context?
Is the difference in behavior with pointers vs references expected? Which compiler is right about the warning, gcc or clang?
I do not know about 2.
But for 1, we can do this:
template<class T, size_t N>
constexpr std::integral_constant<size_t, N> array_size( std::array<T, N> const& ) {
return {};
}
then:
void test(std::array<char, 8>* data) {
using size=decltype(array_size(*data));
char buffer[size{}];
(void)buffer;
// [... some code ...]
}
alternatively:
template<class T, class U, size_t N>
std::array<T,N> same_sized_array( std::array< U, N > const& ) {
return {};
}
void test(std::array<char, 8>* data) {
auto buffer = same_sized_array<char>(*data);
(void)buffer;
// [... some code ...]
}
finally, a C++14 cleanup:
template<class A>
constexpr const decltype(array_size( std::declval<A>() )) array_size_v = {};
void test3(std::array<char, 8>* data) {
char buffer[array_size_v<decltype(*data)>];
(void)buffer;
// [... some code ...]
}
Live example.
The good old C way would be a define, but C++ has const int or for C++11 constexpr. So if you want the compiler to be aware that the size of the array is a compile time constant, the most portable(*) way would be to make it a const or constexpr:
#include <iostream>
#include <array>
const size_t sz = 8; // constexpr size_t sz for c++11
int test(std::array<char, sz>* data) {
char buffer[sz * 2];
buffer[0] = 0;
return 0;
}
int main()
{
std::array<char, sz> arr = { { 48,49,50,51,52,53,54,55 } };
int cr = test(&arr);
std::cout << cr << std::endl;
return 0;
}
It compiles without a warning, even with -Wall -pedantic under Clang 3.4.1
For the second question, I cannot imagine why gcc make that difference between pointers and refs here. Either it can determine that size() method on an std::array whose size is a constant is a constant expression - and it should allow both - or it cannot - and it should emit same warning on both. But it does not only concern the compiler, but also the standard library implementation.
The real problem is that pre-C++11 std::array was not part of the standard library, and constexpr is also defined only from C++11 on. So in pre-C++11 mode, both compiler process std::array as an extension, but there is no way for the size method to declare its return value to be a constant expr. This explains why Clang (and gcc facing a pointer) emits the warning.
But if you compile original code in c++11 mode (-std=c++11) you should have no warning, because the standard requires size() method on a std::array to be a constexpr.
(*) The question is about best / most standard ; I cannot say what is best way, and I cannot define most standard either, so I stick to what I would use if I wanted to avoid portability problems on non C++11 compilers.
What about using std::tuple_size on the decltype of your parameter ?
void test(std::array<char, 8>* data) {
using data_type = std::remove_pointer<decltype(data)>::type;
char buffer[std::tuple_size<data_type>::value * 2];
static_assert(sizeof buffer == 16, "Ouch");
// [... some code ...]
}
I wondered if I could auto deduce the size of an array, which is passed as a template parameter, without (explicitly) passing its size.
The following code both compiles warning-less on g++ 4.8 and clang++ 3.3 (using -std=c++11 -Wall).
#include <iostream>
template<const int* arr>
struct array_container
{
static constexpr int val = arr[1];
array_container() {
std::cout << val << std::endl;
}
// static constexpr int arr_size = ??;
};
constexpr int one[] = { 1 };
constexpr int two[] = { 1, 2 };
int main()
{
// array_container<one> array_one;
array_container<two> array_two;
// (void) array_one;
(void) array_two;
return 0;
}
However, if I remove the two comment signs in main(), I get an out of bound error with both compilers.
Now, this is cool. Somehow the compiler knows the size of the array, though the type of const int* arr is a pointer. Is there any way to get the size of arr, e.g. to complete my comment in array_container?
Of course, you are not allowed to
Use any macros
Store the size in arr (e.g. passing an std::array as template parameter: constexpr std::array<int, 1> one = { 1 }, or using an end marker like '\0' in strings)
Use an additional template parameter for the size that can not be auto deduced (array_container<1, one> array_one).
Maybe std::extent template from <type_traits> header of C++11 standard library is what you want:
#include <iostream>
#include <type_traits>
constexpr int one[] = { 1 };
constexpr int two[] = { 1, 2 };
int main()
{
std::cout << std::extent<decltype(one)>::value << std::endl;
std::cout << std::extent<decltype(two)>::value << std::endl;
return 0;
}
Output:
1
2
template<size_t size>
constexpr size_t arraySize ( const int ( &arrayRef ) [size] ) {
return size;
}
int main(){
int A[1];
int B[2];
cout << arraySize(A) << arraySize(B);
return 0;
}
I believe something like this is what you're looking for, using array references. The syntax for declaring an array reference looks kind of like the syntax for a function pointer. This function template accepts an array reference named arrayRef, which prevents array-to-pointer decay so that compile-time info about array size is preserved. As you can see, the template argument is implicit to the compiler. Note that this can only work when the size can be deduced at compile time. Interestingly, this should still work without naming arrayRef at all. To make the above template more useful, you can add a template parameter to deduce the type of the array as well. I left it out for clarity.
Probably not, as SFINAE only happens in the immediate context, while that error comes from the requirement that UB in constexpr lead to a compile time error, which I think is not immediate. You could try a recursive SFINAE that stops on the UB, but even if it worked you would have to both check the standard and hope it does not change (as it is rather obscure and new).
The easy way is to ise s function to deduce the array size, have to explicitly pass it to the type, then store it in an auto. Probably not what you want.
There are proposals to allow type parameters to be deduced from value parameters, so you could wait for those instead.
Not a solid answer, more of an extended comment, so marked community wiki.
It is indeed possible. I found a solution using SFINAE. What it basically does is produce a substitution error if the index is out of bound (line 3 in this example):
template<class C>
static yes& sfinae(typename val_to_type<
decltype(*C::cont::data), *(C::cont::data + C::pos)>::type );
template<class C>
static no& sfinae(C );
The full source code is on github.
There are only two disadvantages:
You have to specify the type of the array (this can not be avoided)
It only works with g++ 4.8.1 and clang 3.3. g++ fails for empty strings (with a compiler bug). If someone can test for other compilers, that would be appreciated.
Constexpr can be awsome and useful for compilation optimisation. For example...
strlen(char*)
Can be precompiled using....
constexpr inline size_t strlen_constexpr(char* baseChar) {
return (
( baseChar[0] == 0 )
?(// if {
0
)// }
:(// else {
strlen_constexpr( baseChar+1 ) + 1
)// }
);
}
Which gives it a runtime cost of "0" when optimised... But is more than 10+x slower on runtime
// Test results ran on a 2010 macbook air
--------- strlen ---------
Time took for 100,000 runs:1054us.
Avg Time took for 1 run: 0.01054us.
--------- strlen_constexpr ---------
Time took for 100,000 runs:19098us.
Avg Time took for 1 run: 0.19098us.
Are there any existing macro / template hack where a single unified function can be used instead. ie.
constexpr size_t strlen_smart(char* baseChar) {
#if constexpr
... constexpr function
#else its runtime
... runtime function
}
Or some overloading hack that would allow the following
constexpr size_t strlen_smart(char* baseChar) {
... constexpr function
}
inline size_t strlen_smart(char* baseChar) {
... runtime function
}
Note: This question applies to the concept in general. Of having 2 separate functions for runtime and constexpr instead of the example functions given.
Disclaimer: Setting the compiler to -O3 (optimization level) is more than enough to fix 99.9% of static char optimizations making all the examples above "pointless". But that's beside the point of this question, as it applies to other "examples", and not just strlen.
I don't know any generic way, but I know two specific cases where it is possible.
Specific case of some compilers
Also gcc, and clang which copies all features of gcc, have a built-in function __builtin_constant_p. I am not sure whether gcc will correctly see argument to inline function as constant, but I fear you'd have to use it from a macro:
#define strlen_smart(s) \
(__builtin_constant_p(s) && __builtin_constant_p(*s) ? \
strlen_constexpr(s) : \
strlen(s))
Might be of use. Note that I am testing both s and *s for constexpr, because pointer to static buffer is a compile time constant while it's length is not.
Bonus: Specific case of literals (not an actual answer)
For the specific cast of strlen you can use the fact that string literals are not of type const char * but of type const char[N] that implicitly converts to const char *. But it also converts to const char (&)[N] as well while const char * does not.
So you can define:
template <size_t N>
constexpr size_t strlen_smart(const char (&array)[N])
(plus obviously strlen_smart on const char * forwards to strlen)
I've sometimes used function with this type of argument even in C++98 with definition corresponding to (I didn't try to overload strlen itself, but the overloads were so I could avoid calling it):
template <size_t N>
size_t strlen_smart(const char (&)[N]) { return N - 1; }
This has the problem that for
char buffer[10] = { 0 };
strlen_smart(buffer);
should say 0, but that optimized variant just says 9. The functions don't make sense to be called on buffers like that so I didn't care.
I have code C++11:
template<std::size_t n>
static inline constexpr uint32_t mask() noexcept
{
static_assert(n <= 32, "!");
using list = uint32_t[];
return list{
0x0u,
0x1u, 0x3u, 0x7, 0xfu, 0x1fu, 0x3fu, 0x7fu, 0xffu,
0x1ffu, 0x3ffu, 0x7ffu, 0xfffu, 0x1fffu, 0x3fffu, 0x7fffu, 0xffffu,
0x1ffffu, 0x3ffffu, 0x7ffffu, 0xfffffu, 0x1fffffu, 0x3fffffu, 0x7fffffu, 0xffffffu,
0x1ffffffu, 0x3ffffffu, 0x7ffffffu, 0xfffffffu, 0x1fffffffu, 0x3fffffffu, 0x7fffffffu, 0xffffffffu
} [ n ];
}
Q: where is stored list array? (in static memory, auto memory, or nowhere stored)?
In a normal function, it would be a temporary, stored in automatic memory. Since n is a compile time constant, it may be optimised to simply return the value, removing the array.
However, this is constexpr, so the return value should be computed at compile time. The array should not exist at run time at all.
Since n in the function can not be a variable, e.g.
size_t n;
std::cin >> n;
std::cout << mask<n>() << std::endl;
will fail to compile, because n is not a constant at compile-time, the compiler wouldn't need to store the array at all. In general, a constexpr function should not generate any "code" other than some sort of constant value.
My compiler appears to not accept the code posted here - probably because it's a bit ancient by now (it's a gcc 4.6.3 - I also tried clang++, but it fails because it tries to use the 4.6.3 headers, which apparently isn't in "clang flavour").
Of course, it's much easier to write this:
template<std::size_t n>
inline constexpr uint32_t mask() noexcept
{
static_assert(n <= 32, "!");
return (1u << n) -1;
}
To cope with the special case where n == 32 (and avoiding UB, although in most archtiectures, the above would probably do the right thing):
template<>
inline constexpr uint32_t mask<32>() noexcept
{
return ~0;
}