Inlining of a recursive function - c++

When I try to compile this code:
#include <iostream>
#include <limits.h>
// End recursive template-expansion of function select below.
template <typename Type>
static inline constexpr Type select(unsigned index)
{ return Type(); }
// Select one of the items passed to it.
// e.g. select(0, a, b, c) = a; select(1, a, b, c) = b; etc.
template <typename Type, typename... Params>
[[gnu::always_inline]]
static inline constexpr Type select(unsigned index, Type value, Params... values)
{ return index == 0 ? value : select<Type>(index - 1, values...); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_1(Type mask, Type shift, Type value)
{ return ((value & mask) >> shift) | ((value << shift) & mask); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
{
return i == 0
? value
: reflect_mask_helper_0(
i - 1,
reflect_mask_helper_1<Type>(
select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
1 << (i - 1),
value));
}
template <typename Type>
[[gnu::flatten]]
static inline constexpr Type reflect_mask(Type value)
{ return reflect_mask_helper_0(__builtin_ctz(sizeof(Type) * CHAR_BIT), value); }
int main(void) {
for (int i = 0; i < 65536; i++) {
std::cout << reflect_mask<uint16_t>(i) << std::endl;
}
}
gcc gives me an error saying the function reflect_mask_helper_0 cannot be inlined because it is recursive. However, the function select is also recursive, but gcc inlines it without complaining. What am I missing here?
(I need it to be recursive, since constexpr functions cannot contain loops under C++11.)
Error message:
% g++ test.cpp -O3 -march=native -c
test.cpp: In function ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’:
test.cpp:23:30: error: inlining failed in call to always_inline ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’: recursive inlining
23 | static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
| ^~~~~~~~~~~~~~~~~~~~~
test.cpp:27:28: note: called from here
27 | : reflect_mask_helper_0(
| ~~~~~~~~~~~~~~~~~~~~~^
28 | i - 1,
| ~~~~~~
29 | reflect_mask_helper_1<Type>(
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 | 0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 | 1 << (i - 1),
| ~~~~~~~~~~~~~
33 | value));
| ~~~~~~~
test.cpp: In function ‘int main()’:
test.cpp:23:30: error: inlining failed in call to always_inline ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’: recursive inlining
23 | static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
| ^~~~~~~~~~~~~~~~~~~~~
test.cpp:27:28: note: called from here
27 | : reflect_mask_helper_0(
| ~~~~~~~~~~~~~~~~~~~~~^
28 | i - 1,
| ~~~~~~
29 | reflect_mask_helper_1<Type>(
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 | 0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 | 1 << (i - 1),
| ~~~~~~~~~~~~~
33 | value));
| ~~~~~~~

select doesn't actually calls itself. It pops the front of the type list it received and then calls another specialization of select<Type, ...>. The trailing parameter pack is different. Since that "recursion" is essentially a finite set of nested function calls (different functions), GCC can see right through it, regardless of the run-time parameter.
But reflect_mask_helper_0 does call itself, with the same template arguments, indefinitely. GCC has no way to tell how deep this run-time recursion will go at run-time. Recall that a constexpr function is still a regular function that must be invocable at run-time.

If you check out the resulting assembly code, if you remove the always_inline and flatten attributes, you can see that gcc actually inlines everything correctly.
So, this issue is a QoI thing. Maybe, at that point, when always_inline handled, it cannot be inlined (hence the error message), but gcc decides to inline it afterwards anyways.
Btw., you can finetune gcc, and with a little modification to your code, gcc can compile it:
pass --param max-early-inliner-iterations=3 to gcc
remove the flatten attribute (no idea, why it matters...)
(So, actually, this issue has nothing to do with recursive calls - from the compiler standpoint, it doesn't matter whether the function is recursive, or not, it just follows the flow of the code - to a certain extent, of course. Here, recursive depth is just 4, it is not too hard to follow for a compiler)

Here’s the solution I’ve found, thanks to grek40’s comment and to StoryTeller’s answer.
(As for my previous problem with the unused function template instance left in the compiled binary, I solved it by compiling the original code — without the gnu::always_inline and gnu::flatten attributes — with the arguments -ffunction-sections -fdata-sections -Wl,--gc-sections.)
Now reflect_mask_helper_0 is inside a struct (because C++ doesn’t allow partial specialization of function templates), and the i parameter of the function became the Index parameter of the struct template.
#include <iostream>
#include <limits.h>
// End recursive template-expansion of function select below.
template <typename Type>
static inline constexpr Type select(unsigned index)
{ return Type(); }
// Select one of the items passed to it.
// e.g. select(0, a, b, c) = a; select(1, a, b, c) = b; etc.
template <typename Type, typename... Params>
[[gnu::always_inline]]
static inline constexpr Type select(unsigned index, Type value, Params... values)
{ return index == 0 ? value : select<Type>(index - 1, values...); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_1(Type mask, Type shift, Type value)
{ return ((value & mask) >> shift) | ((value << shift) & mask); }
template <typename Type, unsigned Index>
struct reflect_mask_helper_0
{
[[gnu::always_inline]]
static inline constexpr Type invoke(Type value)
{
return reflect_mask_helper_0<Type, Index - 1>::call(
reflect_mask_helper_1<Type>(
static_cast<Type>(select(Index - 1,
0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000)),
1 << (Index - 1),
value));
}
};
template <typename Type>
struct reflect_mask_helper_0<Type, 0>
{
[[gnu::always_inline]]
static inline constexpr Type invoke(Type value) { return value; }
};
template <typename Type>
static inline constexpr Type reflect_mask(Type value)
{ return reflect_mask_helper_0<Type, __builtin_ctz(sizeof(Type) * CHAR_BIT)>::invoke(value); }
int main(void) {
for (int i = 0; i < 65536; i++) {
std::cout << reflect_mask<uint16_t>(i) << std::endl;
}
}

Related

cuda 11 fails to compile due to type_traits in standard library when using extended lambda expression

I'm compiling a project using CUDA 11 with the feature of extended lambda expression,
I found that the following code cannot pass the compilation using NVCC (CUDA 11.8)
// test.cu
#include <iostream>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <type_traits>
template<typename Lam>
__global__ void map(int n, Lam func) {
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if (tid < n) {
func(tid);
}
}
template<typename S,typename F>
__global__ void diveq(size_t len, S* src, F f) {
size_t tid = blockIdx.x * blockDim.x + threadIdx.x;
if(tid >= len) return;
src[tid] /= f;
}
template <typename T>
struct array_t
{
T *_ptr;
size_t _len;
__host__ __device__ array_t(T *ptr, size_t len) : _ptr(ptr), _len(len) {}
template <typename F, std::enable_if_t<std::is_arithmetic_v<F>, int> = 0>
__host__ array_t& operator/=(F f)
{
T *src = _ptr;
size_t grid_size, block_size = 512;
grid_size = (_len + block_size - 1) / block_size;
auto ker = [=] __device__(int eid) { src[eid] /= f; };
map<<<grid_size, block_size>>>(_len, ker);
cudaDeviceSynchronize();
return (*this);
}
};
void testMain(void)
{
float *pfloats;
cudaMalloc(&pfloats, sizeof(float) * 10000);
array_t<float> farr(pfloats, 10000);
farr /= 2.f;
cudaFree(pfloats);
}
I run the command nvcc --std=c++17 --extended-lambda --compile ./test.cu -o test.o and it gives me error :
./test.cu: In member function ‘array_t<T>& array_t<T>::operator/=(F)’:
./test.cu:26:102: error: ‘__T0’ was not declared in this scope; did you mean ‘__y0’?
26 | auto ker = [=] __device__(int eid) { src[eid] /= f; };
| ^
| __y0
./test.cu: In instantiation of ‘array_t<T>& array_t<T>::operator/=(F) [with F = float; int <anonymous> = 0; T = float]’:
./test.cu:38:17: required from here
./test.cu:26:12: error: could not convert ‘&((array_t<float>*)this)->*operator/=<<template arguments error> >’ from ‘<unresolved overloaded function type>’ to ‘array_t<float>& (array_t<float>::*)(float)’
26 | auto ker = [=] __device__(int eid) { src[eid] /= f; };
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./test.cu:26:12: error: could not convert ‘&((array_t<float>*)this)->*operator/=<<template arguments error> >’ from ‘<unresolved overloaded function type>’ to ‘array_t<float>& (array_t<float>::*)(float)’
But if I substitute std::enable_if_t<std::is_arithmetic_v<F>, int> = 0 with int K = 0, Or, if I replace the lambda expression and map function with a template __global__ function call diveq<<<grid_size,block_size>>>(_len, src, f), it passes the compilation,. Why does it happen? The error message makes no sense to me :(
I thought it was a compiler bug, but it wasn't.
There are restrictions on the usage of extended lambda expression. According to https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#extended-lambda-restrictions Point 9:
If the enclosing function is an instantiation of a function template or a member function template, and/or the function is a member of a class template, the template(s) must satisfy the following constraints:
The template must have at most one variadic parameter, and it must be listed last in the template parameter list.
The template parameters must be named. : )
The template instantiation argument types cannot involve types that are >either local to a function (except for closure types for extended lambdas), or are private or protected class members.
So the solution is adding a dummy name to the second parameter : std::enable_if_t<std::is_arithmetic_v<F>, int> dummy = 0

"no matching function for call to" when having a function pointer with template arguments as a template argument

I'm writing a template wrapper function that can be applied to a functions with different number/types of arguments.
I have some code that works but I'm trying to change more arguments into template parameters.
The working code:
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS>
int wrapper(int (*func)(ARGS...), ARGS... args) { return (*func)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool>(func0, b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
Now I want int (*func)(ARGS...) to also be a template parameter. (It's for performance reasons. I want the pointer to be backed into the wrapper, because the way I'm using it prevents the compiler from optimizing it out.)
Here is what I came up with (The only difference is I've changed the one argument into a template parameter.):
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS, int (*FUNC)(ARGS...)>
int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
This doesn't compile. It shows:
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
ASM generation compiler returned: 1
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
Execution build compiler returned: 1
(link to the compiler explorer)
It looks like a problem with the compiler to me, but GCC and Clang agree on it so maybe it isn't.
Anyway, how can I make this template compile correctly with templated pointer to a function?
EDIT:
Addressing the duplicate flag Compilation issue with instantiating function template
I think the core of the problem in that question is the same as in mine, however, it lacks a solution that allows passing the pointer to function (not only its type) as a template parameter.
This doesn't work because a pack parameter (the one including ...) consumes all remaining arguments. All arguments following it can't be specified explicitly and must be deduced.
Normally you write such wrappers like this:
template <typename F, typename ...P>
int wrapper(F &&func, P &&... params)
{
return std::forward<F>(func)(std::forward<P>(params)...) * 10;
}
(And if the function is called more than once inside of the wrapper, all calls except the last can't use std::forward.)
This will pass the function by reference, which should be exactly the same as using a function pointer, but I have no reasons to believe that it would stop the compiler from optimizing it.
You can force the function to be encoded in the template argument by passing std::integral_constant<decltype(&func0), func0>{} instead of func0, but again, I don't think it's going to change anything.
The 2nd snippet is not valid because:
a type parameter pack cannot be expanded in its own parameter clause.
As from [temp.param]/17:
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.
So consider the following invalid example:
template<typename... Ts, Ts... vals> struct mytuple {}; //invalid
The above example is invalid because the template type parameter pack Ts cannot be expanded in its own parameter list.
For the same reason, your code example is invalid. For example, a simplified version of your 2nd snippet doesn't compile in msvc.

How to define a contructor for <...auto...>

With the following program, which is an extract from something larger that I'm experimenting with, I get an error message that seems to be related to the constructor for fixed_string
#include <string>
#include <cstring>
template<std::size_t N>
struct fixed_string {
static const constexpr std::size_t size__ = N;
constexpr fixed_string(char const* s) :
buf("") {
for (std::size_t i = 0; i <= N; ++i)
buf[i] = s[i];
}
constexpr operator char const*() const {
return buf;
}
constexpr bool operator==(const char* other) const {
return ::strncmp(buf, other, N) == 0;
}
template<std::size_t M>
constexpr bool compare(const fixed_string<M>& other) const {
return (N == M && ::strncmp(buf, other.buf, N) == 0) ? std::true_type(): std::false_type();
}
char buf[N + 1];
};
template<std::size_t N>
fixed_string(char const (&)[N]) -> fixed_string<N - 1>;
////////////////////////////////////////////
template<fixed_string TARGET_NAME, fixed_string THIS_NAME>
concept NameMatches = (TARGET_NAME.compare(THIS_NAME));
template<fixed_string NAME, typename TYPE>
class Member {
public:
static const constexpr fixed_string name__ { NAME };
public:
template<fixed_string TARGET_NAME>
const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
return member_;
}
protected:
TYPE member_;
};
template<typename ... MEMBERS>
class Container: public MEMBERS... {
};
The error messages are:
../src/test-concepts.cpp:43:35: error: class template argument deduction failed:
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:43:35: error: no matching function for call to ‘fixed_string(fixed_string<...auto...>)’
../src/test-concepts.cpp:8:12: note: candidate: ‘template<long unsigned int N> fixed_string(const char*)-> fixed_string<N>’
8 | constexpr fixed_string(char const* s) :
| ^~~~~~~~~~~~
../src/test-concepts.cpp:8:12: note: template argument deduction/substitution failed:
../src/test-concepts.cpp:43:35: note: couldn’t deduce template parameter ‘N’
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:5:8: note: candidate: ‘template<long unsigned int N> fixed_string(fixed_string<N>)-> fixed_string<N>’
5 | struct fixed_string {
| ^~~~~~~~~~~~
../src/test-concepts.cpp:5:8: note: template argument deduction/substitution failed:
../src/test-concepts.cpp:43:35: note: mismatched types ‘fixed_string<N>’ and ‘fixed_string<...auto...>’
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:28:1: note: candidate: ‘template<long unsigned int N> fixed_string(const char (&)[N])-> fixed_string<(N - 1)>’
28 | fixed_string(char const (&)[N]) -> fixed_string<N - 1>;
| ^~~~~~~~~~~~
../src/test-concepts.cpp:28:1: note: template argument deduction/substitution failed:
../src/test-concepts.cpp:43:35: note: mismatched types ‘const char [N]’ and ‘fixed_string<...auto...>’
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:43: confused by earlier errors, bailing out
This is part of a project using templates that overloads a given function (get in this case), for which there is only one suitable candidate that fulfils the constraint. The value that the constraint operates on is a name - a string literal - not a const string variable, which is passed as a non-type parameter to an instantiation of the template: something like:
Container<Member<"fred", std::string>, Member<"bert", int>, Member<"alfie", bool>> some_values;
I want to be able to retrieve a value using something like
int result = some_values.get<"bert">();
I have had difficulty finding much information about the "<...auto...>" specialisation of the template. I presume this is an internal representation used by gcc for constant, non-type values.
The error messages point me to the lack of a suitable overloaded constructor for fixed_string. What should this be?
The problem I see is that fixed_string is a template class, not a class.
So, when you define a concept as
template<fixed_string TARGET_NAME, fixed_string THIS_NAME>
concept NameMatches = (TARGET_NAME.compare(THIS_NAME));
you have that fixed_string TARGET_NAME (fixed_string THIS_NAME also) doesn't works because fixed_string isn't a type. I mean: fixed_string<5> is a type, not fixed_string.
I know that you have a deduction guide that, given the literal string, deduce the template parameter for the fixed_string, but remain the problem that the concept should works with elements of different types (fixed_string of different lengths).
I suppose you can solve the problem with auto
template <auto TARGET_NAME, auto THIS_NAME>
concept NameMatches = (TARGET_NAME.compare(THIS_NAME));
but, when you declare Member, you have the same problem: fixed_string NAME doesn't works because fixed_sting (without a length) isn't a type.
template<fixed_string NAME, typename TYPE>
class Member
Unfortunately, if you use auto
template <auto NAME, typename TYPE>
class Member
defining a Member with a literal string argument (Member<"bert", int>, by example), nothing bring "bert" to a fixed_string.
Suggestion: what about a fixed_string without a template argument?

Trying to pass a constexpr lambda and use it to explicitly specify returning type

I would like to use a function and pass a constexpr lambda. However, it only compiles successfully if I let the type be deduced through auto. Explicitly giving the type through -> std::array<event, l()> seems to fail (the first instance). Why is this?
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) -> std::array<event, l()> {
return {};
} // error
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
return std::array<event, (l())>{};
} // OK
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) -> decltype(l()) { return {}; }
// OK
Note that, the lambda returns a size_t.
gcc errors on this without a call (clang accepts it):
prog.cc:9:63: error: template argument 2 is invalid
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:42: error: invalid template-id
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^~~
prog.cc:9:61: error: use of parameter outside function body before '(' token
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^
prog.cc:9:23: error: deduced class type 'array' in function return type
9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
| ^~~
In file included from prog.cc:4:
/opt/wandbox/gcc-head/include/c++/9.0.1/array:94:12: note:
'template<class _Tp, long unsigned int _Nm> struct std::array' declared here
94 | struct array
| ^~~~~
prog.cc: In function 'int main()':
prog.cc:14:5: error: 'foo' was not declared in this scope
14 | foo([]() {return 3; });
| ^~~
Parameters to constexpr functions are not themselves constexpr objects - so you cannot use them in constant expressions. Both of your examples returning arrays are ill-formed because there is no valid call to them.
To understand why, consider this nonsense example:
struct Z { int i; constexpr int operator()() const { return i; }; };
template <int V> struct X { };
template <typename F> constexpr auto foo(F f) -> X<f()> { return {}; }
constexpr auto a = foo(Z{2});
constexpr auto b = foo(Z{3});
Z has a constexpr call operator, and this is well-formed:
constexpr auto c = Z{3}();
static_assert(c == 3);
But if the earlier usage were allowed, we'd have two calls to foo<Z> that would have to return different types. This could only fly if the actual value f were the template parameter.
Note that clang compiling the declaration is not, in of itself, a compiler error. This is a class of situations that are ill-formed, no diagnostic required.

C++ compile-time bitmask addition

I have a number of bitmasks to add (layer, logical OR |), but as they are constants I would like to do so at compile time. Entering advanced template territory...
I tried recursion:
template <uint8_t mask, uint8_t...masks>
struct MaskAdd {
static const uint8_t value = masks | MaskAdd<masks>::value;
};
template <uint8_t mask>
struct MaskAdd {
static const uint8_t value = mask;
};
which gave the following errors:
file.cpp:3:55: error: parameter packs not expanded with ‘...’:
static const uint8_t value = masks | MaskAdd<masks>::value;
^
file.cpp:3:55: note: ‘masks’
file.cpp:7:8: error: redeclared with 1 template parameter
struct MaskAdd {
^
file.cpp:2:8: note: previous declaration ‘template<unsigned char mask, unsigned char ...masks> struct MaskAdd’ used 2 template parameters
struct MaskAdd {
^
I also tried this strange syntax, given by (presumably) a misunderstanding of the cppreference page on parameter packs:
template <uint8_t...masks>
struct MaskAdd {
static const uint8_t value = (masks | ...);
};
which threw these errors:
file.cpp:3:43: error: expected primary-expression before ‘...’ token
static const uint8_t value = (masks | ...);
^
file.cpp:3:43: error: expected ‘)’ before ‘...’ token
I've got a feeling the solution is somewhere in the template<template< region of hell, if anyone can explain those I'd be grateful.
You have typo(s) in this expression:
masks | MaskAdd<masks>::value
It should be:
mask | MaskAdd<masks...>::value
// ^ no 's' ^ the expansion compiler was talking about
Then it will complain about the redeclaration of the class, so provide a specialization instead (for a single parameter):
template <uint8_t mask>
struct MaskAdd<mask> { .. };
This is my aproach for a compile time mask...
First templated parameter is the position of the bit counting from right, the second is the number of bits set as 1 towards left.
template <unsigned START, unsigned RANGE>
struct Mask
{
static const size_t val = 1 << START | Mask<START + 1, RANGE - 1>::val;
};
template <unsigned START>
struct Mask<START, 0>
{
static const size_t val = 0;
};
If I want to create a mask with, for example, number 14 (0000 1110):
unsigned mask = Mask<1, 3>::val;