AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
fields_count.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14#include <cstdint>
   15#include <type_traits>
   16#include <limits>
   17#include "AUI/Traits/unsafe_declval.h"
   18
   19namespace aui::reflect::detail {
   20
   21// based on ideas found in Boost.PFR.
   22// credits: Antony Polukhin
   23
   27struct lref_constructing {
   28    std::size_t ignore;
   29    template <class Type>
   30    constexpr operator Type&() const&& noexcept {
   31        return aui::unsafe_declval<Type&>();
   32    }
   33
   34    template <class Type>
   35    constexpr operator Type&() const& noexcept {
   36        return aui::unsafe_declval<Type&>();
   37    }
   38};
   39
   43struct rref_constructing {
   44    std::size_t ignore;
   45    template <class Type>
   46    /*constexpr*/
   47    operator Type() const&& noexcept {   // allows initialization of rvalue reference fields and move-only types
   48        return aui::unsafe_declval<Type>();
   49    }
   50};
   51
   55template <
   56    class T, std::size_t... I, class /*Enable*/ = typename std::enable_if<std::is_copy_constructible<T>::value>::type>
   57constexpr auto enable_if_constructible(std::index_sequence<I...>) noexcept -> typename std::add_pointer<decltype(T {
   58  lref_constructing { I }... })>::type;
   59
   60template <
   61    class T, std::size_t... I, class /*Enable*/ = typename std::enable_if<!std::is_copy_constructible<T>::value>::type>
   62constexpr auto enable_if_constructible(std::index_sequence<I...>) noexcept -> typename std::add_pointer<decltype(T {
   63  rref_constructing { I }... })>::type;
   64
   65template <
   66    class T, std::size_t N,
   67    class /*Enable*/ = decltype(enable_if_constructible<T>(std::make_integer_sequence<std::size_t, N>()))>
   68using enable_if_constructible_t = std::size_t;
   69
   75template <class T, std::size_t N>
   76constexpr auto detect_fields_count_greedy_remember(long) noexcept -> detail::enable_if_constructible_t<T, N> {
   77    return N;
   78}
   79
   80template <class T, std::size_t N>
   81constexpr std::size_t detect_fields_count_greedy_remember(int) noexcept {
   82    return 0;
   83}
   84
   85using multi_element_range = std::false_type;
   86using one_element_range = std::true_type;
   87
   88template <std::size_t Begin, std::size_t Last>
   89using is_one_element_range = std::integral_constant<bool, Begin == Last>;
   90
   91template <class T, std::size_t Begin, std::size_t Last>
   92constexpr std::size_t detect_fields_count_greedy(detail::one_element_range) noexcept {
   93    static_assert(
   94        Begin == Last,
   95        "====================> aui::reflect: internal logic error. Please report this issue to the github along with the "
   96        "structure you're reflecting.");
   97    return detail::detect_fields_count_greedy_remember<T, Begin>(1L);
   98}
   99
  100template <class T, std::size_t Begin, std::size_t Last>
  101constexpr std::size_t detect_fields_count_greedy(detail::multi_element_range) noexcept {
  102    constexpr std::size_t middle = Begin + (Last - Begin) / 2;
  103    constexpr std::size_t fields_count_big_range =
  104        detail::detect_fields_count_greedy<T, middle + 1, Last>(detail::is_one_element_range<middle + 1, Last> {});
  105
  106    constexpr std::size_t small_range_begin = (fields_count_big_range ? 0 : Begin);
  107    constexpr std::size_t small_range_last = (fields_count_big_range ? 0 : middle);
  108    constexpr std::size_t fields_count_small_range = detail::detect_fields_count_greedy<
  109        T, small_range_begin, small_range_last>(detail::is_one_element_range<small_range_begin, small_range_last> {});
  110    return fields_count_big_range ? fields_count_big_range : fields_count_small_range;
  111}
  112
  122template <class T, std::size_t Begin, std::size_t Middle>
  123constexpr std::size_t detect_fields_count(detail::one_element_range, long) noexcept {
  124    static_assert(
  125        Begin == Middle,
  126        "====================> aui::reflect: internal logic error. Please report this issue to the github along with the "
  127        "structure you're reflecting.");
  128    return Begin;
  129}
  130
  131template <class T, std::size_t Begin, std::size_t Middle>
  132constexpr std::size_t detect_fields_count(detail::multi_element_range, int) noexcept;
  133
  134template <class T, std::size_t Begin, std::size_t Middle>
  135constexpr auto
  136detect_fields_count(detail::multi_element_range, long) noexcept -> detail::enable_if_constructible_t<T, Middle> {
  137    constexpr std::size_t next_v = Middle + (Middle - Begin + 1) / 2;
  138    return detail::detect_fields_count<T, Middle, next_v>(detail::is_one_element_range<Middle, next_v> {}, 1L);
  139}
  140
  141template <class T, std::size_t Begin, std::size_t Middle>
  142constexpr std::size_t detect_fields_count(detail::multi_element_range, int) noexcept {
  143    constexpr std::size_t next_v = Begin + (Middle - Begin) / 2;
  144    return detail::detect_fields_count<T, Begin, next_v>(detail::is_one_element_range<Begin, next_v> {}, 1L);
  145}
  146
  153template <class T, std::size_t N>
  154constexpr auto detect_fields_count_dispatch(std::integral_constant<std::size_t, N>, long, long) noexcept ->
  155    typename std::enable_if<std::is_array<T>::value, std::size_t>::type {
  156    return sizeof(T) / sizeof(typename std::remove_all_extents<T>::type);
  157}
  158
  159template <class T, std::size_t N>
  160constexpr auto
  161detect_fields_count_dispatch(std::integral_constant<std::size_t, N>, long, int) noexcept -> decltype(sizeof(T {})) {
  162    constexpr std::size_t middle = N / 2 + 1;
  163    return detail::detect_fields_count<T, 0, middle>(detail::multi_element_range {}, 1L);
  164}
  165
  166template <class T, std::size_t N>
  167constexpr std::size_t detect_fields_count_dispatch(std::integral_constant<std::size_t, N>, int, int) noexcept {
  168    // Detects the maximum number of default-constructible members in struct type T.
  169    // This is necessary because T, being non-default aggregate initializable,
  170    // has at least one member that isn't default constructible.
  171    //
  172    // We must manually check each constructor up to N parameters and return
  173    // the largest one that doesn't result in a compile-time error.
  174    return detail::detect_fields_count_greedy<T, 0, N>(detail::multi_element_range {});
  175}
  176
  180template <class Derived, class U>
  181constexpr bool static_assert_non_inherited() noexcept {
  182    static_assert(
  183        !std::is_base_of<U, Derived>::value, "====================> aui::reflect: inherited types are not supported.");
  184    return true;
  185}
  186
  187template <class Derived>
  188struct lref_base_asserting {
  189    template <class Type>
  190    constexpr operator Type&() const&&   // tweak for template_unconstrained.cpp like cases
  191        noexcept(detail::static_assert_non_inherited<Derived, Type>())   // force the computation of assert function
  192    {
  193        return aui::unsafe_declval<Type&>();
  194    }
  195
  196    template <class Type>
  197    constexpr operator Type&() const&                                    // tweak for optional_chrono.cpp like cases
  198        noexcept(detail::static_assert_non_inherited<Derived, Type>())   // force the computation of assert function
  199    {
  200        return aui::unsafe_declval<Type&>();
  201    }
  202};
  203
  204template <class Derived>
  205struct rref_base_asserting {
  206    template <class Type>
  207    /*constexpr*/ operator Type() const&&   // Allows initialization of rvalue reference fields and move-only types
  208        noexcept(detail::static_assert_non_inherited<Derived, Type>())   // force the computation of assert function
  209    {
  210        return aui::unsafe_declval<Type>();
  211    }
  212};
  213
  214template <
  215    class T, std::size_t I0, std::size_t... I,
  216    class /*Enable*/ = typename std::enable_if<std::is_copy_constructible<T>::value>::type>
  217constexpr auto
  218assert_first_is_not_base(std::index_sequence<I0, I...>) noexcept -> typename std::add_pointer<decltype(T {
  219  lref_base_asserting<T> {}, lref_constructing { I }... })>::type {
  220    return nullptr;
  221}
  222
  223template <
  224    class T, std::size_t I0, std::size_t... I,
  225    class /*Enable*/ = typename std::enable_if<!std::is_copy_constructible<T>::value>::type>
  226constexpr auto
  227assert_first_is_not_base(std::index_sequence<I0, I...>) noexcept -> typename std::add_pointer<decltype(T {
  228  rref_base_asserting<T> {}, rref_constructing { I }... })>::type {
  229    return nullptr;
  230}
  231
  232template <class T>
  233constexpr void* assert_first_is_not_base(std::index_sequence<>) noexcept {
  234    return nullptr;
  235}
  236
  246template <class Clazz>
  247constexpr std::size_t fields_count() noexcept {
  248    using type = std::remove_cv_t<Clazz>;
  249
  250    static_assert(
  251        !std::is_reference<type>::value, "====================> aui::reflect: attempt to get fields count on a reference.");
  252
  253    static_assert(
  254        !std::is_polymorphic<type>::value,
  255        "====================> aui::reflect: type must have no virtual function, because otherwise it is not aggregate "
  256        "initializable.");
  257
  258#ifdef __cpp_lib_is_aggregate
  259    static_assert(
  260        std::is_aggregate<type>::value || std::is_scalar<type>::value,
  261        "====================> aui::reflect: type must be aggregate initializable.");
  262#endif
  263
  264#if defined(_MSC_VER) && (_MSC_VER <= 1920)
  265    // workaround for msvc compilers. versions <= 1920 have a limit of max 1024 elements in template parameter pack
  266    constexpr std::size_t max_fields_count = (sizeof(type) * CHAR_BIT >= 1024 ? 1024 : sizeof(type) * CHAR_BIT);
  267#else
  268    constexpr std::size_t max_fields_count = (sizeof(type) * CHAR_BIT);   // we multiply by CHAR_BIT because the type
  269                                                                          // may have bitfields in T
  270#endif
  271
  272    constexpr std::size_t result =
  273        detail::detect_fields_count_dispatch<type>(std::integral_constant<std::size_t, max_fields_count> {}, 1L, 1L);
  274
  275    detail::assert_first_is_not_base<type>(std::make_integer_sequence<std::size_t, result>());
  276
  277#ifndef __cpp_lib_is_aggregate
  278    static_assert(
  279        is_aggregate_initializable_n<type, result>::value,
  280        "====================> aui::reflect: types with user specified constructors (non-aggregate initializable types) "
  281        "are not supported.");
  282#endif
  283
  284    static_assert(
  285        result != 0 || std::is_empty<type>::value || std::is_fundamental<type>::value || std::is_reference<type>::value,
  286        "====================> aui::reflect: if there's no other failed static asserts then something went wrong. Please "
  287        "report this issue to the github along with the structure you're reflecting.");
  288
  289    return result;
  290}
  291}   // namespace aui::reflect::detail