In C++, I'm trying to write something similar to boost-mp11's mp_for_each. However, whilst mp_for_each always calls the supplied function for every T in the given mp_list<Ts...>, I'm trying to come up with a solution that stops traversal once a run-time call to the function yields a value evaluating to false in an if-statement.
See the implementation of mp_for_each and a usage example:
Implementation on GitHub
Usage example in Boost reference manual
Apparently, the implementation of mp_for_each manages to pass the function argument as a constant expression, thus enabling the user to apply it where a constant expression is required. Whilst I took a different approach incorporating template tail recursion, I expected the function argument to be passed as a constant expression as welll. However, GCC complains that it "is not a constant expression".
My code looks like this:
#include <cstdlib>
#include <iostream>
#include <typeinfo>
#include <utility>
#include <boost/mp11.hpp>
template<std::size_t T_counter>
struct WhileGreaterZero
{
template<typename T_Function>
constexpr WhileGreaterZero(T_Function&& function)
{
if (function(T_counter)) // pass function argument
WhileGreaterZero<T_counter - 1>(std::forward<T_Function>(function));
}
};
template<>
struct WhileGreaterZero<0>
{
template<typename T_Function>
constexpr WhileGreaterZero(T_Function&&) {}
};
int main()
{
using boost::mp11::mp_at_c;
using boost::mp11::mp_list;
using boost::mp11::mp_size;
using Types = mp_list<bool, int, double>;
WhileGreaterZero<mp_size<Types>::value - 1>(
[](auto counter) { // function parameter
using Type = mp_at_c<Types, counter>;
if (typeid(Type) == typeid(int))
return false;
return true;
}
);
}
When compiling with g++ 7.4.0, the following error is encountered (formatted to my taste):
$ g++ -std=c++17 -I/path/to/boost
wgz.cpp:
In substitution of ‘
template<
class L,
long unsigned int I
>
using mp_at_c =
typename boost::mp11::detail::mp_if_c_impl<
(I < typename boost::mp11::detail::mp_size_impl<L>::type:: value),
boost::mp11::detail::mp_at_c_impl<L, I>,
void
>::type::type
[
with L = boost::mp11::mp_list<bool, int, double>;
long unsigned int I = counter
]
’:
wgz.cpp:42:49:
required from ‘
main()::<lambda(auto:1)>
[with auto:1 = long unsigned int]
’
wgz.cpp:14:21:
required from ‘
constexpr WhileGreaterZero<T_counter>::WhileGreaterZero(T_Function&&)
[
with T_Function = main()::<lambda(auto:1)>;
long unsigned int T_counter = 2
]
’
wgz.cpp:49:5:
required from here
wgz.cpp:42:49:
error: ‘counter’ is not a constant expression
using Type = mp_at_c<Types, counter>;
^
wgz:42:49:
note: in template argument for type ‘long unsigned int’
Why is counter not considered as a constant expression in my code?
What's the crucial difference between mp11's code and mine in this regard?
Change
function(T_counter)
to
function(std::integral_constant< std::size_t, T_counter >{})
within function the argument is not a compile time value. But an integral_constant that isn't a compile time value can be cast to an integer, and that integer is a compile time constant, because it doesn't depend on this.
A related trick is:
template<std::size_t...Is>
constexpr auto indexes( std::index_sequence<Is...> ={} ) {
return std::make_tuple( std::integral_constant<std::size_t, Is>{}... );
}
then you can do:
template<std::size_t N, class F>
void While( F&& f ) {
std::apply( [&](auto...Is) {
(f( Is ) && ...);
}, indexes( std::make_index_sequence<N>{} ) );
}
Live example, no recursion.
Parameter of lambda is a parameter of function, its value is not passed at compile-time.
At very least this line is ill-formed:
using Type = mp_at_c<Types, counter>;
You have to wait for template lambdas or implement your own functor
Related
I've observed I cannot do &decltype(c)::f to get a pointer to member function f using a class instance c, but I can do &C::f to get that pointer to member function, using the class type C, which I believe is the same as decltype(c).
See this minimal example:
struct C{
int f()
{
return 5;
}
} c;
template<typename T, T t, typename S, S*s> void callCf()
{
(s->*t)();
}
int main()
{
callCf<decltype(&decltype(c)::f),&decltype(c)::f,C,&c>();
}
Compiling this gives:
In function 'int main()':
15:3: error: parse error in template argument list
15:58: error: no matching function for call to 'callCf()'
15:58: note: candidate is:
8:49: note: template<class T, T t, class S, S* s> void callCf()
8:49: note: template argument deduction/substitution failed:
15:58: error: template argument 2 is invalid
Using the following works as expected:
int main()
{
callCf<decltype(&decltype(c)::f),&C::f,C,&c>();
}
Even this works:
int main()
{
using tC = decltype(c);
callCf<decltype(&decltype(c)::f),&tC::f,C,&c>();
}
This also works:
template<typename T>
struct forward_type{
typedef T type;
};
int main()
{
callCf<decltype(&decltype(c)::f),&forward_type<decltype(c)>::type::f,C,&c>();
}
My question is: why is it not possible to use decltype to obtain a pointer to member function like this &decltype(c)::f?
Edit: #Paul Sanders has shown in the comments that the minimal example works in c++17. I'm still using c++14. Does c++17 include some changes to the language that allow my minimal example to compile?
c is an lvalue, so decltype(c) will not return the C type itself, which is why decltype(c)::f does not work. decltype(c) will actually return a C& reference type instead:
If the argument is any other expression of type T, and
...
b) if the value category of expression is lvalue, then decltype yields T&;
...
You can use std::remove_reference/_t to get the C type from C&, eg:
int main()
{
callCf<
decltype(&std::remove_reference_t<decltype(c)>::f),
&std::remove_reference_t<decltype(c)>::f,
std::remove_reference_t<decltype(c)>,
&c
>();
}
Live Demo
Which can then be simplified with a using statement:
int main()
{
using tC = std::remove_reference_t<decltype(c)>;
callCf<decltype(&tC::f), &tC::f, tC, &c>();
}
Live Demo
Constexpr function that returns std::array<std:tuple<uint32_t, uint32_t, uint32_t>, size_t> does not work at compile time due to the use of std::make_tuple. Is there any way to overcome this?
When I tried to remove constexpr specifiction. It works correctly. However, the goal of our project is to provide such function evaluation at compile time.
I got the following error:
At calling part:
error: call to non-constexpr function ‘std::tuple<_Elements>& std::tuple<_Elements>::operator=(std::tuple<_Elements>&&) [with _Elements = {unsigned int, unsigned int, unsigned int}]’
At function part:
error: ‘constexpr std::array<std::tuple<unsigned int, unsigned int, unsigned int>, SIZE> GenArrayTuple() [with long unsigned int SIZE = 128]’ called in a constant expression
The code is below.
template<std::size_t SIZE>
constexpr std::array<std::tuple<uint32_t, uint32_t, uint32_t>, SIZE>
GenArrayTuple() {
std::array<std::tuple<uint32_t, uint32_t, uint32_t>, SIZE> array;
for (uint32_t i = 0; i < SIZE; ++i) {
// FIXME constexpr
arr[2*i] = std::make_tuple(i, i * 2, i * 3 + 1);
}
return array;
}
constexpr uint32_t n = 128;
constexpr auto array_tuple = GenArrayTuple<n>();
There's actually no issue with using std::make_tuple in a constant expression in C++14 or later, since C++14 changed it to be constexpr. So it's a valid constant expression, as long as any class constructors used to initialize the tuple's elements evaluate as valid constant expressions (and there are no such constructors when your element types are all scalars like std::uint32_t).
But take a better look at the error message. The function it complains about is (taking out some details) tuple& tuple::operator=(tuple&&). It turns out the assignment operators of std::tuple are not marked constexpr in current C++ versions, meaning any assignment of a tuple object is not a valid constant expression. (cppreference.com notes that they will be marked constexpr in C++20; this generally reflects changes from a proposal already accepted by the appropriate C++ working group.)
So to work around this, you'll need to initialize the array all at once, rather than assigning its elements in a loop. Probably the easiest way to do this is with the help of std::make_integer_sequence:
#include <tuple>
#include <array>
#include <cstdint>
#include <utility>
template <std::uint32_t ... I>
constexpr std::array<std::tuple<std::uint32_t, std::uint32_t, std::uint32_t>,
sizeof...(I)>
GenArrayTuple_helper(std::integer_sequence<std::uint32_t, I...>) {
return { std::make_tuple(I, I * 2, I * 3 + 1) ... };
}
template <std::size_t SIZE>
constexpr std::array<std::tuple<std::uint32_t, std::uint32_t, std::uint32_t>,
SIZE>
GenArrayTuple() {
return GenArrayTuple_helper(std::make_integer_sequence<std::uint32_t, SIZE>{});
}
How can I access members of variant using v.index() then std::get<index>(v)?
Useful when the variant has multiple entries of the same type.
The following does not work. This code doesn't compile on either GCC or clang
#include <iostream>
#include <variant>
#include <string>
#include <sstream>
typedef std::variant<int, int, std::string> foo;
std::string bar(const foo f) {
const std::size_t fi = f.index();
auto ff = std::get<fi>(f);
std::ostringstream ss;
ss << "Index:" << fi << " Value: " << ff;
return ss.str();
}
int main()
{
foo f( 0 );
std::cout << bar(f);
}
There are many versions of std::get of course, so the error messages are lengthy.
gcc complains (for every version of get<>)
prog.cc:10:29: error: the value of 'fi' is not usable in a constant expression
auto ff = std::get<fi>(f);
^
prog.cc:9:23: note: 'fi' was not initialized with a constant expression
const std::size_t fi = f.index();
^~
prog.cc:10:29: note: in template argument for type 'long unsigned int'
auto ff = std::get<fi>(f);
Clang complains (for every version of get<>)
(re _Tp or _Ip as the case may be)
candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
Wandbox
UPDATED to ask how to solve rather than what does the error message mean.
std::get<> is applicable when requesting a variant index that is known at compile time.
If you need to act on a variant value whose type isn't known until runtime, the idiomatic approach is to use a visitor with std::visit.
#include <iostream>
#include <variant>
#include <string>
struct output_visitor
{
template< typename T >
void operator() ( const T& value ) const
{
std::cout << value;
}
};
int main()
{
std::variant<int, std::string> f( 0 );
std::visit( output_visitor{}, f );
}
This can often be implemented with C++14 "generic lambdas"
#include <iostream>
#include <variant>
#include <string>
int main()
{
std::variant<int, std::string> f( 0 );
std::visit( [](auto v){std::cout << v;} , f );
}
gcc 8.1's error output also includes the explanation:
<source>:10:29: error: the value of 'fi' is not usable in a constant expression
auto ff = std::get<fi>(f);
^
<source>:9:23: note: 'fi' was not initialized with a constant expression
const std::size_t fi = f.index();
Integer template arguments have to be constant expressions. f is not a constant expression, therefore a call to its non-static member function is not a constant expression, therefore fi is not.
You can get a better error message with:
constexpr std::size_t fi = f.index();
The code get<fi>(f) could only work if f were also declared to be constexpr ; but that is only possible if all the types in the variant have trivial destructors, which std::string does not.
I'm trying to use a template class (here Foo), with a basic type like:
hana::tuple<hana::pair<hana::type<int>, Runtime>> with Runtime a class which obiviously can't be constepxr.
But the type can be construct in several way that's why I use:
hana::tuple<hana::pair<hana::type<int>, hana::type<Runtime>>> to do the work at compile time.
So the question is basically how convert from the first tuple type to the second one. I wonder if there is something in hana that could help me. Or even better, some tips on that kind of "conversion".
namespace hana = boost::hana;
using namespace hana::literals;
struct Runtime { std::vector<int> data; };
template < typename T >
struct Foo {
T data;
};
constexpr decltype(auto) convertMap(auto storageMap) {
return hana::make_type(hana::transform(
storageMap,
[] (auto pair) {
return hana::make_pair(
hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type {});
}));
}
int main() {
constexpr auto map = hana::make_tuple(
hana::make_pair(hana::type_c<int>, hana::type_c<Runtime>)
);
constexpr auto result = convertMap(map);
static_assert(result ==
hana::type_c<hana::tuple<hana::pair<hana::type<int>, Runtime>>>);
Foo<typename decltype(result)::type> test;
}
As you can see I tried some c++1z convertMap with hana::transform and lambdas, but the second tuple can't be constexpr, so I can't pass it to hana::make_type hoping to get a hana::type_c.
test.cpp: In function ‘int main()’:
test.cpp:70:41: error: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ called in a constant expression
constexpr auto result = convertMap(map);
^
test.cpp:53:27: note: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ is not usable as a constexpr function because:
constexpr decltype(auto) convertMap(auto storageMap) {
^~~~~~~~~~
test.cpp:53:27: error: temporary of non-literal type ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ in a constant expression
In file included from /usr/include/boost/hana/detail/struct_macros.hpp:29:0,
from /usr/include/boost/hana/adapt_adt.hpp:15,
from lib/hana/include/boost/hana.hpp:59,
from test.cpp:1:
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ is not literal because:
struct tuple
^~~~~
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ has a non-trivial destructor
All you care about is a type - you can therefore hide the computation of the type in a non-constexpr implementation function, and afterwards call it with decltype(...){} to "force constexpr":
template <typename T>
decltype(auto) convertMapImpl(T storageMap)
{
return hana::make_type(hana::transform(storageMap, [](auto pair) {
return hana::make_pair(hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type{});
}));
}
template <typename T>
constexpr decltype(auto) convertMap(T storageMap)
{
return decltype(convertMapImpl(storageMap)){};
}
live wandbox example
Also note that using auto in a function signature is a gcc extension - you should use a template parameter instead to be standard-compliant.
I wrote some code that retrieves the types of the non-auto parameters when given a generic lambda function. As you can see in the code below, the idea is to call the connect function with a generic lambda and provide arguments for the auto parameters (which will always be at the front in my use case). So in the code below my goal was to detect that the second parameter is of type float.
The code works fine with clang 3.8 but it doesn't compile with gcc 6.1.1, so I was wondering whether this was a bug in gcc or if this is just not valid c++ code? Can I assume that a generic lambda is implemented with a templated operator() function or is this compiler-specific?
template <typename Functor, typename... AllArgs, typename... ProvidedArgs>
void findArgTypes(void(Functor::*)(AllArgs...) const, Functor, ProvidedArgs...)
{
// AllArgs == int, float
// ProvidedArgs == int
}
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
}
int main()
{
int tmp = 0;
connect([&](auto, float){ ++tmp; }, 0);
}
The error that gcc gives is this:
main.cpp: In instantiation of ‘void connect(Func, ProvidedArgs ...) [with Func = main()::<lambda(auto:1, float)>; ProvidedArgs = {int}]’:
main.cpp:16:33: required from here
main.cpp:11:17: error: no matches converting function ‘operator()’ to type ‘void (struct main()::<lambda(auto:1, float)>::*)() const’
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:27: note: candidate is: template<class auto:1> main()::<lambda(auto:1, float)>
connect([](auto, float){}, 0);
^
Removing the const in findArgTypes gives the same result.
Using the following code works with both compilers:
struct Foo
{
template <typename T>
void operator()(T, float) const {}
};
int main()
{
Foo f;
connect(f, 0);
}
You have error because you are expecting functor (object) but lambda with empty capture is convertible to free function:
int main() {
using function = void (*)(int, float);
function a = [](auto, float){};
}
See lambda from cppreference:
For the newest version of your question that implementation satisfies both compilers:
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
auto mf = &Func::template operator()<ProvidedArgs...>;
findArgTypes(mf, func, providedArgs...);
}
I think this is gcc compiler bug that gcc needs this auto local variable to work correctly...
BTW, one question - one bug in clang, one in gcc - I really advice you to find simpler way to achieve your goals - maybe consider to just use std::function instead of quite fresh generic-lambda?