wip ctl type traits

I am a bit ambivalent about this. <type_traits> is actually not that bad
by STL standards, “only” 148 transitive includes for the whole file. The
split out variants under __type_traits are also mostly reasonable - e.g.
__type_traits/is_same.h only includes 4 other STL files. Many others ask
for more, but nothing is all that egregious.

I do think we can probably provide an 80–95% solution with a fraction of
the footprint in CTL by for example eliding direct struct definitions in
favor of the _t or _v variants when the latter are simpler to write, and
omitting the more obscure tests in favor of a common core. I don’t think
it’s worth splitting things out by file but it might be worth it to e.g.
have a type_traits.h and a type_traits_advanced.h or something.
This commit is contained in:
Steven Dee (Jōshin) 2024-06-20 11:25:18 -07:00
parent 9a5a13854d
commit 163d6c102a
No known key found for this signature in database
2 changed files with 199 additions and 0 deletions

71
ctl/type_traits.cc Normal file
View file

@ -0,0 +1,71 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Justine Alexandra Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "type_traits.h"
// #include <type_traits>
// #define ctl std
using namespace ctl;
#undef ctl
static_assert(is_void_v<void>);
static_assert(is_void_v<const void>);
static_assert(is_void_v<volatile const void>);
static_assert(is_same_v<int, int>);
static_assert(!is_same_v<int, bool>);
struct A
{
};
using B = A;
struct C
{
};
struct Base
{
};
struct Derived : Base
{
};
static_assert(is_same_v<A, A>);
static_assert(is_same_v<A, B>);
static_assert(!is_same_v<A, C>);
static_assert(!is_same_v<Base, Derived>);
static_assert(is_convertible_v<int, bool>);
static_assert(is_convertible_v<Derived, Base>);
static_assert(!is_convertible_v<Base, Derived>);
static_assert(is_convertible_v<Derived*, Base*>);
static_assert(!is_convertible_v<Base*, Derived*>);
static_assert(is_convertible_v<A&, const A&>);
static_assert(is_convertible_v<A&&, const A&>);
static_assert(!is_convertible_v<const A&, A&>);
static_assert(!is_convertible_v<A&, A&&>);
static_assert(!is_convertible_v<A&&, A&>);
static_assert(is_same_v<int, remove_extent_t<int[]>>);
static_assert(is_same_v<int, remove_extent_t<int[5]>>);

128
ctl/type_traits.h Normal file
View file

@ -0,0 +1,128 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef COSMOPOLITAN_CTL_TYPE_TRAITS_H_
#define COSMOPOLITAN_CTL_TYPE_TRAITS_H_
#include "utility.h"
namespace ctl {
template <typename T, T V>
struct integral_constant
{
using value_type = T;
using type = integral_constant<T, V>;
static constexpr T value = V;
constexpr operator value_type() const noexcept
{
return V;
}
constexpr value_type operator()() const noexcept
{
return V;
}
};
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;
template <typename T, typename U>
struct is_same : false_type
{
};
template <typename T>
struct is_same<T, T> : true_type
{
};
template <typename T, typename U>
inline constexpr bool is_same_v = is_same<T, U>::value;
template <typename T>
struct remove_extent
{
using type = T;
};
template <typename T>
struct remove_extent<T[]>
{
using type = T;
};
template <typename T, size_t N>
struct remove_extent<T[N]>
{
using type = T;
};
template <typename T>
using remove_extent_t = typename remove_extent<T>::type;
template <typename T>
struct remove_cv
{
using type = T;
};
template <typename T>
struct remove_cv<const T>
{
using type = T;
};
template <typename T>
struct remove_cv<volatile T>
{
using type = T;
};
template <typename T>
struct remove_cv<const volatile T>
{
using type = T;
};
template <typename T>
using remove_cv_t = typename remove_cv<T>::type;
template <typename T>
inline constexpr bool is_void_v = is_same_v<void, remove_cv_t<T>>;
namespace __ {
template <typename T>
auto test_returnable(int) -> decltype(
void(static_cast<T(*)()>(nullptr)), true_type{});
template <typename>
auto test_returnable(...) -> false_type;
template <typename T, typename U>
auto test_implicitly_convertible(int) -> decltype(
void(declval<void(&)(U)>()(declval<T>())), true_type{});
template <typename, typename>
auto test_implicitly_convertible(...) -> false_type;
} // namespace __
template <typename T, typename U>
struct is_convertible : integral_constant<bool,
(decltype(__::test_returnable<U>(0))::value &&
decltype(__::test_implicitly_convertible<T, U>(0))::value) ||
(is_void_v<T> && is_void_v<U>)
>
{
};
template <typename T, typename U>
inline constexpr bool is_convertible_v = is_convertible<T, U>::value;
} // namespace ctl
#endif // COSMOPOLITAN_CTL_TYPE_TRAITS_H_