Is there a way to force this ambiguity away? - c++

I'd like to supply two forms of a GetLength(psz) style function - one that doesn't know an upper bounds, and one that does:
template <typename T>
size_t GetLength(const T * psz) { /* compute size w/o knowing what upper bound may be */ }
template <typename T, size_t size>
size_t GetLength(const T(&psz)[size]) { /* we know the upper bound */ }
I'd like that this not be ambiguous. I want the array-sized version to be chosen when the argument is an array of known size. I want the unbounded version chosen when the argument is just a pointer, not a known fixed array.
I'd also offer a 3rd version which explicitly takes the upper bounds as an argument, without templated size deduction, for passing that info in from an outer context which has otherwise lost the ability to deduce that from its local arguments.
Is there a technique I can use to force the compiler to discount the 1st version of my function (no known bounds) when the bounds is known?

Is there a technique I can use to force the compiler to discount the 1st version of my function (no known bounds) when the bounds is known?
What about adding a level of indirection?
template <typename T>
std::size_t GetLength (const T * psz, int)
{ /* compute size w/o knowing what upper bound may be */ }
template <typename T, size_t size>
std::size_t GetLength (const T(&psz)[size], long)
{ /* we know the upper bound */ }
template <typename T>
std::size_t GetLength (T const & t)
{ GetLength(t, 0L); }
Adding an unused different parameter (int or long) you can select the preferred version.

We could use type traits:
#include <type_traits>
// If T is an array
template<
typename T,
typename std::enable_if <
std::is_array<T>{},
size_t
> ::type Extent = std::extent<T>::value
>
size_t GetLength(const T& t)
{
return Extent;
}
// If T is not an array
template<typename T,
typename std::enable_if <
!std::is_array<T>{},
size_t
> ::type = 0
>
size_t GetLength(const T& t)
{
return {};
}
int main()
{
int arr[5]{};
GetLength(arr); // calls first
//decay to pointer
auto val = arr;
GetLength(val); // calls second
}

If you have access to a recent version of boost, you can use the incredibly powerful HOF library (stands for higher order functions).
One of the functions I use most to simplify code path selection based on argument type is the function first_of.
The way this works is that you give it a list of template function objects (or lambdas) in the order you want the compiler to try them. The first legal function object in the list is selected.
example:
#include <cstddef>
#include <boost/hof.hpp>
#include <cstring>
#include <utility>
#include <iostream>
// a function to compute length from a pointer. For exposition,
// I have only considered char pointers but any number of overloads will work.
template<class T>
std::size_t
string_pointer_length(T*p)
{
// for exposition
return std::strlen(p);
}
// a function to compute string length from a literal
template<class T, std::size_t N>
constexpr
std::size_t literal_string_length(T (&s)[N])
{
return N - 1;
}
// The generic GetLength function which takes any kind of string
template <typename T>
std::size_t GetLength(T&& str)
{
// select the FIRST legal choice of the following lambdas and invoke...
return boost::hof::first_of(
[](auto&&s) BOOST_HOF_RETURNS(literal_string_length(s)),
[](auto&&s) BOOST_HOF_RETURNS(string_pointer_length(s))
)(str);
}
int main()
{
static const auto lit = "hello";
auto plit = std::addressof(lit[0]);
auto n = GetLength(lit);
auto n2 = GetLength(plit);
std::cout << n << ", " << n2 << std::endl;
}
The macro BOOST_HOF_RETURNS saves us having to spell out the lambdas like this:
return boost::hof::first_of(
[](auto&&s) -> decltype(literal_string_length(s)) { return literal_string_length(s); },
[](auto&&s) BOOST_HOF_RETURNS(string_pointer_length(s))
)(str);
If you're not able to use boost.hof, writing our own replacement is surprisingly trivial:
#include <cstddef>
#include <cstring>
#include <tuple>
#include <utility>
#include <iostream>
template<class T>
std::size_t
string_pointer_length(T*p)
{
// for exposition
return std::strlen(p);
}
template<class T, std::size_t N>
constexpr
std::size_t literal_string_length(T (&s)[N])
{
return N - 1;
}
template<class...Args, class This, class...Others>
constexpr auto try_these(std::tuple<Args...> args, This _this, Others...others)
{
if constexpr (std::is_invocable_v<This, Args...>)
{
return std::apply(_this, args);
}
else
{
return try_these(args, others...);
}
}
struct invoke_string_pointer_length
{
template<class S>
constexpr auto operator()(S&& s) const -> decltype(string_pointer_length(s))
{ return string_pointer_length(s); }
};
struct invoke_literal_string_length
{
template<class S>
constexpr auto operator()(S&& s) const -> decltype(literal_string_length(s))
{ return literal_string_length(s); }
};
template <typename T>
std::size_t GetLength(T&& str)
{
return try_these(std::forward_as_tuple(std::forward<T>(str)),
invoke_literal_string_length(),
invoke_string_pointer_length());
}
int main()
{
static const auto lit = "hello";
auto plit = std::addressof(lit[0]);
auto n = GetLength(lit);
auto n2 = GetLength(plit);
std::cout << n << ", " << n2 << std::endl;
}

Related

How to return multi- dimensional-vector of given dimension and type?

Is it possible in modern c++ to write a function that, given a number n and a type, return a n-dimensional vector type.
1,float -> return std::vector<float>
2,float -> return std::vector<std::vector<float>>
and so on...
Here is a compile time approach using template specialization. We apply std::vector<> recursively until we reach the specialization for N == 0, where the final type is set to T:
#include <type_traits>
#include <vector>
template<std::size_t N, typename T>
struct recursive_vector {
using type = std::vector<typename recursive_vector<N - 1, T>::type>;
};
template<typename T>
struct recursive_vector<0, T> {
using type = T;
};
template<std::size_t N, typename T>
using vec = recursive_vector<N, T>::type;
so vec<3, float> becomes vector<vector<vector<float>>>:
int main() {
vec<3, float> v;
using std::vector;
static_assert(std::is_same_v<decltype(v), vector<vector<vector<float>>>>);
}
Try out the code, here.
Is it possible in modern C++ to write a function that given a number n and a type return a n-dimensional vector type?
Compile time
If n is known at compile time, the job easy, which is already given in the other answer. Alternatively, you could also do:
#include <vector>
template<typename T, std::size_t N>
struct vec : public vec<std::vector<T>, N - 1u> {};
template<typename T> struct vec<T, 0u> { using type = T; };
template<typename T, std::size_t N> using vec_t = typename vec<T, N>::type;
Now vec_t<float, 5u> means std::vector<std::vector<std::vector<std::vector<std::vector<float>>>>>
(See a live demo in godbolt.org)
Run time
When n is run time depended, one possible solution is to wrap the possible multidimensional vectors types to std::variant (Since c++17).
Following is an example code.
#include <vector>
#include <variant> // std::variant
// type aliases for the multi-dim vectors
template<typename T> using Vec1D = std::vector<T>;
template<typename T> using Vec2D = Vec1D<Vec1D<T>>;
template<typename T> using Vec3D = Vec1D<Vec2D<T>>;
// ... so on (hope that you wouldn't need more than 4 or 5!)
template<typename T>
using VecVariants = std::variant<Vec1D<T>, Vec2D<T>, Vec3D<T>/*, so on ...*/>;
template<typename T> auto getVector_of(const std::size_t n)
{
if (n == 1) return VecVariants<T>{Vec1D<T>{}};
if (n == 2) return VecVariants<T>{Vec2D<T>{}};
if (n == 3) return VecVariants<T>{Vec3D<T>{}};
// ... so on
return VecVariants<T>{Vec3D<T>{}}; // some default return!
}
template<typename T> void doSomething(const VecVariants<T>& vec)
{
std::visit([](auto&& arg) {
using Type = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<Type, Vec1D<T>>)
std::cout << "... do something with Vec1D\n";
else if constexpr (std::is_same_v<Type, Vec2D<T>>)
std::cout << "... do something with Vec2D\n";
else if constexpr (std::is_same_v<Type, Vec3D<T>>)
std::cout << "... do something with Vec3D\n";
// .... so on
else
std::cout << "... default!\n";
}, vec);
}
Now you can pass run time n
int main()
{
std::size_t n{ 5 };
while (n)
doSomething(getVector_of<float>(n--)); // n must be 0u < n <= MAX_n you defined!
}
(See a live demo in godbolt.org)

why for-loop isn't a compile time expression and extended constexpr allows for-loop in a constexpr function

I wrote code like this
#include <iostream>
using namespace std;
constexpr int getsum(int to){
int s = 0;
for(int i = 0; i < to; i++){
s += i;
}
return s;
}
int main() {
constexpr int s = getsum(10);
cout << s << endl;
return 0;
}
I understand that it works because of extended constexpr. However in this question why-isnt-a-for-loop-a-compile-time-expression, the author gave his code as follow:
#include <iostream>
#include <tuple>
#include <utility>
constexpr auto multiple_return_values()
{
return std::make_tuple(3, 3.14, "pi");
}
template <typename T>
constexpr void foo(T t)
{
for (auto i = 0u; i < std::tuple_size<T>::value; ++i)
{
std::get<i>(t);
}
}
int main()
{
constexpr auto ret = multiple_return_values();
foo(ret);
}
It could not compile by GCC5.1, however, after replacing std::get<i>(t); with something without a specified template parameter his code did work.
After reading answers under this question I found their main point is constexpr for creates trouble for the compiler so for-loop is used at run-time.
So it makes me confused, on one hand, for-loop is at run-time, on the other, the loop is in a constexpr function, so it must be calculated at compile time, so there seems to be a contradiction. I just wonder where did I make a mistake.
Your​ function need to be valid both at runtime and at compile time. So your Variant i can be seen as a runtime variable. If the function is executed at compile time, the variable i is part of the compiler's runtime. So a int in a constexpr function is under the same rules as a int in a non-constexpr function.
What you can do is to create your​ own constexpr for loop:
template<typename F, std::size_t... S>
constexpr void static_for(F&& function, std::index_sequence<S...>) {
int unpack[] = {0,
void(function(std::integral_constant<std::size_t, S>{})), 0)...
};
(void) unpack;
}
template<std::size_t iterations, typename F>
constexpr void static_for(F&& function) {
static_for(std::forward<F>(function), std::make_index_sequence<iterations>());
}
Then, you can use your static_for like this:
static_for<std::tuple_size<T>::value>([&](auto index) {
std::get<index>(t);
});
Note that lambda function cannot be used in constexpr function until C++17, so you can instead roll your own functor:
template<typename T>
struct Iteration {
T& tuple;
constexpr Iteration(T& t) : tuple{t} {}
template<typename I>
constexpr void operator() (I index) const {
std::get<index>(tuple);
}
};
And now you can use static_for like that:
static_for<std::tuple_size<T>::value>(Iteration{t});
This really has nothing to do with whether or not the constexpr function needs to be able to be runnable at compile-time (tuple_size<T>::value is a constant expression regardless of whether the T comes from a constexpr object or not).
std::get<> is a function template, that requires an integral constant expression to be called. In this loop:
for (auto i = 0u; i < std::tuple_size<T>::value; ++i)
{
std::get<i>(t);
}
i is not an integral constant expression. It's not even constant. i changes throughout the loop and assumes every value from 0 to tuple_size<T>::value. While it kind of looks like it, this isn't calling a function with different values of i - this is calling different functions every time. There is no support in the language at the moment for this sort of iteration†, and this is substantively different from the original example, where we're just summing ints.
That is, in one case, we're looping over i and invoking f(i), and in other, we're looping over i and invoking f<i>(). The second one has more preconditions on it than the first one.
†If such language support is ever added, probably in the form of a constexpr for statement, that support would be independent from constexpr functions anyway.
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

Parameter pack expansion for static variables

I am thinking about following problem:
Let us have a merging function for merge arrays defined in following way:
// input is (const void*, size_t, const void*, size_t,...)
template<typename...ARGS>
MyArray Concatenation(ARGS...args)
And let us have couple of structs with static properties
struct A { static void* DATA; static size_t SIZE; };
struct B { static void* DATA; static size_t SIZE; };
struct C { static void* DATA; static size_t SIZE; };
I would like to have a method:
template<typename...ARGS>
MyArray AutoConcatenation();
Where ARGS should be structs with mentioned static interface.
Following methods should have the same output:
AutoConcatenation<A, B, C>();
Concatenation(A::DATA, A::SIZE, B::DATA, B::SIZE, C::DATA, C::SIZE);
My question is how to implement parameter pack expansion.
I tried:
// not working
template<typename...ARGS>
MyArray AutoConcatenation()
{
return Concatenation((ARGS::DATA, ARGS::SIZE)...);
}
What about expansions
ARGS::DATA... // Correct expansion of pointers
ARGS::SIZE... // Correct expansion of sizes
(ARGS::DATA, ARGS::SIZE)... // Seems to be expansion of sizes
Just info for advisors. I am looking for implementation of AutoConcatenation method, not for its redeclaration nor for redeclaration previous code, thank you.
A lazy solution using std::tuple:
make a tuple of DATA and SIZE for each element of the parameter pack,
flatten the list of tuples to one big tuple using std::tuple_cat,
apply the resulting tuple's elements to Concatenation by expanding a list of indexes in an std::index_sequence.
In the following code, the test harness is longer than the actual solution:
#include <cstddef>
#include <tuple>
#include <utility>
#include <iostream>
#include <typeinfo>
#include <type_traits>
struct MyArray { };
template<class... ARGS> MyArray Concatenation(ARGS... args)
{
// Just some dummy code for testing.
using arr = int[];
(void)arr{(std::cout << typeid(args).name() << ' ' << args << '\n' , 0)...};
return {};
}
struct A { static void* DATA; static std::size_t SIZE; };
struct B { static void* DATA; static std::size_t SIZE; };
struct C { static void* DATA; static std::size_t SIZE; };
// Also needed for testing.
void* A::DATA;
std::size_t A::SIZE;
void* B::DATA;
std::size_t B::SIZE;
void* C::DATA;
std::size_t C::SIZE;
// The useful stuff starts here.
template<class T, std::size_t... Is> MyArray concat_hlp_2(const T& tup, std::index_sequence<Is...>)
{
return Concatenation(std::get<Is>(tup)...);
}
template<class T> MyArray concat_hlp_1(const T& tup)
{
return concat_hlp_2(tup, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template<class... ARGS> MyArray AutoConcatenation()
{
return concat_hlp_1(std::tuple_cat(std::make_tuple(ARGS::DATA, ARGS::SIZE)...));
}
int main()
{
AutoConcatenation<A, B, C>();
}
If you want to avoid std::tuple and std::tuple_cat (which can be heavy in terms of compile times), here's an alternative using indexes into arrays.
The testing code stays the same, this is just the juicy stuff:
template<std::size_t Size> const void* select(std::false_type, std::size_t idx,
const void* (& arr_data)[Size], std::size_t (&)[Size])
{
return arr_data[idx];
}
template<std::size_t Size> std::size_t select(std::true_type, std::size_t idx,
const void* (&)[Size], std::size_t (& arr_size)[Size])
{
return arr_size[idx];
}
template<std::size_t... Is> MyArray concat_hlp(std::index_sequence<Is...>,
const void* (&& arr_data)[sizeof...(Is) / 2], std::size_t (&& arr_size)[sizeof...(Is) / 2])
{
return Concatenation(select(std::bool_constant<Is % 2>{}, Is / 2, arr_data, arr_size)...);
}
template<class... ARGS> MyArray AutoConcatenation()
{
return concat_hlp(std::make_index_sequence<sizeof...(ARGS) * 2>{}, {ARGS::DATA...}, {ARGS::SIZE...});
}
Again a sequence of indexes twice the size of the original parameter pack, but we build separate arrays of DATA and SIZE and then use tag dispatching to select elements from one or the other depending on the parity of the current index.
This may not look as nice as the previous code, but it doesn't involve any template recursion (std::make_index_sequence is implemented using compiler intrinsics in modern compilers as far as I know) and cuts down on the number of template instantiations, so it should be faster to compile.
The select helper can be made constexpr by using arrays of pointers to the static members, but this turns out to be unnecessary in practice. I've tested MSVC 2015 U2, Clang 3.8.0 and GCC 6.1.0 and they all optimize this to a direct call to Concatenation (just like for the tuple-based solution).
I think the following is more elegant, and it illustrates the common recursive unpacking pattern. Finally, it does not perform any voodoo with memory layouts and tries to be idiomatic as far as C++ generic programming.
#include <iostream>
#include <string>
using namespace std;
// Handle zero arguments.
template <typename T = string>
T concat_helper() { return T(); }
// Handle one pair.
template <typename T = string>
T concat_helper(const T &first, size_t flen) { return first; }
// Handle two or more pairs. Demonstrates the recursive unpacking pattern
// (very common with variadic arguments).
template <typename T = string, typename ...ARGS>
T concat_helper(const T &first, size_t flen,
const T &second, size_t slen,
ARGS ... rest) {
// Your concatenation code goes here. We're assuming we're
// working with std::string, or anything that has method length() and
// substr(), with obvious behavior, and supports the + operator.
T concatenated = first.substr(0, flen) + second.substr(0, slen);
return concat_helper<T>(concatenated, concatenated.length(), rest...);
}
template <typename T, typename ...ARGS>
T Concatenate(ARGS...args) { return concat_helper<T>(args...); }
template <typename T>
struct pack {
T data;
size_t dlen;
};
template <typename T>
T AutoConcatenate_helper() { return T(); }
template <typename T>
T AutoConcatenate_helper(const pack<T> *packet) {
return packet->data;
}
template <typename T, typename ...ARGS>
T AutoConcatenate_helper(const pack<T> *first, const pack<T> *second,
ARGS...rest) {
T concatenated = Concatenate<T>(first->data, first->dlen,
second->data, second->dlen);
pack<T> newPack;
newPack.data = concatenated;
newPack.dlen = concatenated.length();
return AutoConcatenate_helper<T>(&newPack, rest...);
}
template <typename T, typename ...ARGS>
T AutoConcatenate(ARGS...args) {
return AutoConcatenate_helper<T>(args...);
}
int main() {
pack<string> first;
pack<string> second;
pack<string> third;
pack<string> last;
first.data = "Hello";
first.dlen = first.data.length();
second.data = ", ";
second.dlen = second.data.length();
third.data = "World";
third.dlen = third.data.length();
last.data = "!";
last.dlen = last.data.length();
cout << AutoConcatenate<string>(&first, &second, &third, &last) << endl;
return 0;
}
We're neither changing the declaration of Concatenate<>(), nor that of AutoConcatenate<>(), as required. We're free to implement AutoConcatenate<>(), as we did, and we assume that there is some implementation of Concatenate<>() (we provided a simple one for a working example).
Here is possible solution:
enum Delimiters { Delimiter };
const void* findData(size_t count) { return nullptr; }
template<typename...ARGS>
const void* findData(size_t count, size_t, ARGS...args)
{
return findData(count, args...);
}
template<typename...ARGS>
const void* findData(size_t count, const void* data, ARGS...args)
{
return count ? findData(count - 1, args...) : data;
}
template<typename...ARGS>
MyArray reordered(size_t count, Delimiters, ARGS...args)
{
return Concatenate(args...);
}
template<typename...ARGS>
MyArray reordered(size_t count, const void* size, ARGS...args)
{
return reordered(count, args...);
}
template<typename...ARGS>
MyArray reordered(size_t count, size_t size, ARGS...args)
{
return reordered(count + 1, args..., findData(count, args...), size);
}
template<typename...ARGS>
MyArray AutoConcatenate()
{
return reordered(0, ARGS::LAYOUT_SIZE..., ARGS::LAYOUT..., Delimiter);
}
If you know more elegant way, please let me know.
EDIT
One little more elegant way is to keep function argument count as template parameter...

Get custom sizeof of std::function's variadic template argument types

I have a variadic template function that accepts std::function with variadic types. I want to find total sizeof of those types that std::function has, except that I want to treat double and float types as special, where sizeof(double) == 100 and sizeof(float) == 50.
Here is pseudo-code (doesn't compile)
#include <iostream>
#include <functional>
// terminating case for T=double
template<>
size_t getSize<double>()
{
return 100;
}
// terminating case for T=float
template<>
size_t getSize<float>()
{
return 50;
}
// terminating case for T being anything else
template<class T>
size_t getSize<T>()
{
return sizeof(T);
}
// recursive case
template<class T, class ...S>
size_t getSize<T, ...S>()
{
return getSize<T>() + getSize<S>();
}
template <class ...T>
void print_function_arg_custom_size(std::function<void(T...)> f)
{
size_t totalSize = 0;
// get size recursively
totalSize = getSize<T>();
std::cout << "totalSize: " << totalSize << std::endl;
}
void foo(uint8_t a, uint16_t b, uint32_t c, double d)
{
// noop
}
int main()
{
std::function<void(uint8_t, uint16_t, uint32_t, double)> f = foo;
print_function_arg_custom_size<uint8_t, uint16_t, uint32_t, double>(f);
return 0;
}
One of the issues I'm having with this is that getSize doesn't like my template specialization.
Couple of errors :
template<class T>
size_t getSize<T>()
{
return sizeof(T);
}
You don't need to provide a "specialization list" since it isn't a specialization. Also, since parameter packs can be empty, the compiler can't choose between the overloads getSize<T> and getSize<T, S...>. To fix it, make these changes:
template <typename T>
size_t getSize()
{
return sizeof(T);
}
// recursive case
template<class T, class U, class ...S>
size_t getSize()
{
return getSize<T>() + getSize<U, S...>();
}
Live Demo.

In C++, is it possible to get the type of one element of a tuple when the element index is known at runtime?

typedef std::tuple< int, double > Tuple;
Tuple t;
int a = std::get<0>(t);
double b = std::get<1>(t);
for( size_t i = 0; i < std::tuple_size<Tuple>::value; i++ ) {
std::tuple_element<i,Tuple>::type v = std::get<i>(t);// will not compile because i must be known at compile time
}
I know it is possible to write code for get std::get working (see for example iterate over tuple ), is it possible to get std::tuple_element working too?
Some constraints (they can be relaxed):
no variadic templates, no Boost
C++ is a compile-time typed language. You cannot have a type that the C++ compiler cannot determine at compile-time.
You can use polymorphism of various forms to work around that. But at the end of the day, every variable must have a well-defined type. So while you can use Boost.Fusion algorithms to iterate over variables in a tuple, you cannot have a loop where each execution of the loop may use a different type than the last.
The only reason Boost.Fusion can get away with it is because it doesn't use a loop. It uses template recursion to "iterate" over each element and call your user-provided function.
If you want to do without boost, the answers to iterate over tuple already tell you everything you need to know. You have to write a compile-time for_each loop (untested).
template<class Tuple, class Func, size_t i>
void foreach(Tuple& t, Func fn) {
// i is defined at compile-time, so you can write:
std::tuple_element<i, Tuple> te = std::get<i>(t);
fn(te);
foreach<i-1>(t, fn);
}
template<class Tuple, class Func>
void foreach<0>(Tuple& t, Func fn) { // template specialization
fn(std::get<0>(t)); // no further recursion
}
and use it like that:
struct SomeFunctionObject {
void operator()( int i ) const {}
void operator()( double f ) const {}
};
foreach<std::tuple_size<Tuple>::value>(t, SomeFunctionObject());
However, if you want to iterate over members of a tuple, Boost.Fusion really is the way to go.
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>
and in your code write:
boost::for_each(t, SomeFunctionObject());
This an example for boost::tuple. There is an adapter for boost::fusion to work with the std::tuple here: http://groups.google.com/group/boost-list/browse_thread/thread/77622e41af1366af/
No, this is not possible the way you describe it. Basically, you'd have to write your code for every possible runtime-value of i and then use some dispatching-logic (e.g. switch(i)) to run the correct code based on the actual runtime-value of i.
In practice, it might be possible to generate the code for the different values of i with templates, but I am not really sure how to do this, and whether it would be practical. What you are describing sounds like a flawed design.
Here is my tuple foreach/transformation function:
#include <cstddef>
#include <tuple>
#include <type_traits>
template<size_t N>
struct tuple_foreach_impl {
template<typename T, typename C>
static inline auto call(T&& t, C&& c)
-> decltype(::std::tuple_cat(
tuple_foreach_impl<N-1>::call(
::std::forward<T>(t), ::std::forward<C>(c)
),
::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
))
{
return ::std::tuple_cat(
tuple_foreach_impl<N-1>::call(
::std::forward<T>(t), ::std::forward<C>(c)
),
::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
);
}
};
template<>
struct tuple_foreach_impl<0> {
template<typename T, typename C>
static inline ::std::tuple<> call(T&&, C&&) { return ::std::tuple<>(); }
};
template<typename T, typename C>
auto tuple_foreach(T&& t, C&& c)
-> decltype(tuple_foreach_impl<
::std::tuple_size<typename ::std::decay<T>::type
>::value>::call(std::forward<T>(t), ::std::forward<C>(c)))
{
return tuple_foreach_impl<
::std::tuple_size<typename ::std::decay<T>::type>::value
>::call(::std::forward<T>(t), ::std::forward<C>(c));
}
The example usage uses the following utility to allow printing tuples to ostreams:
#include <cstddef>
#include <ostream>
#include <tuple>
#include <type_traits>
template<size_t N>
struct tuple_print_impl {
template<typename S, typename T>
static inline void print(S& s, T&& t) {
tuple_print_impl<N-1>::print(s, ::std::forward<T>(t));
if (N > 1) { s << ',' << ' '; }
s << ::std::get<N-1>(::std::forward<T>(t));
}
};
template<>
struct tuple_print_impl<0> {
template<typename S, typename T>
static inline void print(S&, T&&) {}
};
template<typename S, typename T>
void tuple_print(S& s, T&& t) {
s << '(';
tuple_print_impl<
::std::tuple_size<typename ::std::decay<T>::type>::value
>::print(s, ::std::forward<T>(t));
s << ')';
}
template<typename C, typename... T>
::std::basic_ostream<C>& operator<<(
::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
tuple_print(s, t);
return s;
}
And finally, here is the example usage:
#include <iostream>
using namespace std;
struct inc {
template<typename T>
T operator()(T const& val) { return val+1; }
};
int main() {
// will print out "(7, 4.2, z)"
cout << tuple_foreach(make_tuple(6, 3.2, 'y'), inc()) << endl;
return 0;
}
Note that the callable object is constructed so that it can hold state if needed. For example, you could use the following to find the last object in the tuple that can be dynamic casted to T:
template<typename T>
struct find_by_type {
find() : result(nullptr) {}
T* result;
template<typename U>
bool operator()(U& val) {
auto tmp = dynamic_cast<T*>(&val);
auto ret = tmp != nullptr;
if (ret) { result = tmp; }
return ret;
}
};
Note that one shortcoming of this is that it requires that the callable returns a value. However, it wouldn't be that hard to rewrite it to detect whether the return type is void for a give input type, and then skip that element of the resulting tuple. Even easier, you could just remove the return value aggregation stuff altogether and simply use the foreach call as a tuple modifier.
Edit:
I just realized that the tuple writter could trivially be written using the foreach function (I have had the tuple printing code for much longer than the foreach code).
template<typename T>
struct tuple_print {
print(T& s) : _first(true), _s(&s) {}
template<typename U>
bool operator()(U const& val) {
if (_first) { _first = false; } else { (*_s) << ',' << ' '; }
(*_s) << val;
return false;
}
private:
bool _first;
T* _s;
};
template<typename C, typename... T>
::std::basic_ostream<C> & operator<<(
::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
s << '(';
tuple_foreach(t, tuple_print< ::std::basic_ostream<C>>(s));
s << ')';
return s;
}