I am attempting to create the equivalent of the Visual Studio _countof macro using C++ templates. The following are my proposed definitions:
template<typename T, size_t N>
inline constexpr size_t countof(T const (&array)[N]) {
return N;
}
template<typename T, typename U, size_t N>
inline constexpr size_t countof(T const (U::&array)[N]) {
return N;
}
The second declaration above was an attempt to fix the following code, which generates a compile-time error in g++ 9 with the message: "error: invalid use of non-static data member ‘foo::bar’":
struct foo {
int const bar[4];
static_assert(countof(bar) == 4);
};
However, when I add the second definition, and change the assertion to use foo::bar, g++ generates the error: "error: ‘template constexpr const size_t countof’ conflicts with a previous declaration".
I can change the code to use pointer-to-member (instead of reference to member), but that seems like it should be unnecessary. Does anyone know of a way to make a version of countof that only compiles when passed an array, and works in a reasonable way for both free and member variable arrays?
The problem is the usage of bar is invalid in static_assert(countof(bar) == 4);, you need an instance of foo and get the member array bar to pass to countof.
I can change the code to use pointer-to-member (instead of reference to member), but that seems like it should be unnecessary.
You can change the code to use pointer-to-member. e.g.
template<typename T, typename U, size_t N>
inline constexpr size_t countof(T const (U::*array)[N]) {
return N;
}
then
static_assert(countof(&foo::bar) == 4);
LIVE
Or change countof to specify the type instead of passing the array to it.
template<typename T>
struct count_of{};
template<typename T, size_t N>
struct count_of<T const [N]> {
constexpr static size_t value = N;
};
template<typename T>
inline constexpr size_t countof() {
return count_of<T>::value;
}
then
static_assert(countof<decltype(foo::bar)>() == 4);
LIVE
I couldn't figure out a way to do this without macros, but this post provides a way to get both type-safety (ensuring the argument passed to countof is an array) and supports both free and member arrays. The resulting code is:
template<typename T, size_t N>
char (&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T (&array)[N]))[N];
#define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x))
Related
I want to store passed data via constexpr constructor of a struct, and store the data in a std::tuple, to perform various TMP / compile time operations.
Implementation
template <typename... _Ts>
struct myInitializer {
std::tuple<_Ts...> init_data;
constexpr myInitializer(_Ts&&... _Vs)
: init_data{ std::tuple(std::forward<_Ts>(_Vs)...) }
{}
};
Stored data uses a lightweight strong type struct, generated via lvalue and rvalue helper overload:
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... _vs)
: data(std::forward<Ts>(_vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
It's implemented like
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
int main() {
constexpr // fails to run constexpr // works without
auto init = myInitializer (
test<int>::func()
,test<int>::func(3)
,test<int>::func(4,5)
);
std::apply([&](auto&&... args) {
//std::cout << __PRETTY_FUNCTION__ << std::endl;
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
}
, init.init_data);
}
Getting to the point
std::tuple_cat fails if myInitializer instance is constexpr.
std::apply([&](auto&&... args) {
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
It appears to be related to the const qualifier added via constexpr.
How can this be fixed?
See full example at https://godbolt.org/z/j5xdT39aE
This:
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
is not the right way to forward data. decltype(args.data) is going to give you the type of that data member - which is not a function of either the const-ness or value category of args. Let's take a simpler example:
void f(auto&& arg) {
g(std::forward<decltype(arg.data)>(arg.data));
}
struct C { int data; };
C c1{1};
const C c2{2};
f(c1);
f(c2);
f(C{3});
So here I have three calls to f (which call f<C&>, f<const C&>, and f<C>, respectively). In all three cases, decltype(arg.data) is... just int. That's what the type of C::data is. But that's not how it needs to be forwarded (it won't compile for c2 because we're trying to cast away const-ness -- as in your example -- and it'll erroneously move out of c1).
What you want is to forward arg, separately, and then access data:
void f(auto&& arg) {
g(std::forward<decltype(arg)>(arg).data);
}
Now, decltype(arg) actually varies from instantiation to instantiation, which is a good indicator that we're doing something sensible.
In addition of the forwarding problem denoted by Barry, there's a different reason why you cannot have constexpr on init. This is because you contain a reference to a temporary inside data_of_t.
You see, you are containing a type obtained from overload resolution from a forwarding reference:
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
The Ts... in this case could be something like int, float const&, double&. You send those reference type and then you contain them inside of the std::tuple in data_of_t.
Those temporaries are local variables from the test function:
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
The problem here is that p0, p1, p2 are all local variable. You send them in test_of_t which will contain references to them, and you return the object containing all those reference to the local variable. This is maybe the cause of the MSVC crash. Compiler are required to provide diagnostic for any undefined behaviour in constexpr context. This crash is 100% a compiler bug and you should report it.
So how do you fix that?
Simply don't contain references by changing data_of:
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, std::decay_t<Ts>...>(std::forward<Ts>(_vs)...);
};
This will decay the type thus removing the references and decay any reference to C array to pointers.
Then, you have to change your constructor. You call std::forward in there but it's no forwarding occurring if you decay in the template arguments.
template<typename... Vs> requires((std::same_as<std::decay_t<Vs>, Ts>) && ...)
constexpr data_of_t(Vs... _vs)
: data(std::forward<Vs>(_vs)...)
{}
This will add proper forwarding and also constrain it properly so it always do as data_of intended.
Just doing those change will remove UB from the code, but also change it a bit. The type data_of_t will always contain values, and won't contain references. If you want to send a reference, you will need something like std::ref, just like std::bind have to use to defer parameters.
You will still need to use std::forward<decltype(arg)>(arg).data for proper forwarding as #Barry stated
I have a template function array_size which takes a C-style array by reference and returns its size:
template <typename T, std::size_t N>
constexpr std::size_t array_size(T (&)[N]) noexcept {
return N;
}
With this, the following works as expected:
int main() {
int a1[]{1, 2, 3}; // a1's size is 3
int a2[array_size(a1)]; // a2'2 size is also 3
}
If I remove the constexpr in the template function declaration however, the size of a2 becomes 4295032832. The number also does not change between multiple runs.
My question: What is going on if I omit the constexpr keyword?
I am using gcc 9.3.0 on ubuntu.
Please take a look at the code below, sorry that is a bit lengthy, but I did my best to reproduce the problem with a minimum example (there is also a live copy of it). There I basically have a metafunction which returns the size of string literal, and constexpr function which wraps it. Then when I call those functions in a template parameter gcc (5.4, 6.2) is happy with it, but clang (3.8, 3.9) barfs with "non-type template argument is not a constant expression" in test body on strsize(s). If I replace with a str_size<S> both compilers are happy. So the questions are:
whether that is a problem with clang, or my code?
What is the way to make it compile on both clang and gcc with constexpr function?
template<size_t N> using string_literal_t = char[N];
template<class T> struct StrSize; ///< metafunction to get the size of string literal alikes
/// specialize StrSize for string literals
template<size_t N>
struct StrSize <string_literal_t<N>>{ static constexpr size_t value = N-1; };
/// template variable, just for convenience
template <class T>
constexpr size_t str_size = StrSize<T>::value;
/// now do the same but with constexpr function
template<class T>
constexpr auto strsize(const T&) noexcept-> decltype(str_size<T>) {
return str_size<T>;
}
template<class S, size_t... Is>
constexpr auto test_helper(const S& s, index_sequence<Is...>) noexcept-> array<char, str_size<S>> {
return {s[Is]...};
}
template<class S>
constexpr auto test(const S& s) noexcept-> decltype(auto) {
// return test_helper(s, make_index_sequence<str_size<S>>{}); // this work in both clang and gcc
return test_helper(s, make_index_sequence<strsize(s)>{}); // this works only in gcc
}
auto main(int argc, char *argv[])-> int {
static_assert(strsize("qwe") == 3, "");
static_assert(noexcept(test("qwe")) == true, "");
return 0;
}
Clang is correct here. The problem is in the code and in GCC, which erroneously accepted it. This was fixed in GCC 10: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66477
According to the standard expr.const#5.12:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine, would evaluate one of the following:
...
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
it is usable in constant expressions or
its lifetime began within the evaluation of E;
And here the compiler is unable to verify the validity of reference in test(const S& s).
Actually there is a nice article to read here: https://brevzin.github.io/c++/2020/02/05/constexpr-array-size/
As to your other question:
What is the way to make it compile on both clang and gcc with constexpr function?
You can replace references with std::array passed by value:
#include <array>
using namespace std;
template<class T> struct StrSize;
template<size_t N>
struct StrSize <array<char,N>>{ static constexpr size_t value = N-1; };
template <class T>
constexpr size_t str_size = StrSize<T>::value;
template<class T>
constexpr auto strsize(const T&) noexcept-> decltype(str_size<T>) {
return str_size<T>;
}
template<class S, size_t... Is>
constexpr auto test_helper(const S& s, index_sequence<Is...>) noexcept-> array<char, str_size<S>> {
return {s[Is]...};
}
constexpr auto test(array<char,4> s) noexcept-> decltype(auto) {
return test_helper(s, make_index_sequence<strsize(s)>{});
}
int main() {
static_assert(noexcept(test({"qwe"})) == true, "");
}
Demo: https://gcc.godbolt.org/z/G8zof38b1
The following code implements a hash function for a std::tuple which is then used in a different segment of my code-base in a std::unordered_map of std::tuples.
// compute hash function recursively through each std::tuple element
template<class Tuple, std::size_t N>
struct tuple_hash_compute {
static std::size_t hash_compute(const Tuple& t) {
using type = typename std::tuple_element<N-1, decltype(t)>::type; // OFFENDING LINE
return tuple_hash_compute<Tuple, N-1>::hash_compute(t)
+ std::hash<type>()(std::get<N-1>(t));
}
};
// base helper
template<class Tuple>
struct tuple_hash_compute<Tuple, 1> {
static std::size_t hash_compute(const Tuple& t) {
using type = typename std::tuple_element<0, decltype(t)>::type; // OFFENDING LINE
return 51U + std::hash<type>()(std::get<0>(t))*51U;
}
};
// tuple_hash function object
struct tuple_hash {
template<class... Args>
std::size_t operator()(const std::tuple<Args...>& t) const {
return tuple_hash_compute<decltype(t), sizeof...(Args)>::hash_compute(t);
}
// will use std::unordered_map of std::pair too, so overload reqd
template<class Ty1, class Ty2>
std::size_t operator()(const std::pair<Ty1, Ty2>& p) const {
return tuple_hash_compute<decltype(t), 2>::hash_compute(p);
}
};
Then, just as an example, I would use this hash function-object like so,
std::unordered_map<std::tuple<int,int,int>, std::size_t, tuple_hash> agg_map;
agg_map.insert(std::make_pair(std::make_tuple(1,2,3), 0U));
agg_map.insert(std::make_pair(std::make_tuple(4,5,6), 1U));
However, in both GCC 6.1.0 and MSVC2015, I receive the following errors (both the same for each offending line above):
error: invalid use of incomplete type 'class std::tuple_element<2ul, const std::tuple<int,int,int>&>'
I'm not entirely sure what's causing this error (though it may be due to "abstraction" of passing the std::tuple via the template parameter Tuple) or how it can be solved so any help is appreciated.
For a parameter declared as below:
const Tuple& t
decltype(t) yields:
const Tuple&
Similarly, for a parameter declared as:
const std::pair<Ty1, Ty2>& t
decltype(t) yields:
const std::pair<Ty1, Ty2>&
In both cases, the produced type is a reference to a tuple-like type. However, std::tuple_element is not specialized for references, which means, the compiler falls back to the primary, undefined class template:
template <size_t I, typename T> class tuple_element;
What you want, is a plain Tuple in the former case, and std::pair<Ty1, Ty2> in the latter case.
If you have this problem with a type Tuple that may or may not be a reference, you can use std::remove_reference like so:
typename std::tuple_element<num, typename std::remove_reference<Tuple>::type>::type
or, for C++17,
std::tuple_element_t<num, std::remove_reference_t<Tuple>>
PS: I don't think it works with std::reference_wrapper though...
Given a template whose non-type parameter determines the size of a non-const int array member, how can I access the array elements by an integral index at compile time? I want the access to be done via the class template’s getter method at.
I figured since class templates must be instantiated before runtime, I can pass another non-type class template’s enum member value to the prior class’s at method to ensure the index argument is a compile-time constant.
I left the class template deliberate_error undefined to see if its arguments are computed at compile time and to view the compile-time results in the error messages.
template <unsigned int N>
struct compile_time_int {
enum {num = N};
};
template <unsigned int N>
struct array_wrapper {
int arr[N];
template <unsigned int Ind>
constexpr int const& at(compile_time_int<Ind> const& index) const {
return arr[index.num];
}
};
template <unsigned int> struct deliberate_error;
int main() {
compile_time_int<2> cti;
array_wrapper<3> aw;
aw.at(cti);
deliberate_error<cti.num> my_error1;
deliberate_error<aw.at(cti)> my_error2;
}
aw.at(cti); doesn’t give an error, so I thought that if I passed the same expression to deliberate_error instance my_error2, the compiler will display the value of arr[2] in the error message.
my_error1 causes g++ error: aggregate 'deliberate_error<2u> my_error1' has incomplete type and cannot be defined,
showing cti’s wrapped integral value 2. So, I thought if I passed the same cti to object aw's getter, and then pass the result to my_error2, I can get arr[2] in the error message. But instead, it prints:
error: the value of 'aw' is not usable in a constant expression
note: 'aw' was not declared 'constexpr'
note: in template argument for type 'unsigned int'
error: invalid type in declaration before ';'
So, I tried prepending constexpr to aw’s declaration, but that gives even more undesirable errors. What’s wrong here, and how can I fix it?
(Note that as far as I see, std::array with std::get already solves your problem.)
The main issue is that you need your instance aw to be constexpr and of course you need to initialize it with some values:
constexpr array_wrapper<3> aw = { 1, 2, 3 };
Regarding the function at, you can write it as a normal function but simply specify it as constexpr:
constexpr int const& at(int i) const {
return arr[i];
}
Then, aw.at(0) can be used as a constant expression: Live Demo
The advantage of this is that you can use this function in both compile-time and runtime expressions, with static and dynamic indexing, respectively.
If you really want it to be templated, you can either write it as a non-member like std::get<N> or as a class member, but use a template parameter of type int (or size_t or similar). That simplifies its definition (and you can get rid of your compile_time_int class template):
template<int Index>
constexpr int const& at() const {
return arr[Index];
}
Then, aw.at<0>() can be used as a constant expression: Live Demo
The advantage of the second method is that the index is guaranteed to be static, so we can use it in the function for static bound checking, which will not add any performance penalty. I don't know if this is possible with the first version.
Maybe just this:
template <unsigned int N>
struct array_wrapper
{
int arr[N];
};
template <unsigned int I, unsigned int N>
constexpr int & at(array_wrapper<N> & a)
{
static_assert(I < N, "static array index out of bounds");
return a.arr[I];
}
// add a "const" overload, too
Usage:
array_wrapper<10> x;
at<3>(x) = 42;