Reputation: 3995
I'm currently trying to implement a function, which accepts some data
and a parameter-pack ...args
. Inside I call another function, which recursively iterates the given arguments.
Sadly I'm having some issues to compile it. Apparently the compiler keeps trying to compile the recursive function, but not the overload to stop the recursion.
Does anyone have an idea what the issue is ?
class Sample
{
public:
template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
static bool ParseCompositeFieldsXXX(const std::vector<std::string> &data, TArgs &&...args)
{
auto field = std::get<0>(std::forward_as_tuple(std::forward<TArgs>(args)...));
//bool ok = ParseField(field, 0, data);
auto x = data[0];
bool ok = true;
if (TotalSize > 1)
return ok && ParseCompositeFields<1>(data, std::forward<TArgs>(args)...);
return ok;
}
private:
template<std::size_t Index, class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
static bool ParseCompositeFields(const std::vector<std::string> &data, TArgs &&...args)
{
auto field = std::get<Index>(std::forward_as_tuple(std::forward<TArgs>(args)...));
//bool ok = ParseField(field, Index, data);
auto x = data[Index];
bool ok = true;
if (Index < TotalSize)
return ok && ParseCompositeFields<Index + 1>(data, std::forward<TArgs>(args)...);
return ok;
}
template<std::size_t Index>
static bool ParseCompositeFields(const std::vector<std::string> &data)
{
volatile int a = 1 * 2 + 3;
}
};
int wmain(int, wchar_t**)
{
short x1 = 0;
std::string x2;
long long x3 = 0;
Sample::ParseCompositeFieldsXXX({ "1", "Sxx", "-5,32" }, x1, x2, x3);
return 0;
}
\utility(446): error C2338: tuple index out of bounds
...
\main.cpp(56): note: see reference to class template instantiation 'std::tuple_element<3,std::tuple>' being compiled
Upvotes: 3
Views: 1134
Reputation: 2208
You seem to be using rather old technique here. Simple expansion is what you're searching for:
#include <cstddef>
#include <utility>
#include <tuple>
#include <vector>
#include <string>
class Sample
{
template <std::size_t index, typename T>
static bool parse_field(T&& field, const std::vector<std::string>& data)
{
return true;
}
template <typename Tuple, std::size_t ... sequence>
static bool parse_impl(Tuple&& tup, const std::vector<std::string>& data, std::index_sequence<sequence...>)
{
using expander = bool[];
expander expansion{parse_field<sequence>(std::get<sequence>(tup), data)...};
bool result = true;
for (auto iter = std::begin(expansion); iter != std::end(expansion); ++iter)
{
result = result && *iter;
}
return result;
}
public:
template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
static bool ParseCompositeFieldsXXX(const std::vector<std::string> &data, TArgs &&...args)
{
return parse_impl(std::forward_as_tuple(std::forward<TArgs>(args)...),
data, std::make_index_sequence<sizeof...(TArgs)>{});
}
};
int main()
{
short x1 = 0;
std::string x2;
long long x3 = 0;
Sample::ParseCompositeFieldsXXX({ "1", "Sxx", "-5,32" }, x1, x2, x3);
return 0;
}
If you're looking at something like array, then it is array. Don't use recursion unless required, as it usually makes it complicated. Of course there are exceptions though.
As you can see, one doesn't even need a class here. Just remove it.
One problem might arise if the order of invocation matters. IIRC, before C++17 this doesn't have strong evaluation order, so it might fail you sometimes.
Upvotes: 2
Reputation: 7601
Does anyone have an idea what the issue is ?
The crucial point are the lines:
if (Index < TotalSize)
return ok && ParseCompositeFields<Index + 1>(data, std::forward<TArgs>(args)...);
First of all, to be logically correct, the condition should read Index < TotalSize - 1
., as tuple element counts are zero-based.
Furthermore, even if Index == TotalSize - 1
, the compiler is still forced to instantiate ParseCompositeFields<Index + 1>
(as it has to compile the if-branch), which effectively is ParseCompositeFields<TotalSize>
. This however will lead to the error your got when trying to instantiate std::get<TotalSize>
.
So in order to conditionally compile the if-branch only when the condition is fulfilled, you would have to use if constexpr(Index < TotalSize - 1)
(see on godbolt). For C++14, you have to fall back on using template specializations and function objects:
class Sample
{
template<std::size_t Index, bool>
struct Helper {
template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
static bool ParseCompositeFields(const std::vector<std::string> &data, TArgs &&...args)
{
auto field = std::get<Index>(std::forward_as_tuple(std::forward<TArgs>(args)...));
//bool ok = ParseField(field, Index, data);
auto x = data[Index];
bool ok = true;
return ok && Helper<Index + 1, (Index < TotalSize - 1)>::ParseCompositeFields(data, std::forward<TArgs>(args)...);
}
};
template<std::size_t Index>
struct Helper<Index, false> {
template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
static bool ParseCompositeFields(const std::vector<std::string> &data, TArgs &&...args) {
volatile int a = 1 * 2 + 3;
return true;
}
};
public:
template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
static bool ParseCompositeFieldsXXX(const std::vector<std::string> &data, TArgs &&...args)
{
auto field = std::get<0>(std::forward_as_tuple(std::forward<TArgs>(args)...));
//bool ok = ParseField(field, 0, data);
auto x = data[0];
bool ok = true;
return ok && Helper<1, (TotalSize > 1)>::ParseCompositeFields(data, std::forward<TArgs>(args)...);
}
};
Upvotes: 2