Extract C++ template parameters
It's not possible in general to pick arbitrary template parameters.
However, the usual way you do it is this:
template<int N>
struct foo {
static const int value = N;
};
and for types
template<typename T>
struct foo {
typedef T type;
};
You can access it then as foo<39>::value
or foo<int>::type
.
If you have a particular type, you can use partial template specialization:
template<typename>
struct steal_it;
template<std::size_t N>
struct steal_it< std::bitset<N> > {
static const std::size_t value = N;
};
The same principle is possible for type parameters too, indeed. Now you can pass any bitset to it, like steal_it< std::bitset<16> >::value
(note to use size_t, not int!). Because we have no variadic many template paramters yet, we have to limit ourself to a particular parameter count, and repeat the steal_it template specializations for count from 1 up to N. Another difficulty is to scan types that have mixed parameters (types and non-types parameters). This is probably nontrivial to solve.
If you have not the type, but only an object of it, you can use a trick, to still get a value at compile time:
template<typename T>
char (& getN(T const &) )[steal_it<T>::value];
int main() {
std::bitset<16> b;
sizeof getN(b); // assuming you don't know the type, you can use the object
}
The trick is to make the function template auto-deduce the type, and then return a reference to a character array. The function doesn't need to be defined, the only thing needed is its type.
How to extract the types passed in template parameters?
What about a template taking a template?
With the following line you can declare a template taking a template and have the latter's template arguments types named:
template<
template<typename, typename> class D,
typename A, typename B>
With this notation you are saying that D is a is a template parameter which is itself in turn a template and A
and B
are the types that it takes.
Now you can refer to the first and second template parameter of D with the names A
and B
.
using namespace std;
template<class A, class B>
class C{
public :
A a;
B b;
};
template<template<typename, typename> class T, typename A, typename B>
class M{
public :
B f; //!!!!!!!!!!!!!!!!
void show(){cout<<f;}
};
int main(){
M<C, int, float> m;
m.show();
}
For more info check this answer out.
Extract first template parameter from a template template parameter and using it inside the class?
You can't name the template parameter of a template template parameter directly. Even though it looks like you can name it, as you noticed that name can't actually be used.
However, you can use explicit specializations for a type that takes a single template parameter.
template<typename... Ts>
struct B; // incomplete type, only needed for specialization
template<template <typename> typename U, typename InnerType>
struct B<U<InnerType>> // specialized to be able to use InnerType
{
InnerType foo() { return InnerType{}; }
};
Here's a demo.
You could of course do something similar for templates with more than one parameter by adding specializations for those cases as well.
C++ - Extract template parameter from derived class
You can add a type
in A
:
template<typename T>
class A {
protected:
using type = T;
...
};
Then you can use typename DerivedClass::type
in C
.
extracting a template parameter pack with different types into a vector of doubles produces warnings
Yes, you can cast the arguments easily:
std::vector<double> args_vect = {static_cast<double>(args)...};
and there are no warnings emitted.
Here's a demo.
As pointed out in the comments by @NathanOliver, if you just want to print all the variadic arguments, you could do:
void process(Args... args)
{
((std::cout << args << " "), ...);
std::cout << std::endl;
};
Then you don't have to worry about conversions of any kind.
Here's a demo.
You can also use sizeof...
to figure out the number of arguments passed in, and use that information to dispatch to functions that take a fixed number of parameters:
void process1arg(double ) {}
void process2arg(double , double ) {}
void process3arg(double , double , double ) {}
void process(Args... args)
{
((std::cout << args << " "), ...);
std::cout << std::endl;
if constexpr (sizeof...(args) == 1)
process1arg(args...);
if constexpr (sizeof...(args) == 2)
process2arg(args...);
if constexpr (sizeof...(args) == 3)
process3arg(args...);
};
Note that you need if constexpr
and not regular if
, since otherwise the code will not compile when the number of arguments don't match.
Here's a demo.
Get number of template parameters with template template function
There is one...
° Introduction
Like @Yakk pointed out in his comment to my other answer (without saying it explicitly), it is not possible to 'count' the number of parameters declared by a template. It is, on the other hand, possible to 'count' the number of arguments passed to an instantiated template.
Like my other answer shows it, it is rather easy to count these arguments.
So...
If one cannot count parameters...
How would it be possible to instantiate a template without knowing the number of arguments this template is suppose to receive ???
Note
If you wonder why the wordinstantiate(d)has been stricken throughout this post,
you'll find its explanation in the footnote. So keep reading... ;)
° Searching Process
- If one can manage somehow to try to
instantiatea template with an increasing number of arguments, and then, detect when it fails using SFINAE (Substitution Failure Is Not An Error), it should be possible to find a generic solution to this problem... Don't you think ? - Obviously, if one wants to be able to also manage non-type parameters, it's dead.
- But for templates having only
typename
parameters...
There is one...
Here are the elements with which one should be able to make it possible:
A template class declared with only
typename
parameters can receive any type as argument. Indeed, although there can have specializations defined for specific types,
a primary template cannot enforce the type of its arguments.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- The above statement might no longer be true from C++20 concepts.
I cannot try ATM, but @Yakk seems rather confident on the subject. After his comment:
I think concepts breaks this. "a primary template cannot enforce the type of its arguments." is false.
- He might be right if constraints are apply before the template instantiation. But...
- By doing a quick jump to the introduction to Constraints and concepts, one can read, after the first code example:
"Violations of constraints are detected at compile time, early in the template instantiation process, which leads to easy to follow error messages."
- To be confirmed...
- The above statement might no longer be true from C++20 concepts.
It is perfectly possible to create a template having for sole purpose to
be instantiated with any number of arguments. For our use case here, it might contain onlyint
s... (let's call itIPack
).It is possible to define a member template of
IPack
to define the NextIPack
by adding anint
to the arguments of the currentIPack
. So that one can progressively increase its number of arguments...Here is maybe the missing piece. It is maybe something that most people don't realize.
- (I mean, most of us uses it a lot with templates when, for example, the template accesses a member that one of its arguments must have, or when a trait tests for the existence of a specific overload, etc...)
But I think it might help in finding solutions sometimes to view it differently and say:
- It is possible to declare an arbitrary type, built by assembling other types, for which the evaluation by the compiler can be delayed until it is effectively used.
- Thus, it will be possible to inject the arguments of an
IPack
into another template...
Lastly, one should be able to detect if the operation succeeded with a testing trait making use of
decltype
andstd::declval
. (note: In the end, none of both have been used)
° Building Blocks
Step 1: IPack
template<typename...Ts>
struct IPack {
private:
template<typename U> struct Add1 {};
template<typename...Us> struct Add1<IPack<Us...>> { using Type = IPack<Us..., int>; };
public:
using Next = typename Add1<IPack<Ts...>>::Type;
static constexpr std::size_t Size = sizeof...(Ts);
};
using IPack0 = IPack<>;
using IPack1 = typename IPack0::Next;
using IPack2 = typename IPack1::Next;
using IPack3 = typename IPack2::Next;
constexpr std::size_t tp3Size = IPack3::Size; // 3
Now, one has a means to increase the number of arguments,
with a convenient way to retrieve the size of the IPack
.
Next, one needs something to build an arbitrary type
by injecting the arguments of the IPack
into another template.
Step 2: IPackInjector
An example on how the arguments of a template can be injected into another template.
It uses a template specialization to extract the arguments of anIPack
,
and then, inject them into theTarget
.
template<typename P, template <typename...> class Target>
struct IPackInjector { using Type = void; };
template<typename...Ts, template <typename...> class Target>
struct IPackInjector<IPack<Ts...>, Target> { using Type = Target<Ts...>; };
template<typename T, typename U>
struct Victim;
template<typename P, template <typename...> class Target>
using IPInj = IPackInjector<P, Target>;
//using V1 = typename IPInj<IPack1, Victim>::Type; // error: "Too few arguments"
using V2 = typename IPInj<IPack2, Victim>::Type; // Victim<int, int>
//using V3 = typename IPInj<IPack3, Victim>::Type; // error: "Too many arguments"
Now, one has a means to inject the arguments of an IPack
into a Victim
template, but, as one can see, evaluating Type
directly generates an error if the number of arguments does not
match the declaration of the Victim
template...
Note
Have you noticed that theVictim
template is not fully defined ?
It is not a complete type. It's only a forward declaration of a template.
- The template to be tested will not need to be a complete type
for this solution to work as expected... ;)
If one wants to be able to pass this arbitrary built type to some detection trait one will have to find a way to delay its evaluation.
It turns out that the 'trick' (if one could say) is rather simple.
It is related to dependant names. You know this annoying rule
that enforces you to add ::template
everytime you access a member template
of a template... In fact, this rule also enforces the compiler not to
evaluate an expression containing dependant names until it is
effectively used...
- Oh I see ! ...
So, one only needs to prepare theIPackInjector
s without
accessing itsType
member, and then, pass it to our test trait, right ?
It could be done using something like that:
using TPI1 = IPackInjector<IPack1, Victim>; // No error
using TPI2 = IPackInjector<IPack2, Victim>; // No error
using TPI3 = IPackInjector<IPack3, Victim>; // No error
Indeed, the above example does not generate errors, and it confirms
that there is a means to prepare the types to be built and evaluate
them at later time.
Unfortunately, it won't be possible to pass these pre-configured
type builders to our test trait because one wants to use SFINAE
to detect if the arbitrary type can be instantiated or not.
And this is, once again, related to dependent name...
The SFINAE rule can be exploited to make the compiler silently
select another template (or overload) only if the substitution
of a parameter in a template is a dependant name.
In clear: Only for a parameter of the current template instantiation.
Hence, for the detection to work properly without generating
errors, the arbitrary type used for the test will have to be
built within the test trait with, at least, one of its parameters.
The result of the test will be assigned to the Success
member...
Step 3: TypeTestor
template<typename T, template <typename...> class C>
struct TypeTestor {};
template<typename...Ts, template <typename...> class C>
struct TypeTestor<IPack<Ts...>, C>
{
private:
template<template <typename...> class D, typename V = D<Ts...>>
static constexpr bool Test(int) { return true; }
template<template <typename...> class D>
static constexpr bool Test(...) { return false; }
public:
static constexpr bool Success = Test<C>(42);
};
Now, and finally, one needs a machinery that will successively try
to instantiate our Victim
template with an increasing number of arguments. There are a few things to pay attention to:
- A template cannot be declared with no parameters, but it can:
- Have only a parameter pack, or,
- Have all its parameters defaulted.
- If the test procedure begins by a failure, it means that the template must take more arguments. So, the testing must continue until a success, and then, continue until the first failure.
- I first thought that it might make the iteration algorithm using template specializations a bit complicated... But after having thought a little about it, it turns out that the start conditions are not relevant.
- One only needs to detect when the last test was
true
and next test will befalse
.
- There must have a limit to the number of tests.
- Indeed, a template can have a parameter pack, and such a template can receive an undetermined number of arguments...
Step 4: TemplateArity
template<template <typename...> class C, std::size_t Limit = 32>
struct TemplateArity
{
private:
template<typename P> using TST = TypeTestor<P, C>;
template<std::size_t I, typename P, bool Last, bool Next>
struct CheckNext {
using PN = typename P::Next;
static constexpr std::size_t Count = CheckNext<I - 1, PN, TST<P>::Success, TST<PN>::Success>::Count;
};
template<typename P, bool Last, bool Next>
struct CheckNext<0, P, Last, Next> { static constexpr std::size_t Count = Limit; };
template<std::size_t I, typename P>
struct CheckNext<I, P, true, false> { static constexpr std::size_t Count = (P::Size - 1); };
public:
static constexpr std::size_t Max = Limit;
static constexpr std::size_t Value = CheckNext<Max, IPack<>, false, false>::Count;
};
template<typename T = int, typename U = short, typename V = long>
struct Defaulted;
template<typename T, typename...Ts>
struct ParamPack;
constexpr std::size_t size1 = TemplateArity<Victim>::Value; // 2
constexpr std::size_t size2 = TemplateArity<Defaulted>::Value; // 3
constexpr std::size_t size3 = TemplateArity<ParamPack>::Value; // 32 -> TemplateArity<ParamPack>::Max;
° Conclusion
In the end, the algorithm to solve the problem is not that much complicated...
After having found the 'tools' with which it would be possible to do it, it only was a matter, as very often, of putting the right pieces at the right places... :P
Enjoy !
° Important Footnote
Here is the reason why the word
intantiate(d)has been stricken at the places where it was used in relation to theVictim
template.
The word instantiate(d) is simply not the right word...
It would have been better to use try to declare, or to alias the type of a future instantiation of the Victim
template.
(which would have been extremely boring) :P
Indeed, none of the Victim
templates gets ever instantiated within the code of this solution...
As a proof, it should be enough to see that all tests, made in the code above, are made only on forward declarations of templates.
And if you're still in doubt...
using A = Victim<int>; // error: "Too few arguments"
using B = Victim<int, int>; // No errors
template struct Victim<int, int>;
// ^^^^^^^^^^^^^^^^
// Warning: "Explicit instantiation has no definition"
In the end, there's a full sentence of the introduction which might be stricken, because this solution seems to demonstrate that:
- It is possible to 'count' the number of parameters declared by a template...
- Without instantiation of this template.
Can I extract C++ template arguments out of a template class?
Why don't you simply change your holder class to
template< class Value > class Holder {
typedef Value value_type;
value_type m_val; // member variable
};
In any method that consumes an object of type Holder< T > you can access the contained type like that:
template< class THolder >
void SomeMethod( THolder const& holder ) {
typename THolder::value_type v = holder.m_val;
}
This approach follows the pattern all STL classes use, e.g., std::vector< int >::value_type is int.
I think you're trying to do partial template specialization:
template<class T>
class GetValue {
};
template<class Value>
class GetValue< Holder<Value> > {
public:
typedef Value value_type;
};
In your code, you could then do the following:
template<class THolder>
void SomeMethod( THolder const& h ) {
typename GetValue< THolder >::value_type v = h.m_v;
}
In general, I'd prefer the first solution though.
Extract template template parameter and variadic template parameter from class template
You might use those aliases to be allowed to retrieve template parameters:
template <template <typename... Args> class Container,
typename... Args>
struct container_type_holder
{
template <typename ... Ts>
using container = Container<Ts...>;
constexpr std::size arg_count = sizeof...(Args);
using args_as_tuple = std::tuple<Args...>;
template <std::size_t I>
using get_arg = typename std::tuple_element<I, std::tuple<Args...>::type;
// And possibly helpers, such as
template <template <typename ...> OtherContainer>
using template_rebind = container_type_holder<OtherContainer, Args...>;
};
and then, usage might be:
using c1 = container_type_holder<std::map, std::string, int>;
using c2 = c1::template_rebind<std::unordered_map>;
using c3 = container_type_holder<std::vector, std::pair<c1::get_arg<0>, c1::get_arg<1>>>;
Related Topics
How to Store a Value at a Specific Location in the Memory
Why Is the New Operator Allowed to Return *Void to Every Pointer-Type
What Are the Reasons That Extending the Std Namespace Is Considered Undefined Behavior
Boost.Python: Wrap Functions to Release the Gil
Spurious Warning About Binding Temporary to Reference Member in Constructor
How to Read from Memory Just Like from a File Using iOStream
Stl Algorithms: Why No Additional Interface for Containers (Additional to Iterator Pairs)
Get Private Data Members for Non Intrusive Boost Serialization C++
If Constexpr - Why Is Discarded Statement Fully Checked
Disable Sleep Mode in Windows Mobile 6
Debug Assertion Failed! Expression: _Block_Type_Is_Valid
Are Data Members Allocated in the Same Memory Space as Their Objects in C++