#include <iostream>
#include "common/log.h"
#include <limits>
#include <vector>
#include <span>
#include <array>
#include <type_traits>
#include <cmath>
using namespace AdsonLib;
//模板元编程中的条件判断
//模板特化, SFINAE(enable_if),标签分发(tag dispatch), if constexpr, std::contional
//良构判断void_t:比如判断有没有Init函数, T::Init.编译不会失败
//is_floating_point实现
template<typename T>
struct is_floating_point : std::false_type{}; //默认false
//特化float类型
template<>
struct is_floating_point<float> : std::true_type{};
template<>
struct is_floating_point<double> : std::true_type{};
template<>
struct is_floating_point<long double> : std::true_type{};
void test_floating_point(){
static_assert(is_floating_point<float>::value);
static_assert(is_floating_point<double>::value);
static_assert(is_floating_point<long double>::value);
static_assert(std::is_floating_point<long double>::value);
}
//std::is_same实现:判断类型T和U是不是同一个类型
template<typename T, typename U>
struct is_same : std::false_type{};
template<typename T>
struct is_same<T, T> : std::true_type{};
void test_is_same(){
static_assert(is_same<int, int>::value);
static_assert(!is_same<int, float>::value);
static_assert(std::is_same_v<int, int>);
}
//remove const实现
template<typename T>
struct remove_const {
using type = T;
};
template<typename T>
struct remove_const<const T> {
using type = T;
};
void test_remove_const(){
static_assert(std::is_same_v<remove_const<const int>::type, int>);
}
//std::conditional实现 b为true返回类型T,否则返回类型U
template<bool b, typename T, typename U>
struct conditional {
using type = T;
};
template<typename T, typename U>
struct conditional<false, T, U> {
using type = U;
};
template<bool b, typename T, typename U>
using conditional_t = conditional<b, T, U>::type;
void test_conditional(){
static_assert(std::is_same_v<std::conditional_t<true, int, float>, int>);
static_assert(std::is_same_v<std::conditional_t<false, int, float>, float>);
static_assert(std::is_same_v<conditional<true, int, float>::type, int>);
static_assert(std::is_same_v<conditional<false, int, float>::type, float>);
static_assert(std::is_same_v<conditional_t<false, int, double>, double>);
}
// 类型内省提取数组大小,函数参数返回值类型等
template<typename T> struct array_size;
template<typename T, int N>
struct array_size<T[N]>{
using type = T;
static constexpr size_t len = N;
};
void test_array_size(){
static_assert(array_size<int[5]>::len == 5);
static_assert(std::is_same_v<array_size<int[5]>::type, int>);
int a[6];
using T = decltype(a);
static_assert(std::is_same_v<array_size<T>::type, int>);
static_assert(array_size<T>::len == 6);
}
//std::enable_if 实现
template<bool, typename T = void>
struct enable_if {};
template<typename T>
struct enable_if<true, T> {
using type = T;
};
//当不是数值类型时,enable_if里没有::type,编译错误,根据SFINAE,跳过这个
template<typename T, enable_if<std::is_integral_v<T>, void>::type * = nullptr>
bool NumEQ(T &&a, T &&b) {
return a == b;
}
template<typename T, enable_if<std::is_floating_point_v<T>, void>::type * = nullptr>
bool NumEQ(T &&a, T &&b) {
static constexpr double epsilon = 1e-5;
return std::fabs(b - a) < epsilon;
}
void test_num_eq(){
LOG(INFO) << NumEQ(3, 3);
LOG(INFO) << NumEQ(3, 4);
LOG(INFO) << NumEQ(3.3, 3.3);
LOG(INFO) << NumEQ(3.3, 3.31);
}
//标签分发:就像switch case. 比if else嵌套更直观一些
//定义标签
struct FloatType{};
struct OtherNumType{};
struct StringType{};
template<typename T>
struct NumCategory{
using type = std::enable_if_t<std::is_integral_v<T>, OtherNumType> ;
};
template<> struct NumCategory<std::string>{using type = StringType;};
template<> struct NumCategory<float>{using type = FloatType;};
template<> struct NumCategory<double>{using type = FloatType;};
template<> struct NumCategory<long double>{using type = FloatType;};
//函数不能偏特化,所以用参数重载来分发
template<typename T>
bool NumEQ1(T &&a, T &&b, OtherNumType) {
return a == b;
}
template<typename T>
bool NumEQ1(T &&a, T &&b, FloatType) {
static constexpr double epsilon = 1e-5;
return std::fabs(b - a) < epsilon;
}
template<typename T>
bool NumEQ1(T &&a, T &&b, StringType) {
return a.size() == b.size();
}
template<typename T>
bool NumEQTag(T &&a, T &&b) {
//这里还是嵌套了
using U = conditional<std::is_floating_point_v<T>,
FloatType,
typename conditional<std::is_integral_v<T>, OtherNumType, StringType>::type>::type;
return NumEQ1<T> (std::forward<T>(a), std::forward<T>(b), U{});
}
template<typename T>
bool NumEQTag2(T &&a, T &&b) {
//这里还是嵌套了
using U = NumCategory<T>::type;
return NumEQ1<T> (std::forward<T>(a), std::forward<T>(b), U{});
}
void test_num_eq1(){
LOG(INFO) << "test num eq1";
LOG(INFO) << NumEQTag(3, 3);
LOG(INFO) << NumEQTag(3, 4);
LOG(INFO) << NumEQTag(3.3, 3.3);
LOG(INFO) << NumEQTag(3.3, 3.31);
LOG(INFO) << NumEQTag(std::string("abc"), std::string("def"));
}
void test_num_tag2(){
LOG(INFO) << "test num eq2";
LOG(INFO) << NumEQTag2(3, 3);
LOG(INFO) << NumEQTag2(3, 4);
LOG(INFO) << NumEQTag2(3.3, 3.3);
LOG(INFO) << NumEQTag2(3.3, 3.31);
LOG(INFO) << NumEQTag2(std::string("abc"), std::string("def"));
}
//使用if constexpr来实现
template<typename T>
bool num_eq(T &&a, T &&b) {
if constexpr(std::is_floating_point_v<T>){
static constexpr double epsilon = 1e-5;
return std::fabs(b - a) < epsilon;
}else if constexpr(std::is_same_v<T, std::string>) { //if constexpr和if是不同的。if无论如何要生成代码。 if constexpr false是不生成代码的
return a.size() == b.size(); //不用if constexpr这里报错,因为当T=int时,这里还是要生成代码
}else if constexpr(std::is_integral_v<T>){
return a == b;
}else{
return a == b;
}
}
void test_num_if(){
LOG(INFO) << "test num";
LOG(INFO) << num_eq(3, 3);
LOG(INFO) << num_eq(3, 4);
LOG(INFO) << num_eq(3.3, 3.3);
LOG(INFO) << num_eq(3.3, 3.31);
LOG(INFO) << num_eq(std::string("abc"), std::string("def"));
}
//良构判断std::void_t实现
template<typename...> using void_t = void;
//判断T中有没有type,即T::type
template<typename T, typename = void>
struct HasTypeMember : std::false_type{};
template<typename T>
struct HasTypeMember<T, void_t<typename T::type>> : std::true_type{};
//判断T中有没有Init方法
template<typename T, typename = void>
struct HasInit: std::false_type{};
template<typename T>
struct HasInit<T, std::void_t<decltype(std::declval<T>().Init())>>: std::true_type{};
struct FooInt{};
struct Bar{
using type = int;
void Init(){}
};
void test_has_type(){
static_assert(!HasTypeMember<FooInt>::value);
static_assert(HasTypeMember<Bar>::value);
static_assert(HasTypeMember<std::false_type>::value);
static_assert(!HasInit<FooInt>::value);
static_assert(HasInit<Bar>::value);
}
int main(int argc, char *argv[]) {
test_num_eq();
test_num_eq1();
test_num_tag2();
}