Assume that there is a function which accepts several strings:
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
// do something
}
Now, I have a variadic template
function say foo()
as:
template<typename ...Args>
void foo () {
fun(???);
}
This method is called externally as:
foo<A, B, C, D>(); // where A, B, C, D are classes
And these classes which are passed as arguments are expected to contain a common static const
member:
static const std::string value = "...";
Here are my questions (how to):
foo()
, check if all the Args
contain value
using
static_assert
fun()
to form an initializer_list
; e.g.
fun({A::value, B::value, ...});
Searched several threads related to variadic templates and its unpacking but I am still novice in this area. Explanation in little more detail is much appreciated.
The second part is easier:
template<typename ...Args>
void foo () {
fun({Args::value...});
}
The first part is tricky, because static_assert
is a declaration, not an expression, so you'd have to expand the variadic pack within the first parameter. It may be easier just to let the call to fun
do the checking for you. Here's a sketch of how to do it with an auxiliary all
constexpr
function:
constexpr bool all() { return true; }
template<typename... Args> constexpr bool all(bool first, Args&&... rest) {
return first && all(rest...);
}
template<typename ...Args>
void foo () {
static_assert(all(std::is_convertible<decltype(Args::value),
std::string>::value...), "All Args must have a value");
fun({Args::value...});
}
As for the second question, just do it this way:
template<typename ...Args>
void foo () {
fun({Args::value...});
}
The mechanism is pretty intuitive: you create an initalizer list that contains the expanded Args::value
pattern, thus resolving (in your case) to { A::value, B::value, C::value, D::value }
.
Here is a complete program:
#include <string>
#include <iostream>
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
{
std::cout << s << " ";
}
}
template<typename ...Args>
void foo () {
fun({Args::value...});
}
struct A { static std::string value; };
struct B { static std::string value; };
struct C { static std::string value; };
struct D { static std::string value; };
std::string A::value = "Hello";
std::string B::value = "World";
std::string C::value = "of";
std::string D::value = "Variadic Templates";
int main()
{
foo<A, B, C, D>(); // where A, B, C, D are classes
}
And here is a live example.
As for the static assertion, you may write a type trait that determines whether a certain type has a member variable value
:
template<typename T, typename V = bool>
struct has_value : std::false_type { };
template<typename T>
struct has_value<T,
typename std::enable_if<
!std::is_same<decltype(std::declval<T>().value), void>::value,
bool
>::type
> : std::true_type
{
typedef decltype(std::declval<T>().value) type;
};
Then, you could use it this way:
template<typename T>
struct check_has_value
{
static_assert(has_value<T>::value, "!");
};
template<typename ...Args>
void foo () {
auto l = { (check_has_value<Args>(), 0)... };
fun({Args::value...});
}
Here is a live example of a successful check (all classes has a value
data member). Here is a live example of an unsuccessful check (class D
's data member is called values
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With