This is more of a conceptual question. I\'m trying to find the easiest way of converting a two-arg template (the arguments being types) into a one-arg template. I.e., binding on
Yeah, I had this issue to. It took a few iterations to figure out a decent way to do this. Basically, to do this, we need to specify a reasonable representation of what we want and need. I borrowed some aspects from std::bind()
in that I want to specify the template that I wish to bind and the parameters that I want to bind to it. Then, within that type, there should be a template that will allow you to pass a set of types.
So our interface will look like this:
template class OP, typename...Ts>
struct tbind;
Now our implementation will have those parameters plus a container of types that will be applied at the end:
template class OP, typename PARAMS, typename...Ts>
struct tbind_impl;
Our base case will give us a template type, which I'll call ttype
, that'll return a template of the contained types:
template class OP, typename...Ss>
struct tbind_impl>
{
template
using ttype = OP;
};
Then we have the case of moving the next type into the container and having ttype
refer to the ttype
in the slightly simpler base case:
template class OP, typename T, typename...Ts, typename...Ss>
struct tbind_impl, T, Ts...>
{
template
using ttype = typename tbind_impl<
OP
, std::tuple
, Ts...
>::template ttype;
};
And finally, we need a remap of the templates that will be passed to ttype
:
template class OP, size_t I, typename...Ts, typename...Ss>
struct tbind_impl, std::integral_constant, Ts...>
{
template
using ttype = typename tbind_impl<
OP
, typename std::tuple<
Ss...
, typename std::tuple_element<
I
, typename std::tuple
>::type
>
, Ts...
>::template ttype;
Now, since programmers are lazy, and don't want to type std::integral_constant
for each parameter to remap, we specify some aliases:
using t0 = std::integral_constant;
using t1 = std::integral_constant;
using t2 = std::integral_constant;
...
Oh, almost forgot the implementation of our interface:
template class OP, typename...Ts>
struct tbind : detail::tbind_impl, Ts...>
{};
Note that tbind_impl
was placed in a detail
namespace.
And voila, tbind
!
Unfortunately, there is a defect prior to c++17. If you pass tbind
to a template that expects a template with a particular number of parameters, you will get an error as the number of parameters don't match (specific number doesn't match any number). This complicates things slightly requiring an additional level of indirection. :(
template class OP, size_t N>
struct any_to_specific;
template class OP>
struct any_to_specific
{
template
using ttype = OP;
};
template class OP>
struct any_to_specific
{
template
using ttype = OP;
};
...
Using that to wrap tbind
will force the compiler to recognize the template having the specified number of parameters.
Example usage:
static_assert(!tbind::ttype::value, "failed");
static_assert( tbind::ttype::value, "failed");
static_assert(!any_to_specific<
tbind::ttype
, 1
>::ttype::value, "failed");
static_assert( any_to_specific<
tbind::ttype
, 1
>::ttype::value, "failed");
All of which succeed.