Моя цель состоит в том, чтобы иметь структуру, которая берет псевдоним специализированного enable_if_t<>
вместе с набором параметров типа typename и затем сообщает мне, были ли выполнены условия enable_if
для всех типов в пакете,У меня есть несколько этих специализированных enable_if
, но мне нужно написать тесты для них, прежде чем мы сможем поместить их в наш проект с открытым исходным кодом. У меня есть около 2000+ строк кода, вручную тестирующих эти специализации, но держу пари, что я могу довести его до 100 или 200, если смогу разобраться в схеме ниже. У меня есть рабочая версия (+ ссылка Godbolt), но я не уверен, почему это работает, и эта схема не работает в случае, когда реализация получает пакет параметров
Вот пример кода, который яхотел бы написать и это результат. Я использую C ++ 14 и могу украсть базовые реализации вещей из C ++ 17, например, conunction и void_t
#include <type_traits>
#include <string>
// enable_if for arithmetic types
template <typename T>
using require_arithmetic = typename std::enable_if_t<std::is_arithmetic<T>::value>;
const bool true_arithmetic = require_tester<require_arithmetic, double, int, float>::value;
// output: true
// If any of the types fail the enable_if the result is false
const bool false_arithmetic = require_tester<require_arithmetic, double, std::string, float>::value;
// output: false
Ниже приведено то, что я хочу, но я не совсем понимаю, как,
// Base impl
template <template <class> class Check, typename T1, typename = void>
struct require_tester_impl : std::false_type {};
// I'm not totally sure why void_t needs to be here?
template <template <class> class Check, typename T1>
struct require_tester_impl<Check, T1, void_t<Check<T1>>> : std::true_type {};
// The recursive version (stolen conjuction from C++17)
template <template <class> class Check, typename T = void, typename... Types>
struct require_tester {
static const bool value = conjunction<require_tester_impl<Check, T>,
require_tester<Check, Types...>>::value;
};
// For the end
template <template <class> class Check>
struct require_tester<Check, void> : std::true_type {} ;
В частности, я не уверен, почему void_t необходим для частичной специализации impl
для std::true_type
.
Что я хотел бы получить, это require_variadic_tester
, который принимает альтернативный шаблонный псевдоним, что-то вроде enable_if<conjunction<check<T...>>::value>
, и дает мне истину или ложь. К сожалению, ниже возвращается false независимо от того, какие типы входят
// impl
template <template <class...> class Check, typename... Types>
struct require_variadic_impl : std::false_type {};
// Adding void_t here causes the compiler to not understand the partial specialiation
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, Check<Types...>> : std::true_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_tester : require_variadic_impl<Check, Types...> {};
Я хотел бы получить следующее, учитывая входные данные, но, похоже, не может пошатнуться, как скрыть это соединение на один уровень ниже
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
require_variadic_tester<require_all_arithmetic, double, double, double>::value;
// is true
require_variadic_tester<require_all_arithmetic, double, std::string, double>::value;
// is false
Я думаю, что моя неспособность понять void_t
в первой мета-функции вызывает моё недоразумение
Ниже, черт возьми, любая помощь в понимании этого очень ценится!
https://godbolt.org/z/8XNqpo
Редактировать:
Чтобы дать больше контекста, почему я хочу выше с соединением внутри enable_if_t
. Я застрял на C ++ 14, но мы добавляем новую функцию в нашу математическую библиотеку с открытым исходным кодом, которая без более общих типов (и требований к этим универсальным типам) приведет к огромному количеству кода. В настоящее время у нас есть что-то вроде этого
template <int R, int C>
inline Eigen::Matrix<double, R, C> add(
const Eigen::Matrix<double, R, C>& m1, const Eigen::Matrix<double, R, C>& m2) {
return m1 + m2;
}
Я хотел бы иметь более общие шаблоны и сделать что-то вроде этого
template <typename Mat1, typename Mat2,
require_all_eigen<is_arithmetic, Mat1, Mat2>...>
inline auto add(Mat1&& m1, Mat2&& m2) {
return m1 + m2;
}
У меня есть все эти настройки require_*_<container>
aliases, нотесты для всех этих требований - около 2000+ строк, и в будущем это будет сложный беспорядок, с которым придется иметь дело.
У нас есть унарный и переменный шаблон enable_if aliases, на данный момент унарный случай вышевыполняет то, что я хочу, а также хороший тест, например
#include <gtest/gtest.h>
TEST(requires, arithmetic_test) {
EXPECT_FALSE((require_tester<require_arithmetic, std::string>::value));
EXPECT_TRUE((require_tester<require_arithmetic, double, int, float>::value));
}
Проблема, с которой я столкнулся, заключается в тестировании variadic template enable_if aliases, где я хочу иметь возможность написать что-то вроде
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
/// For the tests
TEST(requires, arithmetic_all_test) {
EXPECT_FALSE((require_variadic_tester<require_all_arithmetic, std::string,
Eigen::Matrix<float, -1, -1>>::value));
EXPECT_TRUE((require_variadic_tester<require_all_arithmetic,
double, int, float>::value));
}
Если я смогу протестировать все это, я думаю, что requires
часть нашей библиотеки могла бы быть просто мини-библиотекой с хорошим заголовком для того, что я называю «плохими ложными концепциями в 14» (или для краткости bfc14; -))