Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Concepts v2 #45

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ std::cout << map[0] << ' '
<< map[1] << ' '
<< map[2] << '\n';
// A B C


// Not dirty-macro-magic! just simple alias.
// SFINAE can be easily made without macro using good-old-school mechanism
template<typename T, PREVIEW_REQUIRES(preview::integral<T>)>
void foo(T) {}

// Readable error message!
foo(3);

// error: no matching function for call to 'foo'
// note: candidate template ignored: substitution failure [with T = double]: no type named 'valid' in
// 'constraints_not_satisfied<integral<double>, at<0, 1>, because<std::is_integral<double>, is_false>>
```

**`preview` is standard-conforming, and is compatible with existing STL**
Expand Down
41 changes: 41 additions & 0 deletions include/preview/__concepts_v2/common_reference_with.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Created by yonggyulee on 2024. 10. 20.
//

#ifndef PREVIEW_INCLUDE_PREVIEW___CONCEPTS_V2_COMMON_REFERENCE_WITH_H_
#define PREVIEW_INCLUDE_PREVIEW___CONCEPTS_V2_COMMON_REFERENCE_WITH_H_

#include "preview/__concepts_v2/concept_base.h"
#include "preview/__concepts_v2/convertible_to.h"
#include "preview/__concepts_v2/same_as.h"
#include "preview/__type_traits/common_reference.h"
#include "preview/__type_traits/has_typename_type.h"

namespace preview {

#if defined(PREVIEW_USE_LEGACY_CONCEPT)

namespace detail {

template<typename T, typename U>
struct common_reference_with_helper : concepts::concept_base<common_reference_with_helper<T, U>,
has_typename_type<common_reference<T, U>>
> {};

} // namespace detail

template<typename T, typename U>
struct common_reference_with_c : concepts::concept_base<common_reference_with_c<T, U>, decltype(
detail::common_reference_with_helper<T, U>{} && detail::common_reference_with_helper<U, T>{}
)> {};

template<typename T, typename U>
PREVIEW_INLINE_VARIABLE constexpr common_reference_with_c<T, U> common_reference_with;

#else

#endif

} // namespace preview

#endif // PREVIEW_INCLUDE_PREVIEW___CONCEPTS_V2_COMMON_REFERENCE_WITH_H_
214 changes: 214 additions & 0 deletions include/preview/__concepts_v2/concept_base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
//
// Created by yonggyulee on 2024. 9. 4.
//

#ifndef PREVIEW_CONCEPTS_V2_DETAIL_CONCEPT_BASE_H_
#define PREVIEW_CONCEPTS_V2_DETAIL_CONCEPT_BASE_H_

#include "preview/__core/inline_variable.h"
#include "preview/__concepts_v2/detail/config.h"
#include "preview/__concepts_v2/detail/constraints_not_satisfied.h"
#include "preview/__concepts_v2/detail/sized_true.h"
#include "preview/__type_traits/bool_constant.h"

namespace preview {
namespace concepts {

using namespace PREVIEW_CONCEPT_LEXICAL_NAMESPACE;

template<bool B> struct valid_tag {};
template<> struct valid_tag<true> { using valid = bool; };

template<typename Derived, typename Base>
struct concept_base : Base, valid_tag<Base::value> {
private:
template<typename C2, typename B2>
static constexpr sized_true<2> conj_c_c(concept_base, concept_base<C2, B2>, std::true_type, std::true_type)
{ return {}; }
template<typename C2, typename B2>
static constexpr auto conj_c_c(concept_base, concept_base<C2, B2>, std::true_type, std::false_type)
{ return expand_error(constraints_not_satisfied<C2, at<1, 2>>{}); }
template<typename C2, typename B2, bool B>
static constexpr auto conj_c_c(concept_base, concept_base<C2, B2>, std::false_type, std::integral_constant<bool, B>)
{ return expand_error(constraints_not_satisfied<Derived, at<0, 2>>{}); }

template<std::size_t N>
static constexpr sized_true<N + 1> conj_c_t(concept_base, sized_true<N>, std::true_type)
{ return {}; }
template<std::size_t N>
static constexpr auto conj_c_t(concept_base, sized_true<N>, std::false_type)
{ return expand_error(constraints_not_satisfied<Derived, at<0, N + 1>>{}); }
template<std::size_t N>
static constexpr sized_true<N + 1> conj_t_c(sized_true<N>, concept_base, std::true_type)
{ return {}; }
template<std::size_t N>
static constexpr auto conj_t_c(sized_true<N>, concept_base, std::false_type)
{ return expand_error(constraints_not_satisfied<Derived, at<N, N + 1>>{}); }

template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
static constexpr auto conj_c_f(concept_base, constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> e, std::true_type)
{ return add_to_at_t<1, 1, decltype(e)>{}; }
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
static constexpr auto conj_c_f(concept_base, constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> e, std::false_type)
{ return expand_error(constraints_not_satisfied<Derived, at<0, N + 1>>{}); }


template<typename C2, typename B2>
static constexpr auto disj_c_c(concept_base, concept_base<C2, B2>, std::true_type)
{ return sized_true<2>{}; }
template<typename C2, typename B2>
static constexpr auto disj_c_c(concept_base, concept_base<C2, B2>, std::false_type) {
using E1 = decltype(expand_error(constraints_not_satisfied<Derived, at<0, 2>>{}));
using E2 = decltype(expand_error(constraints_not_satisfied<C2, at<1, 2>>{}));
return concat_error_t<E1, E2>{};
}

template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
static constexpr auto disj_c_f(concept_base, constraints_not_satisfied<Error, at<I, N>, ErrorInfo...>, std::true_type)
{ return sized_true<N + 1>{}; }
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
static constexpr auto disj_c_f(concept_base, constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> e, std::false_type) {
using E1 = decltype(expand_error(constraints_not_satisfied<Derived, at<0, N + 1>>{}));
using E2 = add_to_at_t<1, 1, decltype(e)>;
return concat_error_t<E1, E2>{};
}

template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
static constexpr auto disj_f_c(constraints_not_satisfied<Error, at<I, N>, ErrorInfo...>, concept_base, std::true_type)
{ return sized_true<N + 1> {}; }
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
static constexpr auto disj_f_c(constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> e, concept_base, std::false_type) {
using E1 = add_to_at_t<0, 1, decltype(e)>;
using E2 = decltype(expand_error(constraints_not_satisfied<Derived, at<N, N + 1>>{}));
return concat_error_t<E1, E2>{};
}

static constexpr auto negation(std::true_type)
{ return constraints_not_satisfied<Derived, at<0, 1>>{}; }
static constexpr auto negation(std::false_type)
{ return sized_true<1>{}; }

public:
using base = Base;

/// conjunctions

// concept && concept
template<typename C2, typename B2>
friend constexpr auto operator&&(concept_base x, concept_base<C2, B2> y) noexcept {
return conj_c_c(x, y, preview::bool_constant<Base::value>{}, preview::bool_constant<B2::value>{});
}

// concept && true
template<std::size_t N>
friend constexpr auto operator&&(concept_base, sized_true<N>) noexcept {
return conj_c_t(concept_base{}, sized_true<N>{}, preview::bool_constant<Base::value>{});
}

// true && concept
template<std::size_t N>
friend constexpr auto operator&&(sized_true<N>, concept_base) noexcept {
return conj_t_c(sized_true<N>{}, concept_base{}, preview::bool_constant<Base::value>{});
}

// concept && false
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
friend constexpr auto operator&&(concept_base, constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> e) noexcept {
return conj_c_f(concept_base{}, e, preview::bool_constant<Base::value>{});
}

// false && concept
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
friend constexpr auto operator&&(constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> e, concept_base) noexcept {
return add_to_at_t<0, 1, decltype(e)>{};
}

// concept && bool_constant
template<typename BoolConstant, std::enable_if_t<preview::conjunction<
derived_from_bool_constant<BoolConstant>,
preview::negation<is_concept<BoolConstant>>
>::value, int> = 0>
friend constexpr auto operator&&(concept_base, BoolConstant) noexcept {
return Derived{} && concept_base<BoolConstant, bool_constant<BoolConstant::value>>{};
}

// bool_constant && concept
template<typename BoolConstant, std::enable_if_t<preview::conjunction<
derived_from_bool_constant<BoolConstant>,
preview::negation<is_concept<BoolConstant>>
>::value, int> = 0>
friend constexpr auto operator&&(BoolConstant, concept_base) noexcept {
return concept_base<BoolConstant, bool_constant<BoolConstant::value>>{} && Derived{};
}

/// disjunctions

// concept || concept
template<typename C2, typename B2>
friend constexpr auto operator||(concept_base x, concept_base<C2, B2> y) noexcept {
return disj_c_c(x, y, disjunction<Base, B2>{});
}

// concept || true
template<std::size_t N>
friend constexpr auto operator||(concept_base, sized_true<N>) noexcept {
return sized_true<N + 1>{};
}

// true || concept
template<std::size_t N>
friend constexpr auto operator||(sized_true<N>, concept_base) noexcept {
return sized_true<N + 1>{};
}

// concept || false
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
friend constexpr auto operator||(concept_base x, constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> y) noexcept {
return disj_c_f(x, y, bool_constant<Base::value>{});
}

// false || concept
template<typename Error, std::size_t I, std::size_t N, typename... ErrorInfo>
friend constexpr auto operator||(constraints_not_satisfied<Error, at<I, N>, ErrorInfo...> x, concept_base y) noexcept {
return disj_f_c(x, y, bool_constant<Base::value>{});
}

// concept || bool_constant
template<typename BoolConstant, std::enable_if_t<preview::conjunction<
derived_from_bool_constant<BoolConstant>,
preview::negation<is_concept<BoolConstant>>
>::value, int> = 0>
friend constexpr auto operator||(concept_base, BoolConstant) noexcept {
return Derived{} || concept_base<BoolConstant, BoolConstant>{};
}

// bool_constant || concept
template<typename BoolConstant, std::enable_if_t<preview::conjunction<
derived_from_bool_constant<BoolConstant>,
preview::negation<is_concept<BoolConstant>>
>::value, int> = 0>
friend constexpr auto operator||(BoolConstant, concept_base) noexcept {
return concept_base<BoolConstant, BoolConstant>{} || Derived{};
}


/// negation
friend constexpr auto operator!(concept_base) noexcept { return negation(preview::bool_constant<Base::value>{}); }
};

} // namespace concepts
} // namespace preview

#define PREVIEW_DEFINE_CONCEPT_TYPE(name, name_typed, ...) \
struct name : preview::concepts::concept_base<name_typed, decltype(__VA_ARGS__)> {}

#define PREVIEW_DEFINE_CONCEPT_UNDER(ns, Template, name, name_typed, ...) \
namespace ns { \
Template \
struct name \
: preview::concepts::concept_base<name_typed, decltype(__VA_ARGS__)> {}; \
} \
Template \
PREVIEW_INLINE_VARIABLE constexpr ns::name_typed name

#endif // PREVIEW_CONCEPTS_V2_DETAIL_CONCEPT_BASE_H_
31 changes: 31 additions & 0 deletions include/preview/__concepts_v2/constructible_from.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef PREVIEW_CONCEPTS_V2_CONSTRUCTIBLE_FROM_H_
#define PREVIEW_CONCEPTS_V2_CONSTRUCTIBLE_FROM_H_

#include <type_traits>

#include "preview/__concepts_v2/concept_base.h"
#include "preview/__concepts_v2/destructible.h"

namespace preview {

#if defined(PREVIEW_USE_LEGACY_CONCEPT)

template<typename T, typename... Args>
struct constructible_from_c : concepts::concept_base<constructible_from_c<T, Args...>, decltype(
destructible<T> && std::is_constructible<T, Args...>{}
)> {};

template<typename T, typename... Args>
PREVIEW_INLINE_VARIABLE constexpr constructible_from_c<T, Args...> constructible_from{};

#else

template<typename T, typename... Args>
concept constructible_from = destructible<T> && std::is_constructible_v<T, Args...>;

#endif

} // namespace preview


#endif // PREVIEW_CONCEPTS_V2_CONSTRUCTIBLE_FROM_H_
42 changes: 42 additions & 0 deletions include/preview/__concepts_v2/convertible_to.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Created by yonggyulee on 2024. 10. 20.
//

#ifndef PREVIEW_INCLUDE_PREVIEW___CONCEPTS_V2_CONVERTIBLE_TO_H_
#define PREVIEW_INCLUDE_PREVIEW___CONCEPTS_V2_CONVERTIBLE_TO_H_

#include <type_traits>

#include "preview/__concepts_v2/concept_base.h"
#include "preview/__type_traits/conjunction.h"
#include "preview/__type_traits/is_explicitly_convertible.h"

namespace preview {

#if defined(PREVIEW_USE_LEGACY_CONCEPT)

template<typename From, typename To>
struct convertible_to_c : concepts::concept_base<convertible_to_c<From, To>,
conjunction<
std::is_convertible<From, To>,
is_explicitly_convertible<From, To>
>
>{};

template<typename From, typename To>
PREVIEW_INLINE_VARIABLE convertible_to_c<From, To> convertible_to;

#else

template<typename From, typename To>
concept convertible_to =
std::is_convertible_v<From, To> &&
requires {
static_cast<To>(std::declval<From>());
};

#endif

} // namespace preview

#endif // PREVIEW_INCLUDE_PREVIEW___CONCEPTS_V2_CONVERTIBLE_TO_H_
39 changes: 39 additions & 0 deletions include/preview/__concepts_v2/derived_from.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Created by yonggyulee on 2024. 10. 20.
//

#ifndef PREVIEW_CONCEPTS_V2_DERIVED_FROM_H_
#define PREVIEW_CONCEPTS_V2_DERIVED_FROM_H_

#include <type_traits>

#include "preview/__concepts_v2/concept_base.h"
#include "preview/__type_traits/conjunction.h"

namespace preview {

#if defined(PREVIEW_USE_LEGACY_CONCEPT)

template<typename Derived, typename Base>
struct derived_from_c : concepts::concept_base<derived_from_c<Derived, Base>,
conjunction<
std::is_base_of<Base, Derived>,
std::is_convertible<const volatile Derived*, const volatile Base*>
>
> {};

template<typename Derived, typename Base>
PREVIEW_INLINE_VARIABLE constexpr derived_from_c<Derived, Base> derived_from;

#else

template<typename Derived, typename Base>
concept derived_from =
std::is_base_of_v<Base, Derived> &&
std::is_convertible_v<const volatile Derived*, const volatile Base*>;

#endif

} // namespace preview

#endif // PREVIEW_CONCEPTS_V2_DERIVED_FROM_H_
Loading
Loading