I'm now learning a little about templates and templates in C++11, C++14 and C++1z. I'm trying to write a variadic class template with an inside class that will associate an int to every template argument - and have a constexpr method that returns its array representation.
Let's say that I have ensured that the template cannot receive two of the same type as an argument. I was thinking about doing it somewhat like this:
template <typename... Types>
struct MyVariadicTemplate {
//we know that all types in Types... are different
template <int... Values>
struct MyInnerTemplate {
//I need to make sure that sizeof...(Values) == sizeof...(Types)
constexpr std::array<int, sizeof...(Values)> to_array() {
std::array<int, sizeof...(Values)> result = {Values...};
return result;
// this is only valid since C++14, as far as I know
}
};
};
this code should be valid (if it's not, I'd love to know why). Now, I'd like to add another inner template:
template <typedef Type>
struct AnotherInnerTemplate {};
that has a public typedef, which represents MyInnerTemplate with one on the position of Type in Types... and zeros elsewhere - and here I'm lost. I don't know how to proceed
I would appreciate any hint on how that can be done - and if I'm heading towards the wrong direction, I hope somebody can give me a hint on how to do that.
I think what you're looking for is something like this.
#include <array>
#include <cstddef>
#include <iostream>
#include <type_traits>
template <typename NeedleT, typename... HaystackTs>
constexpr auto get_type_index_mask() noexcept
{
constexpr auto N = sizeof...(HaystackTs);
return std::array<bool, N> {
(std::is_same<NeedleT, HaystackTs>::value)...
};
}
template <typename T, std::size_t N>
constexpr std::size_t ffs(const std::array<T, N>& array) noexcept
{
for (auto i = std::size_t {}; i < N; ++i)
{
if (array[i])
return i;
}
return N;
}
int
main()
{
const auto mask = get_type_index_mask<float, bool, int, float, double, char>();
for (const auto& bit : mask)
std::cout << bit;
std::cout << "\n";
std::cout << "float has index " << ffs(mask) << "\n";
}
Output:
00100
float has index 2
The magic happens in the parameter pack expansion
(std::is_same<NeedleT, HaystackTs>::value)...
where you test each type in HaystackTs against NeedleT. You might want to apply std::decay to either type if you want to consider, say, const int and int the same type.
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
Full:
template <class... Types> struct My {
template <int... Values> struct Inner {
constexpr std::array<int, sizeof...(Values)> to_array() {
return std::array<int, sizeof...(Values)>{Values...};
}
};
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
};
auto main() -> int {
My<int, float, char>::Another<int>::Type s;
auto a = s.to_array();
for (auto e : a) {
cout << e << " ";
}
cout << endl;
return 0;
}
prints:
1 0 0
Is this what you want?
Related
I've got a parameter pack saved as a tuple in some function traits struct.
How can I find out, how many of those parameters are std::optional types?
I tried to write a function to check each argument with a fold expression, but this doesn't work as I only pass a single template type which is the tuple itself.
void foo1(){}
void foo2(int,float){}
void foo3(int, std::optional<int>, float, std::optional<int>){}
void foo4(int, std::optional<int>, bool){}
template<typename R, typename... TArgs>
struct ftraits<R(TArgs...)>
{
using ret = R;
using args = std::tuple<TArgs...>;
};
template<typename T>
struct is_optional : std::false_type
{
};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type
{
};
template<typename... Ts>
constexpr auto optional_count() -> std::size_t
{
// doesn't work since Ts is a single parameter with std::tuple<...>
return (0 + ... + (is_optional<Ts>::value ? 1 : 0));
}
int main() {
using t1 = typename ftraits<decltype(foo1)>::args;
std::cout << optional_count<t1>() << std::endl; // should print 0
using t2 = typename ftraits<decltype(foo2)>::args;
std::cout << optional_count<t2>() << std::endl; // should print 0
using t3 = typename ftraits<decltype(foo3)>::args;
std::cout << optional_count<t3>() << std::endl; // should print 2
using t4 = typename ftraits<decltype(foo4)>::args;
std::cout << optional_count<t4>() << std::endl; // should print 1
}
You can use template partial specialization to get element types of the tuple and reuse the fold-expression
template<typename>
struct optional_count_impl;
template<typename... Ts>
struct optional_count_impl<std::tuple<Ts...>> {
constexpr static std::size_t count =
(0 + ... + (is_optional<Ts>::value ? 1 : 0));
};
template<typename Tuple>
constexpr auto optional_count() -> std::size_t {
return optional_count_impl<Tuple>::count;
}
Demo
You can get the size of the tuple using std::tuple_size, then iterate over all its members using a recursive template. In that template, you can pretend to construct an instance of the tuple using std::declval, get the value at the current index using std::get, and then finally get the type of that value using decltype.
Example implementation:
#include <optional>
#include <tuple>
#include <utility>
template<typename T>
struct is_optional : std::false_type {};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template<typename Tuple, size_t i>
constexpr size_t optional_count_impl() {
size_t val = is_optional<std::remove_reference_t<decltype(std::get<i>(std::declval<Tuple>()))>>::value ? 1 : 0;
if constexpr (i) {
val += optional_count_impl<Tuple, i - 1>();
}
return val;
}
template<typename Tuple>
constexpr size_t optional_count() {
const size_t tuple_size = std::tuple_size<Tuple>::value;
if constexpr (tuple_size == 0) {
return 0;
} else {
return optional_count_impl<Tuple, tuple_size - 1>();
}
}
using Tup1 = std::tuple<int, int, std::optional<size_t>, bool, std::optional<bool>, std::optional<std::optional<int>>>;
int main() {
return optional_count<Tup1>();
}
This is probably a weird use case, but I am trying to hack around the fact string literals can not be used as arguments to templates using std::array<char, N> as non template type parameter.
This works but with extreme limitation that all strings must be of same length(I could use MAX_STR_LEN=100 or whatever and make all arrays that size, but that feels ugly...).
Is there a way to make this code work so that different size std::arrays can be accepted as template parameter?
#include <iostream>
#include <array>
#include <tuple>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/tuple.hpp>
// I wish that this 6 is not fixed... but IDK how to fix it, maybe concept(IDK if concepts can be used as "types" on NTTP.
template <typename Type, std::array<char, 6> val_val>
struct TypeToValues
{
using type = Type;
static constexpr const char* val = val_val.data();
};
template <std::size_t Sz, std::size_t... Is>
constexpr std::array<char, Sz>
arrayify(const char (&arr)[Sz], std::index_sequence<Is...>)
{
return {{arr[Is]...}};
}
template <std::size_t Sz>
constexpr std::array<char, Sz> arrayify(const char (&arr)[Sz])
{
return arrayify(arr, std::make_index_sequence<Sz>());
}
struct HelloType{
};
struct YoloType{
};
int main(){
std::tuple<
TypeToValues<HelloType, arrayify("Hello")>,
TypeToValues<YoloType, arrayify("Yolo!")>> mapping;
boost::mp11::tuple_for_each(mapping, []<typename T>(const T&){
if constexpr(std::is_same_v<typename T::type, HelloType>){
std::cout << "HelloType says: " << T::val << std::endl;;
}
if constexpr(std::is_same_v<typename T::type, YoloType>){
std::cout << "YoloType says: " << T::val << std::endl;;
}
});
}
Sure, why not use a requires requires clause?
template <typename Type, auto val_val>
requires requires { { val_val.data() } -> std::same_as<char const*>; }
struct TypeToValues
{
// ...
Example.
You could also write a constraint that only specifically std::array<char, N> satisfy:
template<class> constexpr bool is_array_of_char_v = false;
template<unsigned N> constexpr bool is_array_of_char_v<std::array<char, N>> = true;
template<class T> concept ArrayOfChar = requires { is_array_of_char_v<T>; };
template <typename Type, ArrayOfChar auto val_val>
struct TypeToValues
{
// ...
But that feels excessively restrictive; you'll want to accept static string types in future.
consider the following example of a compile-time "vector".
#include <iostream>
template <int n, int...ns>
struct static_vector {
static constexpr int value = n;
static_vector<ns...> rest;
};
template <int n>
struct static_vector<n> {
static constexpr int value = n;
void* rest;
};
template <int n, class sv>
constexpr int access_nth() {
static_assert(n >= 0, "vector size out of bound");
if constexpr(n == 0) {
return sv::value;
} else {
static_assert(!std::is_same_v<decltype(sv::rest), void *>, "vector size out of bound");
return access_nth<n-1, decltype(sv::rest)>();
}
}
int main()
{
constexpr auto a = static_vector<12, 23, 34, 45>();
constexpr int nth = access_nth<5, decltype(a)>();
std::cout << nth << std::endl;
}
I am mostly satisfied with what we can do now: define a vector and then get the nth element out of it. The one thing I found that not satisfying is this: I have to use void * as a dummy in the base case (where the vector only holds one element and no tail...)
I tried to have a specialisation like this:
template <>
struct static_vector<> {
}
to represent the empty vector. But it seems that the compiler always rejects this definition with the following error:
<source>:16:8: error: too few template arguments for class template 'static_vector'
struct static_vector<> {
^
What should I do here so I can have an empty vector?
Thanks a lot.
But why recursion ?
You tagged C++17 so you can use template folding, so... what about as follows ?
#include <iostream>
template <int ... Is>
struct static_vector
{
template <std::size_t N>
int get () const
{
static_assert( N < sizeof...(Is), "index out of bound" );
std::size_t i{};
int ret;
( ... , (N == i++ ? ret = Is : 0) );
return ret;
}
};
int main()
{
constexpr auto a = static_vector<12, 23, 34, 45>();
std::cout << a.get<3u>() << std::endl;
}
Specializations must conform to the base template declaration. Since at least one int is required by the base template, this does not compile.
You can make this work by declaring the template to take any number of int arguments, then specializing every case that takes one or more arguments. The base declaration is then the empty case:
template <int...>
struct static_vector {
// Instantiated only for the no-argument case
};
template <int n>
struct static_vector<n> {
// One-argument specialization
};
template <int n, int... ns>
struct static_vector<n, ns...> {
// Two-or-more-argument specialization
};
I'm using C++17. I'd like to get an element of a tuple that satisfies some type trait. It would be amazing if the trait could be supplied generically, but I'd be satisfied with a specific function for a certain trait. Usage might look something like this:
auto my_tuple = std::make_tuple { 0.f, 1 };
auto basic = get_if_integral (my_tuple);
auto fancy = get_if<std::is_floating_point> (my_tuple);
std::cout << basic; // '1'
std::cout << fancy; // '0.f'
Ideally this would fail to compile if more than one element satisfies the trait, like std::get (std::tuple).
Here's a surprisingly simple way without using recursion:
template <template <typename...> typename T, typename... Ts>
constexpr int index_of_integral(const T<Ts...>&)
{
const bool a[] = { std::is_integral_v<Ts>... };
for (int i = 0; i < sizeof...(Ts); ++i) if (a[i]) return i;
return -1;
}
template <typename T>
constexpr decltype(auto) get_if_integral(T&& t)
{
return std::get<index_of_integral(t)>(std::forward<T>(t));
}
int main()
{
constexpr auto t = std::make_tuple(3.14, 42, "xyzzy");
static_assert(get_if_integral(t) == 42);
}
It could easily be extended to be parametrized on the trait.
The only things that make it C++17 are the is_integral_v variable template and the single-argument static_assert. Everything else is C++14.
Note that in C++20 the for loop could be replaced with std::find and std::distance.
Ideally it should throw an exception instead of returning -1, but compilers don't seem to like that.
Inspired by this answer.
If I understand correctly what you want... I propose an helper struct gf_h ("get first helper") as follows
template <std::size_t, bool ...>
struct gf_h
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, false, Bs...> : public gf_h<I+1u, Bs...>
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, true, Bs...> : public std::integral_constant<std::size_t, I>
{ };
and a couple of functions that use it:
template <typename ... Us,
std::size_t I = gf_h<0, std::is_integral<Us>::value...>::value>
auto get_first_integral (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
template <typename ... Us,
std::size_t I = gf_h<0, std::is_floating_point<Us>::value...>::value>
auto get_first_floating (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
Observe that are SFINAE enabled/disabled functions, so are enabled only if there is an integral (or float) value in the tuple
The following is a full compiling example
#include <tuple>
#include <iostream>
template <std::size_t, bool ...>
struct gf_h
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, false, Bs...> : public gf_h<I+1u, Bs...>
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, true, Bs...> : public std::integral_constant<std::size_t, I>
{ };
template <typename ... Us,
std::size_t I = gf_h<0, std::is_integral<Us>::value...>::value>
auto get_first_integral (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
template <typename ... Us,
std::size_t I = gf_h<0, std::is_floating_point<Us>::value...>::value>
auto get_first_floating (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
int main()
{
auto tup1 = std::make_tuple(3.f, 2., 1, 0);
std::cout << get_first_integral(tup1) << std::endl; // 1
std::cout << get_first_floating(tup1) << std::endl; // 3
auto tup2 = std::make_tuple("abc", 4, 5);
std::cout << get_first_integral(tup2) << std::endl; // 4
// std::cout << get_first_floating(tup2) << std::endl; // error
auto tup3 = std::make_tuple("xyz", 6., 7.f);
// std::cout << get_first_integral(tup3) << std::endl; // error
std::cout << get_first_floating(tup3) << std::endl; // 6
}
Ok, I figured out a way to accomplish this in a way that is not generic over the trait, but that's good enough for my current purpose. Using if constexpr this really doesn't look too bad. I'm sure this isn't hugely idiomatic, but it works for me:
template <std::size_t Idx, typename... Us>
auto& get_if_integral_impl (std::tuple<Us...>& t)
{
static_assert (Idx < std::tuple_size_v<std::tuple<Us...>>,
"No integral elements in this tuple.");
if constexpr (std::is_integral<std::tuple_element_t<Idx, std::tuple<Us...>>>::value)
return std::get<Idx> (t);
else
return get_if_integral_impl<Idx + 1> (t);
}
template<typename... Us>
auto& get_if_integral (std::tuple<Us...>& t)
{
return get_if_integral_impl<0> (t);
}
auto tup = std::make_tuple (3.f, 2., 1, 0);
std::cout << get_if_integral (tup); // '1'
My use case is a little more complex, involving returning the first nested tuple which itself contains another type, but this should convey the basic idea.
Thanks to SO guys I resolved one of my problem:
Create a tuple with variatic type wrapped
But I realized after that, I still have a problem that I can't solve.
So now I have :
template < typename T,
size_t Size >
struct Metadata {
using type = T;
std::bitset<Size> bitset;
};
template <class... Ts>
constexpr auto make_metadata()
{
constexpr size_t N = sizeof...(Ts);
return std::make_tuple(Metadata<Ts, N>{0}...);
}
I intent to use it "like" that :
constexpr auto result = make_metadata<Foo1, Foo2, Foo3>({0}, {0}, {0});
And according to Jarod42 comment, I think I'll need 2 functions.
But how I can pass the arguments to the function and then to the tuple?
And I wonder how do that but without force to pass each arguments for each Ts, if they are not present I'll just put a default value (2 questions).
this may be what you want, I am not sure. (since you never show where the arguments to be used)
#include <tuple>
#include <bitset>
#include <iostream>
struct A{int value;};
struct B{int value;};
struct C{int value;};
template <typename T,int Size>
struct Metadata{
using type = T;
T value;
std::bitset<Size> bitset;
};
template <typename...Ts,typename...Args>
constexpr auto make_metadata(Args... args)
{
constexpr auto N = sizeof...(Ts);
return std::make_tuple(Metadata<Ts, N>{args,0}...);
}
int main(){
auto data = make_metadata<A,B,C>(1,2,3);
std::cout << "(" << std::get<0>(data).value.value
<< ", " << std::get<1>(data).value.value
<< ", " << std::get<2>(data).value.value << ")";
}