I am trying to implement a unit_converter using traits. Here is what I set up so far
// create an enumerate class including basic distance units.
enum class Unit {km, m, cm}
// name a trait _Measure_ holding unit and value
template<int v, Unit u> struct M{
static const int value=v;
static const Unit unit=u;
};
int main(int, char**){
// ...
}
now, what I expected is write another trait M_add which can be called in main()
as like M_add<M<10, Unit::cm>, M<10, Unit::m>>, and it has the same inner parameters value and unit. However, in the process of defining such trait, I found difficulties in registering it with proper template arguments.
Firstly, I've tried using M as a 'type' so I can make operations on innter paras of a and b and assign inner paras of M_add.
template<M<int, Unit> a, M<int, Unit> b> struct M_add{
// implementation
}
Then it returns error: template argument for non-type template parameter must be an expression
So I change the arguments as
template<template<int v, Unit u> class M1, template<int v, Unit u> class M2 >
struct M_add{
// implementation
};
while this time the registration pass the compilation, the implementation M_add<M<5, Unit::cm>, M<6, Unit::km>> a; in main() returns error error: template argument for template template parameter must be a class template or type alias template.
What I expect is to operate the inner parameters of M a and M b in template struct M_add, specifically the a.value and a.unit, so the typename specification would be necessary. So what would be the proper initiation for trait M_add in this case so I can do operation like
template<M a, M b> struct M_add{
// implementation
static const int value=a.value+b.value;
}
If you want to be able to write M_add<M<5, Unit::cm>, M<6, Unit::km>> a;, M_add must be something like this:
template <typename M1, typename M2>
struct M_add;
because M<5, Unit::cm> and M<6, Unit::km> are types, not templates.
Your try with template template parameters would be used like M_add<M, M> a;, because M is a template.
If you want to only allow types instantiated from M to be used as parameters, you can add a trait:
template <typename T>
struct is_M : std::false_type {};
template <int v, Unit u>
struct is_M<M<v, u>> : std::true_type {};
template <typename T>
static constexpr bool is_M_v = is_M<T>::value;
template <typename M1, typename M2>
struct M_add {
static_assert(is_M_v<M1> && is_M_v<M2>);
static const int value = []{
if constexpr (M1::unit == M2::unit) {
return M1::value + M2::value;
} else if constexpr (M1::unit == Unit::km && M2::unit == Unit::m
|| M1::unit == Unit::m && M2::unit == Unit::cm) {
return M1::value * 1000 + M2::value;
} else if constexpr (M1::unit == Unit::km && M2::unit == Unit::cm) {
return M1::value * 1000000 + M2::value;
} else if constexpr (M1::unit == Unit::m && M2::unit == Unit::km
|| M1::unit == Unit::cm && M2::unit == Unit::m) {
return M1::value + M2::value * 1000;
} else if constexpr (M1::unit == Unit::cm && M2::unit == Unit::km) {
return M1::value + M2::value * 1000000;
}
return 0;
}();
static const Unit unit = []{
if constexpr (M1::unit == Unit::cm || M2::unit == Unit::cm) {
return Unit::cm;
} else if constexpr (M1::unit == Unit::m || M2::unit == Unit::m) {
return Unit::m;
} else if constexpr (M1::unit == Unit::km || M2::unit == Unit::km) {
return Unit::km;
}
return Unit{};
}();
};
Demo
Related
I have a function that computes a certain object from a given parameter (say, an important node from a graph). Now, when calculating such an object, the function might allocate some memory. Sometimes I want the function to just return the result, and sometimes to return the result plus the memory used to compute it.
I typically solve this binary case like this:
enum class what {
what1, // return, e.g., just an int
what2 // return, e.g., a std::pair<int, std::vector<int>>
};
template <what w>
std::conditional_t<w == what::what1, int, std::pair<int, std::vector<int>>>
calculate_something(const param& p) { ... }
I would like to generalize the solution above to longer enumerations
enum class list_whats {
what1,
what2,
what3,
what4,
what5
};
One possible solution is to nest as many std::conditional_t as needed
template <list_whats what>
std::conditional_t<
what == list_whats::what1,
int,
std::conditional_t<
what == list_whats::what2,
float,
....
>
>
>
calculate_something(const param& p) { ... }
But this is cumbersome and perhaps not too elegant.
Does anyone know how to do this in C++ 17?
EDIT
To make the question perfectly clear: how do I implement the function return_something so as to be able to run the following main?
int main() {
int s1 = return_something<list_whats::what1>();
s1 = 3;
float s2 = return_something<list_whats::what2>();
s2 = 4.0f;
double s3 = return_something<list_whats::what3>();
s3 = 9.0;
std::string s4 = return_something<list_whats::what4>();
s4 = "qwer";
std::vector<int> s5 = return_something<list_whats::what5>();
s5[3] = 25;
}
I don't think you should use std::conditional at all to solve your problem. If I get this right, you want to use a template parameter to tell your function what to return. The elegant way to do this could look something like this:
#include <vector>
enum class what { what1, what2 };
template <what W>
auto compute() {
if constexpr (W == what::what1) {
return 100;
}
if constexpr (W == what::what2) {
return std::pair{100, std::vector<int>{}};
}
}
auto main() -> int {
[[maybe_unused]] const auto as_int = compute<what::what1>();
[[maybe_unused]] const auto as_pair = compute<what::what2>();
}
You can also use template specialization if you prefer another syntax:
template <what W>
auto compute();
template <>
auto compute<what::what1>() {
return 100;
}
template <>
auto compute<what::what2>() {
return std::pair{100, std::vector<int>{}};
}
Here's my approach:
what_pair is a pair that corresponds one enum to one type.
what_type_index accepts a enum and a std::tuple<what_pair<...>...> and searches the tuple map where the enums are equal and returns index. It returns maximum std::size_t value, if no match was found.
what_type is the final type, it is the tuple element at the found position. The program won't compile when the index is std::size_t max value because of invalid std::tuple access.
template<what W, typename T>
struct what_pair {
constexpr static what w = W;
using type = T;
};
template<what w, typename tuple_map>
constexpr auto what_type_index() {
std::size_t index = std::numeric_limits<std::size_t>::max();
auto search_map = [&]<std::size_t... Ints>(std::index_sequence<Ints...>) {
((std::tuple_element_t<Ints, tuple_map>::w == w ? (index = Ints) : 0), ...);
};
search_map(std::make_index_sequence<std::tuple_size_v<tuple_map>>());
return index;
}
template<what w, typename tuple_map>
using what_type = typename
std::tuple_element_t<what_type_index<w, tuple_map>(), tuple_map>::type;
and this is the example usage:
int main() {
using what_map = std::tuple<
what_pair<what::what1, int>,
what_pair<what::what2, float>,
what_pair<what::what3, double>,
what_pair<what::what4, std::string>,
what_pair<what::what5, std::vector<int>>>;
static_assert(std::is_same_v<what_type<what::what1, what_map>, int>);
static_assert(std::is_same_v<what_type<what::what2, what_map>, float>);
static_assert(std::is_same_v<what_type<what::what3, what_map>, double>);
static_assert(std::is_same_v<what_type<what::what4, what_map>, std::string>);
static_assert(std::is_same_v<what_type<what::what5, what_map>, std::vector<int>>);
//compilation error, because 'what6' wasn't specified in the 'what_map'
using error = what_type<what::what6, what_map>;
}
try it out on godbolt.
I found a possible solution that nests two structs: the first takes a list of Boolean values to indicate which type should be used, and the nested struct takes the list of possible types (see conditional_list in the example code below -- the nested structs were inspired by this answer). But perhaps it's not elegant enough. I'm wondering if there is a possible solution of the form
std::conditional_list<
..., // list of Boolean values, (of any length!)
... // list of types (list that should be as long as the first)
>::type
My proposal
#include <type_traits>
#include <iostream>
#include <vector>
// -----------------------------------------------------------------------------
template<auto A, auto... ARGS>
constexpr auto sum = A + sum<ARGS...>;
template<auto A>
constexpr auto sum<A> = A;
// -----------------------------------------------------------------------------
template <bool... values>
static constexpr bool exactly_one_v = sum<values...> == 1;
// -----------------------------------------------------------------------------
template <bool... values>
struct which {
static_assert(exactly_one_v<values...>);
template <std::size_t idx, bool v1, bool... vs>
struct _which_impl {
static constexpr std::size_t value =
(v1 ? idx : _which_impl<idx + 1, vs...>::value);
};
template <std::size_t idx, bool v>
struct _which_impl<idx, v> {
static constexpr std::size_t value = (v ? idx : idx + 1);
};
static constexpr std::size_t value = _which_impl<0, values...>::value;
};
template <bool... conds>
static constexpr std::size_t which_v = which<conds...>::value;
// -----------------------------------------------------------------------------
template <std::size_t ith_idx, typename... Ts>
struct ith_type {
template <std::size_t cur_idx, typename t1, typename... ts>
struct _ith_type_impl {
typedef
std::conditional_t<
ith_idx == cur_idx,
t1,
typename _ith_type_impl<cur_idx + 1, ts...>::type
>
type;
};
template <std::size_t cur_idx, typename t1>
struct _ith_type_impl<cur_idx, t1> {
typedef
std::conditional_t<ith_idx == cur_idx, t1, std::nullptr_t>
type;
};
static_assert(ith_idx < sizeof...(Ts));
typedef typename _ith_type_impl<0, Ts...>::type type;
};
template <std::size_t ith_idx, typename... ts>
using ith_type_t = typename ith_type<ith_idx, ts...>::type;
// -----------------------------------------------------------------------------
template <bool... conds>
struct conditional_list {
template <typename... ts>
struct good_type {
static_assert(sizeof...(conds) == sizeof...(ts));
typedef ith_type_t<which_v<conds...>, ts...> type;
};
};
// -----------------------------------------------------------------------------
enum class list_whats {
what1,
what2,
what3,
what4,
what5,
};
template <list_whats what>
typename conditional_list<
what == list_whats::what1,
what == list_whats::what2,
what == list_whats::what3,
what == list_whats::what4,
what == list_whats::what5
>::template good_type<
int,
float,
double,
std::string,
std::vector<int>
>::type
return_something() noexcept {
if constexpr (what == list_whats::what1) { return 1; }
if constexpr (what == list_whats::what2) { return 2.0f; }
if constexpr (what == list_whats::what3) { return 3.0; }
if constexpr (what == list_whats::what4) { return "42"; }
if constexpr (what == list_whats::what5) { return {1,2,3,4,5}; }
}
int main() {
auto s1 = return_something<list_whats::what1>();
s1 = 3;
auto s2 = return_something<list_whats::what2>();
s2 = 4.0f;
auto s3 = return_something<list_whats::what3>();
s3 = 9.0;
auto s4 = return_something<list_whats::what4>();
s4 = "qwer";
auto s5 = return_something<list_whats::what5>();
s5[3] = 25;
}
An alternative to working only with types is to write a function that returns the specific type, or an identity<type>. It's sometimes more readable. Here is an example:
// if you don't have it in std
template<typename T>
struct identity {
using type = T;
};
enum class what {
what1,
what2,
what3
};
template<what w>
auto return_type_for_calc() {
if constexpr (w == what::what1) {
return identity<int>();
} else if constexpr (w==what::what2) {
return identity<double>();
} else {
return identity<float>();
}
}
template<what w>
decltype(return_type_for_calc<w>())
calculate_something()
{
return {};
}
int main() {
calculate_something<what::what1>();
calculate_something<what::what2>();
return 0;
}
Although I already posted an answer to my own question, and I accepted an answer from another user, I thought I could post another possibility in tackling this problem using the following struct
template <typename... Ts> struct type_sequence { };
which I learnt about in this talk by Andrei Alexandrescu. Since I learnt quite a bit by using it and the result is a bit simpler than the original answer that used two nested structs I thought I would share it here. However, the solution I would actually implement is the one that was accepted.
This is the full code with a main function included. Notice the change in the specification of function return_something. Now this function indicates the return type (which I like very much, perhaps I'm old fashioned) in a more readable way than in my first answer. You can try it out here.
#include <type_traits>
#include <iostream>
#include <vector>
template <bool... values>
struct which {
template <std::size_t idx, bool v1, bool... vs>
struct _which_impl {
static constexpr std::size_t value =
(v1 ? idx : _which_impl<idx + 1, vs...>::value);
};
template <std::size_t idx, bool v>
struct _which_impl<idx, v> {
static constexpr std::size_t value = (v ? idx : idx + 1);
};
static constexpr std::size_t value = _which_impl<0, values...>::value;
};
template <std::size_t ith_idx, typename... Ts>
struct ith_type {
template <std::size_t cur_idx, typename t1, typename... ts>
struct _ith_type_impl {
using type =
std::conditional_t<
ith_idx == cur_idx,
t1,
typename _ith_type_impl<cur_idx + 1, ts...>::type
>;
};
template <std::size_t cur_idx, typename t1>
struct _ith_type_impl<cur_idx, t1> {
using type = std::conditional_t<ith_idx == cur_idx, t1, std::nullptr_t>;
};
using type = typename _ith_type_impl<0, Ts...>::type;
};
template <std::size_t ith_idx, typename... ts>
using ith_type_t = typename ith_type<ith_idx, ts...>::type;
template <bool... conds>
static constexpr std::size_t which_v = which<conds...>::value;
template <typename... Ts> struct type_sequence { };
template <bool... values> struct bool_sequence {
static constexpr std::size_t which = which_v<values...>;
};
template <std::size_t ith_idx, typename... Ts>
struct ith_type<ith_idx, type_sequence<Ts...>> : ith_type<ith_idx, Ts...>
{ };
template <typename bool_sequence, typename type_sequence>
struct conditional_list {
using type = ith_type_t<bool_sequence::which, type_sequence>;
};
template <typename bool_sequence, typename type_sequence>
using conditional_list_t =
typename conditional_list<bool_sequence, type_sequence>::type;
enum class list_whats {
what1,
what2,
what3,
what4,
what5,
};
template <list_whats what>
conditional_list_t<
bool_sequence<
what == list_whats::what1,
what == list_whats::what2,
what == list_whats::what3,
what == list_whats::what4,
what == list_whats::what5
>,
type_sequence<
int,
float,
double,
std::string,
std::vector<int>
>
>
return_something() noexcept {
if constexpr (what == list_whats::what1) { return 1; }
if constexpr (what == list_whats::what2) { return 2.0f; }
if constexpr (what == list_whats::what3) { return 3.0; }
if constexpr (what == list_whats::what4) { return "42"; }
if constexpr (what == list_whats::what5) { return {1,2,3,4,5}; }
}
int main() {
[[maybe_unused]] auto s1 = return_something<list_whats::what1>();
[[maybe_unused]] auto s2 = return_something<list_whats::what2>();
[[maybe_unused]] auto s3 = return_something<list_whats::what3>();
[[maybe_unused]] auto s4 = return_something<list_whats::what4>();
[[maybe_unused]] auto s5 = return_something<list_whats::what5>();
}
I am experimenting with basic template metaprogramming. I tried implementing structure templates which help us establish whether their template argument is prime or not. I.e.:
template<int N, int D>
struct IsPrime_Descend {
const static bool val = (N % D != 0) && IsPrime_Descend<N, D - 1>::val;
};
template<int N>
struct IsPrime_Descend<N, 1> {
const static bool val = true;
};
template <int N>
struct IsPrime {
const static bool val = IsPrime_Descend<N, N - 1>::val;
};
But the implementation above takes linear time. I wanted to boost it up to O(sqrt(n)). Of course, there is the long way of introducing a structure template for calculating the square root and descending from it:
template<int N, int D>
struct Sqrt_Descend {
const static int val = D * D > N ? Sqrt_Descend<N, D - 1>::val : D;
};
template<int N>
struct Sqrt_Descend<N, 1> {
const static int val = 1;
};
template<int N>
struct Sqrt {
const static int val = Sqrt_Descend<N, N>::val;
};
template<int N, int D>
struct IsPrime_Descend {
const static bool val = (N % D != 0) && IsPrime_Descend<N, D - 1>::val;
};
template<int N>
struct IsPrime_Descend<N, 1> {
const static bool val = true;
};
template <int N>
struct IsPrime {
const static bool val = IsPrime_Descend<N, Sqrt<N>::val>::val;
};
But there's something else I tried:
template <int N, int D>
struct IsPrime_Ascend {
const static bool val = (N % D != 0) && (D * D <= N) && IsPrime_Ascend<N, D + 1>::val;
};
template <int N>
struct IsPrime {
const static bool val = IsPrime_Ascend<N, 1>::val;
};
I reckoned this snippet to instantiate IsPrime_Ascend<N, D> as long as the two preceding conditions ((N % D != 0) && (D * D <= N)) are true due to the laziness of &&. But, apparently, it does not stop when one of them breaks and exceeds template instantiation maximum depth.
So, why is && strict (as in not lazy) in compile-time?
Short-circuit evaluation deals with evaluation of expressions. The expression is still there in the text of the C++ file, and it therefore must be compiled. If that expression contains a template instantiation, then that template must be instantiated. That's how compilation works (unless you use if constexpr, which you can't within that context).
If you want to prevent further instantiation, you have to do so via the rules of templates, not the rules of expression evaluation. So you need to use a partial specialization of the template, one which probably uses SFINAE techniques that is active when the condition is true. C++20 makes this easier with a requires clause.
Better still, turn IsPrime_Descend into a constexpr function.
In order to define overloaded functions to parse an input string to specific data type, I do like below:
#include <type_traits>
// /std:c++17 enabled
template <class T>
constexpr bool is_point_v = std::is_class_v<T> && !std::is_enum_v<T>
&& std::is_arithmetic_v<decltype(T::x)>
&& std::is_arithmetic_v<decltype(T::y)>
&& (sizeof(T) == sizeof(T::y) + sizeof(T::y));
template <class T>
constexpr bool is_rect_v = std::is_class_v<T> && !std::is_enum_v<T>
&& std::is_arithmetic_v<decltype(T::left)>
&& std::is_arithmetic_v<decltype(T::top)>
&& std::is_arithmetic_v<decltype(T::right)>
&& std::is_arithmetic_v<decltype(T::bottom)>
&& (sizeof(T) == sizeof(T::left) + sizeof(T::top) + sizeof(T::right) + sizeof(T::bottom));
template<typename T>
struct Point {
std::enable_if_t<std::is_arithmetic_v<T>, T>
x {}, y {};
Point() noexcept {}
};
template<typename T>
struct Rect {
std::enable_if_t<std::is_arithmetic_v<T>, T>
left {}, top {}, right {}, bottom {};
Rect() noexcept {}
};
class DBParser
{
public:
template <typename T>
typename std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>, T>
get(const char* key, T defaultValue = {}) {
// ...
return defaultValue;
}
template <typename T>
typename std::enable_if_t<is_point_v<T>, T>
get(const char* key, T defaultValue = {}) {
// ...
return defaultValue;
}
template <typename T>
typename std::enable_if_t<is_rect_v<T>, T>
get(const char* key, T defaultValue = {}) {
// ...
return defaultValue;
}
};
int main()
{
DBParser db;
auto point1 = db.get<Point<int>>("point1", {});
auto point2 = db.get<Point<float>>("point2", {});
auto rect1 = db.get<Rect<int>>("rect1", {});
auto rect2 = db.get<Rect<float>>("rect2", {});
auto ret3 = db.get<int>("key3", {});
}
Compiling the code above gives a tone of errors. Obviously the SFINAE I did for the functions "get" are incorrect and somewhat clumsy but I could not figure it out.
Could you give me some fixes for the code? In the case C++17 could not make the code less verbose, I accept C++20.
UPDATE:
In order to make the question is more appropriate to the issue, I want to change the title and add more information.
One thing strange is if I put the testing of struct directly to the functions like this:
template <class T>
std::enable_if_t < std::is_class_v<T> && !std::is_enum_v<T>
&& std::is_arithmetic_v<decltype(T::x)>
&& std::is_arithmetic_v<decltype(T::y)>
&& (sizeof(T) == sizeof(decltype(T::x)) + sizeof(decltype(T::y))), T>
get(const char* key, T defaultValue) { // Point
return defaultValue;
}
It get compiled without errors. So I think the assumption from Nicol Bolas may be not correct.
I'm creating a variadic template.
Let's say I have something like this:
template<typename T, T ... Numbers>
class Sequence final {
// Unpack parameter pack into a constexpr array
constexpr static T count = sizeof...(Numbers);
constexpr static T numbers[count] = { Numbers... };
// ...
}
Instances of this class can be instantiated like:
Sequence<uint32_t, 1, 2, 3, 42, 25> seq;
I'd like to make sure at compile time using a static_assert that the numbers parameter pack only contains specific numbers. For the sake of this example, let's say I only want to allow 0 or 1.
So I'd like to do something like:
for (size_t i = 0; i < count; i++) {
static_assert(numbers[i] == 1 || numbers[i] == 0, "Only ones and zeroes are allowed.");
}
But obviously, static_assert doesn't work with a for loop. I'm pretty sure there must be some sort of syntax for this but I haven't been able to figure it out.
I'd prefer to use something that compiles with a C++11 compiler (or perhaps a C++14 compiler, if it isn't doable in C++11).
I'll throw in #Columbo's bool_pack trick.
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
static_assert(all_true<(Numbers == 0 || Numbers == 1)...>::value, "");
Extract the expression into a constexpr function if it gets complex.
Simple C++14 solution:
template <typename T, T ... Numbers>
class Sequence final {
static constexpr bool is_all_zero_or_one(std::initializer_list<T> list) {
for (auto elem : list) {
if (elem != 0 && elem != 1) return false;
}
return true;
}
static_assert(is_all_zero_or_one({Numbers...}),
"Only zeroes and ones are allowed.");
};
a one-line c++17 solution based on #T.C.'s answer.
#include <type_traits>
static_assert(std::conjunction<std::bool_constant<(Numbers == 0 || Numbers == 1)>...>::value, "");
actually this could be done with c++11, given both std::conjunction and std::bool_constant are purely library feature and don't require any core language feature beyond c++11.
You cannot use a traditional for loop with compile-time values, but there are many ways you can iterate over a compile-time collection. In your case, however, it is not necessary to explicitly loop over every single number: you can use pack expansion to make sure the numbers are only 0 or 1:
coliru example
#include <type_traits>
// We define a `conjunction<...>` helper that will evaluate to
// a true boolean `std::integral_constant` if all passed types evaluate
// to true.
template <typename...>
struct conjunction : std::true_type
{
};
template <typename T>
struct conjunction<T> : T
{
};
template <typename T, typename... Ts>
struct conjunction<T, Ts...>
: std::conditional_t<T::value != false, conjunction<Ts...>, T>
{
};
// Define `constexpr` predicates:
template <int T>
constexpr bool is_zero_or_one()
{
return T == 0 || T == 1;
}
template <int... Ts>
constexpr bool all_are_zero_or_one()
{
// Using variadic pack expansion and `conjunction` we can
// simulate an `and` left fold over the parameter pack:
return conjunction<
std::integral_constant<bool, is_zero_or_one<Ts>()>...
>{};
}
int main()
{
static_assert(all_are_zero_or_one<0, 1, 0, 1, 0, 0>(), "");
static_assert(!all_are_zero_or_one<2, 1, 0, 1, 0, 0>(), "");
}
If you are looking for an explicit way to iterate over a compile-time collection of elements, I suggest you to look into the following resources:
boost::hana - a modern metaprogramming library that allows compile-time computations using "traditional" imperative syntax.
My CppCon 2015 talk: for_each_argument explained and expanded - using std::tuple and the "type-value encoding" paradigm you can store compile-time numerical values in a tuple and iterate over it at compile time. My talk shows a possible way to iterate in such a way.
You can implement your static validation with a recursive template helper, like this. Then when you attempt to compile code with a sequence that contains invalid numbers you will get a compiler error with a static assertion failure as you wanted.
#include <iostream>
template<typename T, T... Numbers>
struct ValidateSequence;
template<typename T>
struct ValidateSequence<T>{};
template<typename T, T Number, T... Numbers>
struct ValidateSequence<T, Number, Numbers...>
{
static_assert(Number == 0 || Number == 1, "Invalid Number");
ValidateSequence<T, Numbers...> rest;
};
template<typename T, T... Numbers>
class Sequence
{
public:
constexpr static unsigned count = sizeof...(Numbers);
constexpr static T numbers[] = {Numbers...};
ValidateSequence<T, Numbers...> validate;
};
int main()
{
Sequence <int, 1, 2, 1, 2> sec;
std::cout << sec.count << std::endl;
return 0;
}
C++11
msvc2015u3,gcc5.4,clang3.8
#include <cstdint>
#include <algorithm>
namespace utility {
template <typename T0>
inline constexpr bool is_all_true(T0 && v0)
{
return std::forward<T0>(v0) ? true : false;
}
template <typename T0, typename... Args>
inline constexpr bool is_all_true(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? true : false) && is_all_true(std::forward<Args>(args)...);
}
template <typename T0>
inline constexpr bool is_all_false(T0 && v0)
{
return std::forward<T0>(v0) ? false : true;
}
template <typename T0, typename... Args>
inline constexpr bool is_all_false(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? false : true) && is_all_false(std::forward<Args>(args)...);
}
template <typename T0>
inline constexpr bool is_any_true(T0 && v0)
{
return std::forward<T0>(v0) ? true : false;
}
template <typename T0, typename... Args>
inline constexpr bool is_any_true(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? true : false) || is_any_true(std::forward<Args>(args)...);
}
template <typename T0>
inline constexpr bool is_any_false(T0 && v0)
{
return std::forward<T0>(v0) ? false : true;
}
template <typename T0, typename... Args>
inline constexpr bool is_any_false(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? false : true) || is_any_false(std::forward<Args>(args)...);
}
}
'
#gcc, clang
static_assert(utility::is_all_true((Numbers == 0 || Numbers == 1)...), "Only ones and zeroes are allowed.");
#msvc2015u3 (with workaround for: error C2059: syntax error: '...')
static constexpr const bool boo = utility::is_all_true((Numbers == 0 || Numbers == 1)...);
static_assert(boo, "Only ones and zeroes are allowed.");
https://godbolt.org/z/hcS9FY
Implemented as part of the tacklelib library:
https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/utility/type_identity.hpp
https://github.com/andry81/tacklelib/tree/trunk/include/tacklelib/utility/type_identity.hpp
Yet another solution:
template<typename T>
constexpr bool IsOneOrZero(T&& t) {
return t == 0 || t == 1;
}
template<typename T, typename... Args>
constexpr bool IsOneOrZero(T&& first, Args&&... args) {
return IsOneOrZero(std::forward<T>(first)) && IsOneOrZero(std::forward<Args>(args)...);
}
template<typename T, T First, T... Numbers>
class Sequence final {
// Unpack parameter pack into a constexpr array
constexpr static T count = sizeof...(Numbers);
constexpr static T numbers[count] = { Numbers... };
static_assert(IsOneOrZero(First, Numbers...), "ERROR");
};
I have a variadic class template
template <size_t ...T>
struct Foo
{
std::vector<size_t> t;
bool IsEqual()
{
//??
}
};
which I want to use like:
Foo<1,2,3,4> foo;
foo.data = {1,2,3,4};
foo.IsEqual();
How can I implement IsEqual to iterate and compare every element of the vector and return false / true if the elements are in the same order as the template parameters?
Use the index sequence trick:
bool IsEqual()
{
return t.size() == sizeof...(T) &&
IsEqual(std::make_index_sequence<sizeof...(T)>{});
}
with:
template <size_t... Is>
bool IsEqual(std::index_sequence<Is...> ) {
bool valid = true;
using expander = int[];
expander{0,
(valid = valid && t[Is] == T,
0)...
};
return valid;
}
Could even do this in one function by taking advantage of the fact that every value computation and side effect in an initializer-clause is sequenced before the next one by doing this in one go:
bool IsEqual()
{
if (t.size() == sizeof...(T)) {
auto it = t.begin();
bool valid = true;
using expander = int[];
expander{0,
(valid = valid && *it++ == T,
0)...
};
return valid;
}
else {
return false;
}
}
Simply unpack template arguments.
template <size_t ...T>
struct Foo
{
std::vector<size_t> t;
bool IsEqualTemplate(size_t index)
{
return true;
}
template <typename FIRSTARG, typename ...OTHERARGS>
bool IsEqualTemplate(size_t index, FIRSTARG firstArg, OTHERARGS... otherArgs)
{
return t[index] == firstArg && IsEqualTemplate(index + 1, otherArgs...);
}
bool IsEqual()
{
return t.size() == sizeof...(T) ? IsEqualTemplate(0, T...) : false;
}
};