Some better approach as Guillaume Racicot have mentioned with workaround of a bit unfinished constexpr
support and std::size
in such compilers like Visual Studio 2015 Update 3
and so.
#include <tuple>
#include <stdio.h>
#include <assert.h>
// std::size is supported from C++17
template <typename T, size_t N>
constexpr size_t static_size(const T (&)[N]) noexcept
{
return N;
}
template <typename ...T>
constexpr size_t static_size(const std::tuple<T...> &)
{
return std::tuple_size<std::tuple<T...> >::value;
}
template<typename Functor>
void runtime_for_lt(Functor && function, size_t from, size_t to)
{
if (from < to) {
function(from);
runtime_for_lt(std::forward<Functor>(function), from + 1, to);
}
}
template <template <typename T_> class Functor, typename T>
void runtime_foreach(T & container)
{
runtime_for_lt(Functor<T>{ container }, 0, static_size(container));
}
template <typename Functor, typename T>
void runtime_foreach(T & container, Functor && functor)
{
runtime_for_lt(functor, 0, static_size(container));
}
template <typename T>
void static_consume(std::initializer_list<T>) {}
template<typename Functor, std::size_t... S>
constexpr void static_foreach_seq(Functor && function, std::index_sequence<S...>) {
return static_consume({ (function(std::integral_constant<std::size_t, S>{}), 0)... });
}
template<std::size_t Size, typename Functor>
constexpr void static_foreach(Functor && functor) {
return static_foreach_seq(std::forward<Functor>(functor), std::make_index_sequence<Size>());
}
Usage:
using mytuple = std::tuple<char, int, long>;
template <typename T>
struct MyTupleIterator
{
T & ref;
MyTupleIterator(T & r) : ref(r) {}
void operator() (size_t index) const
{
// still have to do with switch
assert(index < static_size(ref));
size_t value;
switch(index) {
case 0: value = std::get<0>(ref); break;
case 1: value = std::get<1>(ref); break;
case 2: value = std::get<2>(ref); break;
}
printf("%u: %u\n", unsigned(index), unsigned(value));
}
};
template <typename T>
struct MyConstexprTupleIterator
{
T & ref;
constexpr MyConstexprTupleIterator(T & r) : ref(r) {}
constexpr void operator() (size_t index) const
{
// lambda workaround for:
// * msvc2015u3: `error C3250: 'value': declaration is not allowed in 'constexpr' function body`
// * gcc 5.x: `error: uninitialized variable ‘value’ in ‘constexpr’ function`
[&]() {
// still have to do with switch
assert(index < static_size(ref));
size_t value;
switch(index) {
case 0: value = std::get<0>(ref); break;
case 1: value = std::get<1>(ref); break;
case 2: value = std::get<2>(ref); break;
}
printf("%u: %u\n", unsigned(index), unsigned(value));
}();
}
};
int main()
{
mytuple t = std::make_tuple(10, 20, 30);
runtime_foreach<MyTupleIterator>(t);
mytuple t2 = std::make_tuple(40, 50, 60);
runtime_foreach(t2, [&](size_t index) {
// still have to do with switch
assert(index < static_size(t2));
size_t value;
switch(index) {
case 0: value = std::get<0>(t2); break;
case 1: value = std::get<1>(t2); break;
case 2: value = std::get<2>(t2); break;
}
printf("%u: %u\n", unsigned(index), unsigned(value));
});
mytuple t3 = std::make_tuple(70, 80, 90);
static_foreach<std::tuple_size<decltype(t3)>::value>(MyConstexprTupleIterator<mytuple>{t3});
return 0;
}
Output:
0: 10
1: 20
2: 30
0: 40
1: 50
2: 60
0: 70
1: 80
2: 90
constexpr
doesn't mean it definitely runs at compile-time.constexpr
functions are meant to work at both compile-time and runtime. – Tacit