I have written the following basic Tuple template:
template
class Tuple;
template
struct TupleIndex
Do something like this:
namespace detail
{
template
R select(Tuple&& pTuple, Func pFunc)
{
return pFunc(get(std::forward(pTuple)));
}
template
R select_element(Tuple&& pTuple, std::size_t pIndex, Func pFunc)
{
if (pIndex == I)
return select(std::forward(pTuple), pFunc);
else
return select(std::forward(pTuple), pIndex, pFunc);
}
}
template
R select(Tuple&& pTuple, std::size_t pIndex, Func pFunc)
{
typedef typename std::remove_reference::type tuple_type;
// assumes all possible calls to Func return the same type
typedef typename std::tuple_element<0, tuple_type>::type dummy_type;
typedef typename std::result_of::type result_type;
if (pIndex >= std::tuple_size::value)
throw std::out_of_range("select out of range");
return detail::select<0, result_type>(
std::forward(pTuple), pIndex, pFunc);
}
This lets you call a functor with a run-time selected element, by checking each index incrementally. It returns whatever the function call returns, but it assumes that all invocations result in the same type. (Though right now, it'll "work" as long as all invocations happen to be implicitly convertible to the same type as an invocation of the first element. You can assert they all match if you want to, but that's outside the scope of this question.)
I'd be surprised if the compiler didn't unroll it, but I don't know for certain. In any case, it's simple and works (well, untested, but I assume it does) and that's far more important.
So whatever you wanted to do with your run-time selected element, operate on it with this. You can make the call templated:
struct print_element
{
// T is determined at compile time for each possible element type,
// but which overload gets selected is determined at run-time
template
void operator()(const T& pX) const
{
std::cout << pX << std::endl;
}
};
If you really just want the value as some type, then you can make a simple functor:
namespace detail
{
template
struct get_element
{
template
R operator()(T&& pValue) const
{
return std::forward(pValue);
}
};
}
template
R get(Tuple&& pTuple, std::size_t pIndex)
{
return select(std::forward(pTuple), pIndex, get_element());
}
You can use it like this:
auto x = get(myTuple, i);
To get void*
's (yuck), you need one last simple utility (too bad we don't get polymorphic lambda's):
class get_address
{
public:
template
get_address(T& pValue) :
mResult(&pValue)
{}
void* get() const
{
return mResult;
}
operator void*() const
{
return get();
}
private:
void* mResult;
};
Allowing:
void* addr = get(myTuple, i);