Migrate to C++20 modules

This commit is contained in:
sha512sum 2024-07-31 02:19:28 +00:00
parent 16889ef3b0
commit 6367dccf19
35 changed files with 623 additions and 604 deletions

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
.cache/
.ninja_deps
.ninja_log
CMakeCache.txt
CMakeFiles/
CPM_modules/
CTestTestfile.cmake
_deps/
build.ninja
cmake/CPM.cmake
cmake_install.cmake
compile_commands.json
compile_commands.json.tmp9cdd8
cpm-package-lock.cmake
examples/output/
install_manifest.txt
lib__cmake_cxx23.a
utemplConfig.cmake
utemplConfigVersion.cmake
utempl_tests
utempl_tests\[1\]_include.cmake
utempl_tests\[1\]_tests.cmake
libutempl_shared.so
libutempl_static.so

View file

@ -1,12 +1,26 @@
cmake_minimum_required(VERSION 3.27) cmake_minimum_required(VERSION 3.30)
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD
"0e5b6991-d74f-4b3d-a41c-cf096e0b2508")
project(utempl project(utempl
VERSION 0.1) VERSION 0.1
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) LANGUAGES CXX)
set(CMAKE_CXX_STANDART 23)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CXX_EXTENSIONS NO) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_MODULE_STD 1)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(FMT_MODULE ON)
set(BOOST_INCLUDE_LIBRARIES "pfr") set(BOOST_INCLUDE_LIBRARIES "pfr")
option(CPM_USE_LOCAL_PACKAGES "Use local packages" ON) option(CPM_USE_LOCAL_PACKAGES "Use local packages" ON)
option(BUILD_SHARED_LIBS "Build shared libraries (.so)" ON)
option(BUILD_STATIC_LIBS "Build static libraries (.a)" OFF)
file(GLOB_RECURSE SOURCES "src/*.cpp")
set_target_properties(__cmake_cxx23 PROPERTIES CXX_EXTENSIONS OFF)
file( file(
@ -26,38 +40,71 @@ CPMAddPackage(
OPTIONS "BOOST_SKIP_INSTALL_RULES OFF" OPTIONS "BOOST_SKIP_INSTALL_RULES OFF"
) )
CPMAddPackage("gh:fmtlib/fmt#10.2.1") set(TMP ${CPM_USE_LOCAL_PACKAGES})
set(CPM_USE_LOCAL_PACKAGES OFF)
CPMAddPackage(
NAME fmt
URL
"https://github.com/linuxnyasha/fmt_import/archive/refs/heads/linuxnyasha-patch-1.zip"
# Fixes for fmt module and constexpr fmt::formatted_size
OPTIONS "CMAKE_CXX_FLAGS -DFMT_ATTACH_TO_GLOBAL_MODULE"
)
set(CPM_USE_LOCAL_PACKAGES ${TMP})
function(configure target)
target_compile_features(${target} INTERFACE cxx_std_23)
target_include_directories(${target} INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_sources(${target} PUBLIC FILE_SET ${target} TYPE CXX_MODULES
FILES ${SOURCES})
install(TARGETS ${target}
EXPORT utemplTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
FILE_SET ${target} DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
if(TARGET Boost::pfr)
target_link_libraries(${target} PUBLIC fmt::fmt
Boost::pfr)
else()
find_package(Boost 1.85.0 REQUIRED)
target_link_libraries(${target} PUBLIC fmt::fmt
${Boost_LIBRARIES})
endif()
endfunction(configure)
cmake_policy(SET CMP0079 NEW) cmake_policy(SET CMP0079 NEW)
include(GNUInstallDirs) include(GNUInstallDirs)
add_library(utempl INTERFACE)
add_library(utempl::utempl ALIAS utempl) if(BUILD_SHARED_LIBS)
add_library(utempl_shared)
target_include_directories( add_library(utempl::utempl ALIAS utempl_shared)
utempl configure(utempl_shared)
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
if(TARGET Boost::pfr)
target_link_libraries(utempl INTERFACE fmt::fmt-header-only Boost::pfr)
else()
find_package(Boost 1.85.0 REQUIRED)
target_link_libraries(utempl INTERFACE fmt::fmt-header-only ${Boost_LIBRARIES})
endif() endif()
target_compile_features(utempl INTERFACE cxx_std_23)
install(TARGETS utempl if(BUILD_STATIC_LIBS)
EXPORT utemplTargets add_library(utempl_static)
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} if(NOT BUILD_SHARED_LIBS)
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} add_library(utempl ALIAS utempl_static)
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} add_library(utempl::utempl ALIAS utempl_static)
) endif()
configure(utempl_static)
endif()
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
write_basic_package_version_file("utemplConfigVersion.cmake" write_basic_package_version_file("utemplConfigVersion.cmake"
@ -87,8 +134,10 @@ if(ENABLE_TESTS)
find_package(GTest REQUIRED) find_package(GTest REQUIRED)
enable_testing() enable_testing()
file(GLOB SOURCES tests/* tests/*/* tests/*/*/*) file(GLOB SOURCES tests/* tests/*/* tests/*/*/*)
add_executable(utempl_tests ${SOURCES}) add_executable(utempl_tests)
target_link_libraries(utempl_tests GTest::gtest_main utempl) target_sources(utempl_tests PUBLIC FILE_SET utempl_tests TYPE CXX_MODULES
FILES ${SOURCES})
target_link_libraries(utempl_tests GTest::gtest_main utempl::utempl)
set_property(TARGET utempl_tests PROPERTY CXX_STANDARD 23) set_property(TARGET utempl_tests PROPERTY CXX_STANDARD 23)
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(utempl_tests) gtest_discover_tests(utempl_tests)
@ -98,7 +147,7 @@ if(ENABLE_EXAMPLES)
foreach(EXAMPLE_SRC ${EXAMPLES_SRC}) foreach(EXAMPLE_SRC ${EXAMPLES_SRC})
get_filename_component(EXAMPLE_NAME ${EXAMPLE_SRC} NAME_WE) get_filename_component(EXAMPLE_NAME ${EXAMPLE_SRC} NAME_WE)
add_executable(${EXAMPLE_NAME} ${EXAMPLE_SRC}) add_executable(${EXAMPLE_NAME} ${EXAMPLE_SRC})
target_link_libraries(${EXAMPLE_NAME} utempl) target_link_libraries(${EXAMPLE_NAME} utempl::utempl)
set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD 23) set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD 23)
set_target_properties(${EXAMPLE_NAME} PROPERTIES set_target_properties(${EXAMPLE_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/examples/output") RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/examples/output")

View file

@ -1,5 +1,7 @@
#include <utempl/attributes.hpp> import std;
#include <string> import utempl.attributes;
import utempl.type_list;
#include <utempl/macro.hpp>
template <typename T> template <typename T>
struct AttributeData { struct AttributeData {
@ -7,25 +9,21 @@ struct AttributeData {
constexpr auto operator==(const AttributeData<T>&) const -> bool = default; constexpr auto operator==(const AttributeData<T>&) const -> bool = default;
}; };
#define MY_ATTRIBUTE(type, ...) GENERIC_ATTRIBUTE(AttributeData<type>{__VA_ARGS__}) // NOLINT
#define MY_ATTRIBUTE(type, ...) GENERIC_ATTRIBUTE(AttributeData<type>{__VA_ARGS__}) // clang-format off
ATTRIBUTE_STRUCT(SomeStruct, ATTRIBUTE_STRUCT(SomeStruct,
MY_ATTRIBUTE(int, .value = 2); MY_ATTRIBUTE(int, .value = 2);
int field; int field;
SKIP_ATTRIBUTE(); SKIP_ATTRIBUTE();
int field2; int field2;
MY_ATTRIBUTE(std::string, .value = "HEY!") MY_ATTRIBUTE(std::string, .value = "HEY!");
std::string field3; std::string field3;
); );
// clang-format on
static_assert(utempl::GetAttributes<SomeStruct>() static_assert(utempl::GetAttributes<SomeStruct>() ==
== utempl::Tuple{ utempl::Tuple{AttributeData<int>{.value = 2}, utempl::NoInfo{}, AttributeData<std::string>{.value = "HEY!"}});
AttributeData<int>{.value = 2},
utempl::NoInfo{},
AttributeData<std::string>{.value = "HEY!"}});
struct SomeOtherStruct { struct SomeOtherStruct {
static_assert(utempl::OpenStruct<SomeOtherStruct>()); static_assert(utempl::OpenStruct<SomeOtherStruct>());
@ -35,13 +33,6 @@ struct SomeOtherStruct {
static_assert(utempl::CloseStruct()); static_assert(utempl::CloseStruct());
}; };
static_assert(utempl::GetAttributes<SomeOtherStruct>() static_assert(utempl::GetAttributes<SomeOtherStruct>() == utempl::Tuple{utempl::kTypeList<int>, utempl::NoInfo{}, utempl::kTypeList<void>});
== utempl::Tuple{
utempl::kTypeList<int>,
utempl::NoInfo{},
utempl::kTypeList<void>});
auto main() -> int {}; auto main() -> int {};

View file

@ -1,9 +1,9 @@
#include <utempl/utils.hpp> import utempl.utils;
#include <iostream> import std;
auto main() -> int { auto main() -> int {
constexpr auto f = utempl::Curry([](auto... args){ constexpr auto f = utempl::Curry([](auto... args) {
return (0 + ... + args); return (0 + ... + args);
}); });
std::cout << f(1)(2)(3) << std::endl; // Call on cast to return type std::cout << f(1)(2)(3) << std::endl; // Call on cast to return type
}; };

View file

@ -1,5 +1,6 @@
#include <utempl/go_interface.hpp> import fmt;
#include <fmt/core.h> import utempl.go_interface;
import std;
struct SomeInterfaceImpl { struct SomeInterfaceImpl {
int a; int a;
@ -7,15 +8,14 @@ struct SomeInterfaceImpl {
}; };
using SomeInterface = utempl::GoInterface<SomeInterfaceImpl>; using SomeInterface = utempl::GoInterface<SomeInterfaceImpl>;
struct SomeStruct { struct SomeStruct {
short a; std::int16_t a;
short b; std::int16_t b;
}; };
inline auto Func(SomeInterface arg) { inline auto Func(SomeInterface arg) {
fmt::print("{} {}\n", arg.a, arg.b); fmt::print("{} {}\n", arg.a, arg.b);
}; };
auto main() -> int { auto main() -> int {
Func(SomeStruct{42, 300}); Func(SomeStruct{42, 300}); // NOLINT
}; };

View file

@ -1,4 +1,6 @@
#include <utempl/utils.hpp> import utempl.utils;
import utempl.tuple;
import std;
auto main() -> int { auto main() -> int {
constexpr auto value = utempl::LeftFold( constexpr auto value = utempl::LeftFold(

View file

@ -1,4 +1,5 @@
#include <utempl/utils.hpp> import utempl.utils;
import std;
auto main() -> int { auto main() -> int {
constexpr auto arr = utempl::TupleMaker<std::array<void*, 0>>::Make(); constexpr auto arr = utempl::TupleMaker<std::array<void*, 0>>::Make();

View file

@ -1,13 +1,13 @@
#include <utempl/menu.hpp> import utempl.menu;
#include <iostream> import std;
auto main() -> int { auto main() -> int {
utempl::menu::Menu{} utempl::menu::Menu{}
.With<{"This is 0"}>([]{ .With<{"This is 0"}>([] {
std::cout << "You entered 0" << std::endl; std::cout << "You entered 0" << std::endl;
}) })
.With<{"Some Long", "S"}>([]{ .With<{"Some Long", "S"}>([] {
std::cout << "It aligns the output to the longest element" << std::endl; std::cout << "It aligns the output to the longest element" << std::endl;
}) })
.Run<"[{0}]{2} - |{1}|\n">(); .Run<"[{0}]{2} - |{1}|\n">();
}; };

View file

@ -1,6 +1,5 @@
#include <utempl/meta_info.hpp> import utempl.meta_info;
#include <array> import std;
#include <type_traits>
auto main() -> int { auto main() -> int {
constexpr std::array types = {utempl::kTypeId<int>, utempl::kTypeId<void>}; constexpr std::array types = {utempl::kTypeId<int>, utempl::kTypeId<void>};

View file

@ -1,13 +1,9 @@
#include <utempl/overloaded.hpp> import utempl.overloaded;
#include <iostream> import std;
auto main() -> int { auto main() -> int {
utempl::Overloaded( utempl::Overloaded([](auto&& arg) {},
[](auto&& arg){ [](int arg) {
}, std::cout << arg << std::endl;
[](int arg) { })(42);
std::cout << arg << std::endl;
}
)(42);
}; };

View file

@ -1,4 +1,4 @@
#include <utempl/utils.hpp> import utempl;
struct Container { struct Container {
float data{}; float data{};
@ -6,15 +6,17 @@ struct Container {
auto main() -> int { auto main() -> int {
using namespace utempl; using namespace utempl;
constexpr auto value = Tuple{1, 2, 3, 4, 5, 6} constexpr auto value = Tuple{1, 2, 3, 4, 5, 6} | Take<5>() | Map([](int arg) {
| Take<5>() return arg + 1;
| Map([](int arg) {return arg + 1;}) }) |
| Map([](int arg) -> float {return arg;}) Map([](int arg) -> float {
| Map([](float arg) -> Container {return {.data = arg};}) return arg;
| Reverse() }) |
| Take<3>() Map([](float arg) -> Container {
| Reduce(0.f, [](auto accumulator, Container arg) -> float { return {.data = arg};
return accumulator + arg.data; }) |
}); // Lazy evavalue Reverse() | Take<3>() | Reduce(0.f, [](auto accumulator, Container arg) -> float {
return accumulator + arg.data;
}); // Lazy evavalue
static_assert(value == 15.0f); static_assert(value == 15.0f);
}; };

View file

@ -1,9 +1,10 @@
#include <utempl/utils.hpp> import utempl.utils;
#include <iostream> import utempl.tuple;
import std;
auto main() -> int { auto main() -> int {
auto tuple = Reverse(utempl::Tuple{4, 3, 2, 1}); auto tuple = Reverse(utempl::Tuple{4, 3, 2, 1});
ForEach(tuple, [](auto arg){ ForEach(tuple, [](auto arg) {
std::cout << arg << std::endl; std::cout << arg << std::endl;
}); });
}; };

View file

@ -1,4 +1,6 @@
#include <utempl/utils.hpp> import utempl.utils;
import utempl.tuple;
import std;
auto main() -> int { auto main() -> int {
static_assert(utempl::Switch<int>(utempl::Tuple{2, 1, 0}, utempl::Tuple{0, 1, 2}, 0, [](int value) { static_assert(utempl::Switch<int>(utempl::Tuple{2, 1, 0}, utempl::Tuple{0, 1, 2}, 0, [](int value) {

View file

@ -1,9 +1,9 @@
#include <utempl/utils.hpp> import utempl.utils;
#include <iostream> import std;
auto main() -> int { auto main() -> int {
std::size_t i = 0; std::size_t i = 0;
utempl::Times<10>([&](){ utempl::Times<10>([&]() {
++i; ++i;
std::cout << i << std::endl; std::cout << i << std::endl;
}); });

View file

@ -1,23 +1,24 @@
#include <utempl/tuple.hpp>
#include <utempl/utils.hpp>
#include <iostream>
#include <cassert> #include <cassert>
import utempl.utils;
import utempl.tuple;
import utempl.overloaded;
import std;
auto main() -> int { auto main() -> int {
using utempl::literals::operator""_c; using utempl::literals::operator""_c;
constexpr utempl::Tuple tuple = {42, 3.141500, "Hello World"}; constexpr utempl::Tuple tuple = {42, 3.141500, "Hello World"};
utempl::Tuple<int> tuple2{{}}; utempl::Tuple<int> tuple2{{}};
std::ignore = tuple2; std::ignore = tuple2;
std::cout << utempl::kTupleSize<decltype(tuple)> << std::endl; // Get tuple size std::cout << utempl::kTupleSize<decltype(tuple)> << std::endl; // Get tuple size
std::cout << tuple[0_c] << std::endl; // Get element using [] with literal std::cout << tuple[0_c] << std::endl; // Get element using [] with literal
auto newTuple = Transform(tuple, utempl::Overloaded( auto newTuple = Transform(tuple,
[](auto arg){ utempl::Overloaded(
return std::to_string(arg); [](auto arg) {
}, return std::to_string(arg);
[](const char* arg) { },
return std::string(arg); [](const char* arg) {
} return std::string(arg);
)); }));
auto flag = newTuple == utempl::Tuple<std::string, std::string, std::string>{"42", "3.141500", "Hello World"}; auto flag = newTuple == utempl::Tuple<std::string, std::string, std::string>{"42", "3.141500", "Hello World"};
assert(flag); assert(flag);
}; };

View file

@ -1,103 +0,0 @@
#include <utempl/loopholes/counter.hpp>
#include <utempl/meta_info.hpp>
#include <utempl/utils.hpp>
namespace utempl {
namespace impl {
struct AttributesTag {};
template <typename T>
struct AttributesCounterTag {};
} // namespace impl
template <typename T, typename..., auto f = [] {}, auto = AddTypeToTag<impl::AttributesTag, T, decltype(f)>()>
consteval auto OpenStruct() -> bool {
return true;
};
template <typename...,
auto f = [] {},
auto I = loopholes::CountValue<impl::AttributesTag, decltype(f)>(),
auto II = (I >= 2) ? I - 2 : I - 1,
typename T = decltype(Magic(loopholes::Getter<MetaInfoKey<II, impl::AttributesTag>{}>{}))::Type,
auto = AddTypeToTag<impl::AttributesTag, T, decltype(f)>()>
consteval auto CloseStruct() -> bool {
return true;
};
struct NoInfo {
consteval auto operator==(const NoInfo&) const -> bool = default;
};
namespace impl {
template <typename... Ts>
struct FieldAttributeData {
using Type = TypeList<Ts...>;
};
template <>
struct FieldAttributeData<> {
using Type = NoInfo;
};
template <typename T,
typename... Ts,
auto f = [] {},
typename Current = decltype(GetCurrentTagType<impl::AttributesTag, decltype(f)>())::Type,
auto = AddTypeToTag<impl::AttributesCounterTag<Current>, typename FieldAttributeData<Ts...>::Type, decltype(f)>()>
consteval auto FieldAttribute() -> T;
} // namespace impl
template <typename... Ts>
using FieldAttribute = decltype(impl::FieldAttribute<Ts...>());
#define ATTRIBUTE_STRUCT(name, ...) /* NOLINT */ \
\
struct name { \
static_assert(::utempl::OpenStruct<name>()); \
template <std::size_t N> \
static consteval auto GetAttribute(); \
__VA_ARGS__ \
static_assert(::utempl::CloseStruct()); \
}
#define GENERIC_ATTRIBUTE(value) /* NOLINT */ \
template <> \
consteval auto GetAttribute<::utempl::loopholes::Counter< \
::utempl::impl::AttributesCounterTag<decltype(::utempl::GetCurrentTagType<::utempl::impl::AttributesTag, decltype([] {})>())::Type>, \
decltype([] {})>()>() { \
return value; \
}
#define SKIP_ATTRIBUTE() /* NOLINT */ GENERIC_ATTRIBUTE(::utempl::NoInfo{})
template <typename T, auto f = [] {}, bool R = (loopholes::CountValue<impl::AttributesCounterTag<T>, decltype(f)>() > 0)>
concept HasAttributes = R;
template <typename T>
concept HasMacroAttributes = requires { T::template GetAttribute<0>(); };
template <HasAttributes T>
consteval auto GetAttributes()
requires HasMacroAttributes<T>
{
constexpr auto I = loopholes::CountValue<impl::AttributesCounterTag<T>>();
return [](auto... is) {
return Tuple{T::template GetAttribute<is>()...};
} | kSeq<I>;
};
template <typename T>
consteval auto GetAttributes() {
constexpr auto I = loopholes::CountValue<impl::AttributesCounterTag<T>>();
return [](auto... is) {
return utempl::Tuple{typename decltype(Magic(loopholes::Getter<MetaInfoKey<is, impl::AttributesCounterTag<T>>{}>{}))::Type{}...};
} | kSeq<I>;
};
} // namespace utempl

20
include/utempl/macro.hpp Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#define ATTRIBUTE_STRUCT(name, ...) /* NOLINT */ \
\
struct name { \
static_assert(::utempl::OpenStruct<name>()); \
template <std::size_t N> \
static consteval auto GetAttribute(); \
__VA_ARGS__ \
static_assert(::utempl::CloseStruct()); \
}
#define GENERIC_ATTRIBUTE(value) /* NOLINT */ \
template <> \
consteval auto GetAttribute<::utempl::loopholes::Counter< \
::utempl::impl::AttributesCounterTag<decltype(::utempl::GetCurrentTagType<::utempl::impl::AttributesTag, decltype([] {})>())::Type>, \
decltype([] {})>()>() { \
return value; \
}
#define SKIP_ATTRIBUTE() /* NOLINT */ GENERIC_ATTRIBUTE(::utempl::NoInfo{})

View file

@ -1,40 +0,0 @@
#pragma once
namespace utempl {
template <typename T>
struct ReferenceWrapper;
template <typename T>
struct ReferenceWrapper<T&&> {
constexpr auto operator*() -> T& {
return this->value;
};
constexpr auto operator->() -> T* {
return &this->value;
};
T&& value;
};
template <typename T>
struct ReferenceWrapper<const T&> {
constexpr auto operator*() -> const T& {
return this->value;
};
constexpr auto operator->() -> const T* {
return &this->value;
};
const T& value;
};
template <typename T>
struct ReferenceWrapper<T&> {
constexpr auto operator*() -> T& {
return this->value;
};
constexpr auto operator->() -> T* {
return &this->value;
};
T& value;
};
} // namespace utempl

82
src/attributes.cpp Normal file
View file

@ -0,0 +1,82 @@
export module utempl.attributes;
export import utempl.meta_info;
export import utempl.tuple;
export import utempl.loopholes.counter;
import utempl.utils;
import utempl.type_list;
namespace utempl {
namespace impl {
export struct AttributesTag {};
export template <typename T>
struct AttributesCounterTag {};
} // namespace impl
export template <typename T, typename..., auto f = [] {}, auto = AddTypeToTag<impl::AttributesTag, T, decltype(f)>()>
consteval auto OpenStruct() -> bool {
return true;
};
export template <typename...,
auto f = [] {},
auto I = loopholes::CountValue<impl::AttributesTag, decltype(f)>(),
auto II = (I >= 2) ? I - 2 : I - 1,
typename T = decltype(Magic(loopholes::Getter<MetaInfoKey<II, impl::AttributesTag>{}>{}))::Type,
auto = AddTypeToTag<impl::AttributesTag, T, decltype(f)>()>
consteval auto CloseStruct() -> bool {
return true;
};
export struct NoInfo {
consteval auto operator==(const NoInfo&) const -> bool = default;
};
template <typename... Ts>
struct FieldAttributeData {
using Type = TypeList<Ts...>;
};
template <>
struct FieldAttributeData<> {
using Type = NoInfo;
};
template <typename T,
typename... Ts,
auto f = [] {},
typename Current = decltype(GetCurrentTagType<impl::AttributesTag, decltype(f)>())::Type,
auto = AddTypeToTag<impl::AttributesCounterTag<Current>, typename FieldAttributeData<Ts...>::Type, decltype(f)>()>
consteval auto FieldAttributeHelper() -> T;
export template <typename... Ts>
using FieldAttribute = decltype(FieldAttributeHelper<Ts...>());
export template <typename T, auto f = [] {}, bool R = (loopholes::CountValue<impl::AttributesCounterTag<T>, decltype(f)>() > 0)>
concept HasAttributes = R;
export template <typename T>
concept HasMacroAttributes = requires { T::template GetAttribute<0>(); };
export template <HasAttributes T>
consteval auto GetAttributes()
requires HasMacroAttributes<T>
{
constexpr auto I = loopholes::CountValue<impl::AttributesCounterTag<T>>();
return [](auto... is) {
return Tuple{T::template GetAttribute<is>()...};
} | kSeq<I>;
};
export template <typename T>
consteval auto GetAttributes() {
constexpr auto I = loopholes::CountValue<impl::AttributesCounterTag<T>>();
return [](auto... is) {
return utempl::Tuple{typename decltype(Magic(loopholes::Getter<MetaInfoKey<is, impl::AttributesCounterTag<T>>{}>{}))::Type{}...};
} | kSeq<I>;
};
} // namespace utempl

View file

@ -1,28 +1,31 @@
#pragma once module;
#include <fmt/format.h> #include <fmt/compile.h>
export module utempl.string;
#include <algorithm> import std;
// import fmt;
namespace utempl { namespace utempl {
template <std::size_t> export template <std::size_t>
struct ConstexprString; struct ConstexprString;
} // namespace utempl } // namespace utempl
template <std::size_t Size> export {
struct fmt::formatter<utempl::ConstexprString<Size>> : public fmt::formatter<std::string_view> { template <std::size_t Size>
constexpr auto parse(format_parse_context& ctx) const { struct fmt::formatter<utempl::ConstexprString<Size>> : public fmt::formatter<std::string_view> {
return ctx.begin(); constexpr auto parse(format_parse_context& ctx) const {
}; return ctx.begin();
constexpr auto format(const utempl::ConstexprString<Size>& str, auto& ctx) const { };
return fmt::formatter<std::string_view>::format({str.begin()}, ctx); constexpr auto format(const utempl::ConstexprString<Size>& str, auto& ctx) const {
return fmt::formatter<std::string_view>::format({str.begin()}, ctx);
};
}; };
}; };
export namespace utempl {
namespace utempl {
template <std::size_t Size> template <std::size_t Size>
struct ConstexprString { struct ConstexprString {
std::array<char, Size> data; std::array<char, Size> data;
using char_type = char;
constexpr auto begin() -> char* { constexpr auto begin() -> char* {
return this->data.begin(); return this->data.begin();
}; };
@ -74,6 +77,11 @@ struct ConstexprString {
constexpr ConstexprString(ConstexprString&&) = default; constexpr ConstexprString(ConstexprString&&) = default;
}; };
template <ConstexprString Str>
consteval auto GetFmtCompiledString() {
return FMT_COMPILE(Str.data.begin());
};
template <std::size_t N> template <std::size_t N>
constexpr auto operator<<(std::ostream& stream, const ConstexprString<N>& str) -> std::ostream& { constexpr auto operator<<(std::ostream& stream, const ConstexprString<N>& str) -> std::ostream& {
stream << static_cast<std::string_view>(str); stream << static_cast<std::string_view>(str);

View file

@ -1,7 +1,8 @@
#pragma once module;
#include <boost/pfr.hpp> #include <boost/pfr.hpp>
#include <type_traits> export module utempl.go_interface;
#include <utempl/constexpr_string.hpp> import std;
import utempl.string;
namespace utempl { namespace utempl {
@ -19,8 +20,6 @@ constexpr auto Get(T&& arg) {
template <auto...> template <auto...>
struct EmptyField {}; struct EmptyField {};
namespace impl {
template <ConstexprString name, typename T> template <ConstexprString name, typename T>
constexpr auto TryGet(T&& arg) -> decltype(boost::pfr::get<FindField<name, T>()>(std::forward<T>(arg))) constexpr auto TryGet(T&& arg) -> decltype(boost::pfr::get<FindField<name, T>()>(std::forward<T>(arg)))
requires(FindField<name, T>() < boost::pfr::tuple_size_v<std::remove_cvref_t<T>>) requires(FindField<name, T>() < boost::pfr::tuple_size_v<std::remove_cvref_t<T>>)
@ -41,9 +40,7 @@ constexpr auto Transform(Transformer&& transformer, From&& from) {
}(std::make_index_sequence<boost::pfr::tuple_size_v<To>>()); }(std::make_index_sequence<boost::pfr::tuple_size_v<To>>());
}; };
} // namespace impl export struct DefaultFieldTransformer {
struct DefaultFieldTransformer {
constexpr auto operator()(auto&& arg) -> auto&& { constexpr auto operator()(auto&& arg) -> auto&& {
return arg; return arg;
}; };
@ -56,12 +53,12 @@ struct DefaultFieldTransformer {
}; };
}; };
template <typename Value, typename Transformer = DefaultFieldTransformer> export template <typename Value, typename Transformer = DefaultFieldTransformer>
struct GoInterface : Value { struct GoInterface : Value {
constexpr GoInterface(Value&& value) : Value(std::move(value)) {}; // NOLINT constexpr GoInterface(Value&& value) : Value(std::move(value)) {}; // NOLINT
constexpr GoInterface(const Value& value) : Value(value) {}; // NOLINT constexpr GoInterface(const Value& value) : Value(value) {}; // NOLINT
template <typename T> template <typename T>
constexpr GoInterface(T&& value) : Value(impl::Transform<Value>(Transformer{}, std::forward<T>(value))){}; // NOLINT constexpr GoInterface(T&& value) : Value(Transform<Value>(Transformer{}, std::forward<T>(value))){}; // NOLINT
constexpr auto operator=(const GoInterface&) -> GoInterface& = default; constexpr auto operator=(const GoInterface&) -> GoInterface& = default;
constexpr auto operator=(GoInterface&&) -> GoInterface& = default; constexpr auto operator=(GoInterface&&) -> GoInterface& = default;
constexpr auto operator==(const Value& other) const -> bool constexpr auto operator==(const Value& other) const -> bool
@ -78,11 +75,10 @@ struct GoInterface : Value {
using Type = std::remove_cvref_t<T>; using Type = std::remove_cvref_t<T>;
Transformer transformer; Transformer transformer;
// + 1 - NULL Terminator // + 1 - NULL Terminator
return ( return ((boost::pfr::get<Is>(static_cast<const Value&>(*this)) ==
(boost::pfr::get<Is>(static_cast<const Value&>(*this)) == transformer(TryGet<ConstexprString<boost::pfr::get_name<Is, Type>().size() + 1>{boost::pfr::get_name<Is, Type>().begin()}>(
transformer(impl::TryGet<ConstexprString<boost::pfr::get_name<Is, Type>().size() + 1>{boost::pfr::get_name<Is, Type>().begin()}>( other))) &&
other))) && ...);
...);
}(std::make_index_sequence<boost::pfr::tuple_size_v<Value>>()); }(std::make_index_sequence<boost::pfr::tuple_size_v<Value>>());
}; };
template <typename T> template <typename T>

View file

@ -1,19 +1,20 @@
#include <type_traits> export module utempl.loopholes.core;
#pragma once import std;
namespace utempl::loopholes { namespace utempl::loopholes {
template <auto I> export {
struct Getter { template <auto I>
friend constexpr auto Magic(Getter<I>); struct Getter {
}; friend constexpr auto Magic(Getter<I>);
template <auto I, auto Value = 0>
struct Injector {
friend constexpr auto Magic(Getter<I>) {
return Value;
}; };
}; template <auto I, auto Value = 0>
struct Injector {
friend constexpr auto Magic(Getter<I>) {
return Value;
};
};
}
template <auto, typename = void, typename... Ts> template <auto, typename = void, typename... Ts>
struct InjectedImpl { struct InjectedImpl {
static constexpr bool value = false; static constexpr bool value = false;
@ -23,7 +24,7 @@ struct InjectedImpl<V, std::void_t<decltype(Magic(Getter<V>{}))>, Ts...> {
static constexpr bool value = true; static constexpr bool value = true;
}; };
template <auto I, typename... Ts> export template <auto I, typename... Ts>
concept Injected = InjectedImpl<I, void, Ts...>::value; concept Injected = InjectedImpl<I, void, Ts...>::value;
} // namespace utempl::loopholes } // namespace utempl::loopholes

View file

@ -1,19 +1,16 @@
#pragma once export module utempl.loopholes.counter;
#include <cstddef> export import utempl.loopholes.core;
#include <tuple> import std;
#include <utempl/loopholes/core.hpp>
namespace utempl::loopholes { namespace utempl::loopholes {
namespace impl {
template <typename Tag, std::size_t Value> template <typename Tag, std::size_t Value>
struct TagWithValue {}; struct TagWithValue {};
template <bool Add, typename Tag, std::size_t I, typename... Ts> template <bool Add, typename Tag, std::size_t I, typename... Ts>
consteval auto Counter() -> std::size_t { consteval auto CounterHelper() -> std::size_t {
if constexpr(requires { Magic(Getter<TagWithValue<Tag, I>{}>{}); }) { if constexpr(requires { Magic(Getter<TagWithValue<Tag, I>{}>{}); }) {
return Counter<Add, Tag, I + 1, Ts...>(); return CounterHelper<Add, Tag, I + 1, Ts...>();
}; };
if constexpr(Add) { if constexpr(Add) {
return (std::ignore = Injector<TagWithValue<Tag, I>{}, 0>{}, I); return (std::ignore = Injector<TagWithValue<Tag, I>{}, 0>{}, I);
@ -22,16 +19,14 @@ consteval auto Counter() -> std::size_t {
}; };
}; };
} // namespace impl
// For incerement counter need a unique Ts... // For incerement counter need a unique Ts...
template <typename Tag, typename... Ts, std::size_t R = impl::Counter<true, Tag, 0, Ts...>()> export template <typename Tag, typename... Ts, std::size_t R = CounterHelper<true, Tag, 0, Ts...>()>
consteval auto Counter(auto...) -> std::size_t { consteval auto Counter(auto...) -> std::size_t {
return R; return R;
}; };
// Without increment // Without increment
template <typename Tag, typename... Ts, std::size_t R = impl::Counter<false, Tag, 0, Ts...>()> export template <typename Tag, typename... Ts, std::size_t R = CounterHelper<false, Tag, 0, Ts...>()>
consteval auto CountValue(auto...) -> std::size_t { consteval auto CountValue(auto...) -> std::size_t {
return R; return R;
}; };

View file

@ -0,0 +1,3 @@
export module utempl.loopholes;
export import utempl.loopholes.core;
export import utempl.loopholes.counter;

View file

@ -1,13 +1,14 @@
#pragma once module;
#include <fmt/compile.h> #include <cstdio>
#include <fmt/format.h> export module utempl.menu;
#include <array> import fmt;
#include <iostream> import std;
#include <utempl/constexpr_string.hpp> import utempl.string;
#include <utempl/optional.hpp> import utempl.optional;
#include <utempl/tuple.hpp> import utempl.tuple;
#include <utempl/utils.hpp> import utempl.utils;
import utempl.type_list;
namespace utempl { namespace utempl {
@ -46,8 +47,6 @@ constexpr auto GetMax(Range&& range) {
namespace menu { namespace menu {
namespace impl {
template <std::size_t N1, std::size_t N2> template <std::size_t N1, std::size_t N2>
struct CallbackMessage { struct CallbackMessage {
ConstexprString<N1> message; ConstexprString<N1> message;
@ -62,17 +61,24 @@ CallbackMessage(const char (&)[N1], const char (&)[N2]) -> CallbackMessage<N2, N
template <std::size_t N1> template <std::size_t N1>
CallbackMessage(const char (&)[N1]) -> CallbackMessage<N1, 0>; // NOLINT CallbackMessage(const char (&)[N1]) -> CallbackMessage<N1, 0>; // NOLINT
} // namespace impl export template <Tuple storage = Tuple{}, typename... Fs>
template <Tuple storage = Tuple{}, typename... Fs>
struct Menu { struct Menu {
private: private:
template <ConstexprString fmt, ConstexprString message, ConstexprString alignment, ConstexprString neededInput> template <ConstexprString fmt, ConstexprString message, ConstexprString alignment, ConstexprString neededInput>
static consteval auto FormatMessage() { static consteval auto FormatMessage() {
constexpr auto fmtlib = GetFmtCompiledString<fmt>();
// + 1 - NULL Terminator // + 1 - NULL Terminator
constexpr auto size = fmt::formatted_size(FMT_COMPILE(fmt.data.begin()), neededInput, message, alignment) + 1; constexpr auto size = fmt::formatted_size(fmtlib,
static_cast<std::string_view>(neededInput),
static_cast<std::string_view>(message),
static_cast<std::string_view>(alignment)) +
1;
std::array<char, size> data{}; std::array<char, size> data{};
fmt::format_to(data.begin(), FMT_COMPILE(fmt.data.begin()), neededInput, message, alignment); fmt::format_to(data.begin(),
fmtlib,
static_cast<std::string_view>(neededInput),
static_cast<std::string_view>(message),
static_cast<std::string_view>(alignment));
return ConstexprString<size>(data); return ConstexprString<size>(data);
}; };
template <ConstexprString fmt, std::size_t I> template <ConstexprString fmt, std::size_t I>
@ -101,7 +107,7 @@ struct Menu {
: CountDigits(Is))...}); : CountDigits(Is))...});
}(std::index_sequence_for<Fs...>()); }(std::index_sequence_for<Fs...>());
}; };
template <impl::CallbackMessage message, std::invocable F> template <CallbackMessage message, std::invocable F>
constexpr auto With(F&& f) const { constexpr auto With(F&& f) const {
return Menu<storage + Tuple{message}, Fs..., std::remove_cvref_t<F>>{.functionStorage = return Menu<storage + Tuple{message}, Fs..., std::remove_cvref_t<F>>{.functionStorage =
this->functionStorage + Tuple(std::forward<F>(f))}; this->functionStorage + Tuple(std::forward<F>(f))};
@ -120,7 +126,7 @@ struct Menu {
std::string input; std::string input;
std::getline(in, input); std::getline(in, input);
( (
[&]<auto I, impl::CallbackMessage message = Get<I>(storage)>(Wrapper<I>) { [&]<auto I, CallbackMessage message = Get<I>(storage)>(Wrapper<I>) {
if constexpr(message.need) { if constexpr(message.need) {
if(*message.need == input) { if(*message.need == input) {
Get<I>(this->functionStorage)(); Get<I>(this->functionStorage)();

View file

@ -1,18 +1,16 @@
#pragma once export module utempl.meta_info;
#include <utempl/loopholes/counter.hpp> import utempl.loopholes;
#include <utempl/type_list.hpp> import utempl.type_list;
import std;
namespace utempl { namespace utempl {
namespace impl { export struct Types {};
struct Types {}; export template <std::size_t Id, typename Tag>
} // namespace impl
template <std::size_t Id, typename Tag>
struct MetaInfoKey {}; struct MetaInfoKey {};
template <typename T, typename Tag = impl::Types> export template <typename T, typename Tag = Types>
struct MetaInfo { struct MetaInfo {
static constexpr std::size_t kTypeId = loopholes::Counter<Tag, T>(); static constexpr std::size_t kTypeId = loopholes::Counter<Tag, T>();
using Type = T; using Type = T;
@ -21,27 +19,25 @@ struct MetaInfo {
static constexpr auto _ = loopholes::Injector<MetaInfoKey<kTypeId, Tag>{}, TypeList<T>{}>{}; static constexpr auto _ = loopholes::Injector<MetaInfoKey<kTypeId, Tag>{}, TypeList<T>{}>{};
}; };
template <typename T, typename Tag = impl::Types> export template <typename T, typename Tag = Types>
inline constexpr std::size_t kTypeId = MetaInfo<T, Tag>::kTypeId; inline constexpr std::size_t kTypeId = MetaInfo<T, Tag>::kTypeId;
template <typename Tag, export template <typename Tag,
typename T, typename T,
typename... Ts, typename... Ts,
typename... TTs, typename... TTs,
std::size_t Id = loopholes::Counter<Tag, T, Ts..., TTs...>(), std::size_t Id = loopholes::Counter<Tag, T, Ts..., TTs...>(),
auto = loopholes::Injector<MetaInfoKey<Id, Tag>{}, TypeList<T>{}>{}> auto = loopholes::Injector<MetaInfoKey<Id, Tag>{}, TypeList<T>{}>{}>
consteval auto AddTypeToTag(TTs&&...) -> std::size_t { consteval auto AddTypeToTag(TTs&&...) -> std::size_t {
return Id; return Id;
}; };
template <std::size_t Id, typename Tag = impl::Types> export template <std::size_t Id, typename Tag = Types>
using GetMetaInfo = MetaInfo<typename decltype(Magic(loopholes::Getter<MetaInfoKey<Id, Tag>{}>{}))::Type>; using GetMetaInfo = MetaInfo<typename decltype(Magic(loopholes::Getter<MetaInfoKey<Id, Tag>{}>{}))::Type>;
template <std::size_t Id, typename Tag = impl::Types> export template <std::size_t Id, typename Tag = Types>
using GetType = GetMetaInfo<Id, Tag>::Type; using GetType = GetMetaInfo<Id, Tag>::Type;
namespace impl {
template <typename Tag, std::size_t I = 0, typename... Ts, typename G> template <typename Tag, std::size_t I = 0, typename... Ts, typename G>
static consteval auto GetTypeListForTag(G g) static consteval auto GetTypeListForTag(G g)
requires(I == 0 || requires { Magic(loopholes::Getter<MetaInfoKey<I, Tag>{}>{}); }) requires(I == 0 || requires { Magic(loopholes::Getter<MetaInfoKey<I, Tag>{}>{}); })
@ -59,14 +55,12 @@ static consteval auto GetTypeListForTag(G g)
}; };
}; };
} // namespace impl export template <typename Tag = Types, typename... Ts>
template <typename Tag = impl::Types, typename... Ts>
consteval auto GetTypeListForTag() { consteval auto GetTypeListForTag() {
return impl::GetTypeListForTag<Tag>(TypeList<Ts...>{}); return GetTypeListForTag<Tag>(TypeList<Ts...>{});
}; };
template <typename Tag, typename... Ts, auto I = utempl::loopholes::CountValue<Tag, Ts...>() - 1> export template <typename Tag, typename... Ts, auto I = utempl::loopholes::CountValue<Tag, Ts...>() - 1>
consteval auto GetCurrentTagType() { consteval auto GetCurrentTagType() {
return Magic(utempl::loopholes::Getter<MetaInfoKey<I, Tag>{}>()); return Magic(utempl::loopholes::Getter<MetaInfoKey<I, Tag>{}>());
}; };
@ -74,7 +68,7 @@ consteval auto GetCurrentTagType() {
/* /*
static_assert(kTypeId<int> == 0); static_assert(kTypeId<int> == 0);
static_assert(kTypeId<void> == 1); static_assert(kTypeId<void> == 1);
static_assert(AddTypeToTag<impl::Types, void, int>() == 2); static_assert(AddTypeToTag<Types, void, int>() == 2);
static_assert(std::is_same_v<decltype(GetTypeListForTag()), TypeList<int, void, void>>); static_assert(std::is_same_v<decltype(GetTypeListForTag()), TypeList<int, void, void>>);
*/ */

View file

@ -1,9 +1,9 @@
#pragma once export module utempl.optional;
#include <optional> import std;
namespace utempl { namespace utempl {
template <typename T> export template <typename T>
struct Optional { // NOLINT struct Optional { // NOLINT
bool flag = false; bool flag = false;
union { union {

View file

@ -1,8 +1,8 @@
#pragma once export module utempl.overloaded;
#include <utility> import std;
namespace utempl { namespace utempl {
template <typename... Fs> export template <typename... Fs>
constexpr auto Overloaded(Fs&&... fs) { constexpr auto Overloaded(Fs&&... fs) {
struct Overloaded : public std::remove_cvref_t<Fs>... { struct Overloaded : public std::remove_cvref_t<Fs>... {
using Fs::operator()...; using Fs::operator()...;

View file

@ -1,11 +1,25 @@
#pragma once export module utempl.tuple;
#include <utempl/overloaded.hpp>
#include <utempl/type_list.hpp> import utempl.type_list;
import utempl.overloaded;
import std;
namespace utempl { namespace utempl {
template <auto> export template <auto Value>
struct Wrapper; struct Wrapper {
namespace impl { static constexpr auto kValue = Value;
static constexpr auto value = Value;
inline constexpr auto operator==(auto&& arg) const {
return arg == Value;
};
consteval operator decltype(Value)() const { // NOLINT
return Value;
};
consteval auto operator*() const {
return Value;
};
};
template <auto, typename T> template <auto, typename T>
struct TupleLeaf { struct TupleLeaf {
@ -17,60 +31,58 @@ template <typename, typename...>
struct TupleHelper; struct TupleHelper;
template <std::size_t... Is, typename... Ts> template <std::size_t... Is, typename... Ts>
struct TupleHelper<std::index_sequence<Is...>, Ts...> : public impl::TupleLeaf<Is, Ts>... { struct TupleHelper<std::index_sequence<Is...>, Ts...> : public TupleLeaf<Is, Ts>... {
constexpr auto operator==(const TupleHelper&) const -> bool = default; constexpr auto operator==(const TupleHelper&) const -> bool = default;
}; };
} // namespace impl export template <typename... Ts>
template <typename... Ts>
struct Tuple; struct Tuple;
template <std::size_t I, typename... Ts> export template <std::size_t I, typename... Ts>
constexpr auto Get(Tuple<Ts...>& tuple) -> auto& constexpr auto Get(Tuple<Ts...>& tuple) -> auto&
requires(I < sizeof...(Ts)) requires(I < sizeof...(Ts))
{ {
using Type = decltype(Get<I>(TypeList<Ts...>{})); using Type = decltype(Get<I>(TypeList<Ts...>{}));
return static_cast<impl::TupleLeaf<I, Type>&>(tuple).value; return static_cast<TupleLeaf<I, Type>&>(tuple).value;
}; };
template <std::size_t I, typename... Ts> export template <std::size_t I, typename... Ts>
constexpr auto Get(const Tuple<Ts...>& tuple) -> const auto& constexpr auto Get(const Tuple<Ts...>& tuple) -> const auto&
requires(I < sizeof...(Ts)) requires(I < sizeof...(Ts))
{ {
using Type = decltype(Get<I>(TypeList<Ts...>{})); using Type = decltype(Get<I>(TypeList<Ts...>{}));
return static_cast<const impl::TupleLeaf<I, Type>&>(tuple).value; return static_cast<const TupleLeaf<I, Type>&>(tuple).value;
}; };
template <std::size_t I, typename... Ts> export template <std::size_t I, typename... Ts>
constexpr auto Get(Tuple<Ts...>&& tuple) -> decltype(auto) constexpr auto Get(Tuple<Ts...>&& tuple) -> decltype(auto)
requires(I < sizeof...(Ts)) requires(I < sizeof...(Ts))
{ {
using Type = decltype(Get<I>(TypeList<Ts...>{})); using Type = decltype(Get<I>(TypeList<Ts...>{}));
return std::move(static_cast<impl::TupleLeaf<I, Type>&&>(tuple).value); return std::move(static_cast<TupleLeaf<I, Type>&&>(tuple).value);
}; };
template <std::size_t I, typename T> export template <std::size_t I, typename T>
constexpr auto Get(T&& arg) -> decltype(get<I>(std::forward<T>(arg))) { constexpr auto Get(T&& arg) -> decltype(get<I>(std::forward<T>(arg))) {
return get<I>(std::forward<T>(arg)); return get<I>(std::forward<T>(arg));
}; };
template <typename... Ts> export template <typename... Ts>
struct Tuple : impl::TupleHelper<std::index_sequence_for<Ts...>, Ts...> { struct Tuple : TupleHelper<std::index_sequence_for<Ts...>, Ts...> {
template <typename... TTs> template <typename... TTs>
constexpr Tuple(TTs&&... args) /* NOLINT */ : impl::TupleHelper<std::index_sequence_for<Ts...>, Ts...>{{std::forward<TTs>(args)}...} {}; constexpr Tuple(TTs&&... args) /* NOLINT */ : TupleHelper<std::index_sequence_for<Ts...>, Ts...>{{std::forward<TTs>(args)}...} {};
template <std::invocable... Fs> template <std::invocable... Fs>
constexpr Tuple(Fs&&... fs) // NOLINT constexpr Tuple(Fs&&... fs) // NOLINT
requires(!std::invocable<Ts> || ...) requires(!std::invocable<Ts> || ...)
: impl::TupleHelper<std::index_sequence_for<Ts...>, Ts...>{{fs()}...} {}; : TupleHelper<std::index_sequence_for<Ts...>, Ts...>{{fs()}...} {};
constexpr Tuple(Ts&&... args) : impl::TupleHelper<std::index_sequence_for<Ts...>, Ts...>{{args}...} {}; // NOLINT constexpr Tuple(Ts&&... args) : TupleHelper<std::index_sequence_for<Ts...>, Ts...>{{args}...} {}; // NOLINT
constexpr Tuple(const Tuple&) = default; constexpr Tuple(const Tuple&) = default;
constexpr Tuple(Tuple&&) = default; constexpr Tuple(Tuple&&) = default;
constexpr Tuple(Tuple&) = default; constexpr Tuple(Tuple&) = default;
constexpr auto operator=(const Tuple&) -> Tuple& = default; constexpr auto operator=(const Tuple&) -> Tuple& = default;
constexpr auto operator=(Tuple&&) -> Tuple& = default; constexpr auto operator=(Tuple&&) -> Tuple& = default;
constexpr auto operator=(Tuple&) -> Tuple& = default; constexpr auto operator=(Tuple&) -> Tuple& = default;
constexpr Tuple() : impl::TupleHelper<std::index_sequence_for<Ts...>, Ts...>() {}; constexpr Tuple() : TupleHelper<std::index_sequence_for<Ts...>, Ts...>() {};
constexpr auto operator==(const Tuple<Ts...>& other) const -> bool { constexpr auto operator==(const Tuple<Ts...>& other) const -> bool {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) { return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return ((Get<Is>(*this) == Get<Is>(other)) && ...); return ((Get<Is>(*this) == Get<Is>(other)) && ...);
@ -94,7 +106,7 @@ struct Tuple : impl::TupleHelper<std::index_sequence_for<Ts...>, Ts...> {
}; };
}; };
template <> export template <>
struct Tuple<> { struct Tuple<> {
template <typename... Ts> template <typename... Ts>
constexpr auto operator+(const Tuple<Ts...>& other) const -> Tuple<Ts...> { constexpr auto operator+(const Tuple<Ts...>& other) const -> Tuple<Ts...> {
@ -104,10 +116,10 @@ struct Tuple<> {
return true; return true;
}; };
}; };
template <typename... Ts> export template <typename... Ts>
Tuple(Ts&&...) -> Tuple<std::decay_t<Ts>...>; Tuple(Ts&&...) -> Tuple<std::decay_t<Ts>...>;
template <typename... Ts> export template <typename... Ts>
consteval auto ListFromTuple(Tuple<Ts...>) -> TypeList<Ts...> { consteval auto ListFromTuple(Tuple<Ts...>) -> TypeList<Ts...> {
return {}; return {};
}; };

View file

@ -1,27 +1,25 @@
#pragma once export module utempl.type_list;
#include <algorithm>
#include <array> import std;
#include <concepts> import utempl.overloaded;
#include <utempl/overloaded.hpp>
#include <utility>
namespace utempl { namespace utempl {
template <typename... Ts> export template <typename... Ts>
struct TypeList {}; struct TypeList {};
template <typename T> export template <typename T>
struct TypeList<T> { struct TypeList<T> {
using Type = T; using Type = T;
}; };
template <typename T> export template <typename T>
inline constexpr auto kType = TypeList<T>{}; inline constexpr auto kType = TypeList<T>{};
template <typename... Ts> export template <typename... Ts>
inline constexpr auto kTypeList = TypeList<Ts...>{}; inline constexpr auto kTypeList = TypeList<Ts...>{};
template <typename T> export template <typename T>
concept IsTypeList = Overloaded( concept IsTypeList = Overloaded(
[]<typename... Ts>(TypeList<TypeList<Ts...>>) { []<typename... Ts>(TypeList<TypeList<Ts...>>) {
return true; return true;
@ -30,8 +28,6 @@ concept IsTypeList = Overloaded(
return false; return false;
})(kType<std::remove_cvref_t<T>>); })(kType<std::remove_cvref_t<T>>);
namespace impl {
template <std::size_t I, typename T> template <std::size_t I, typename T>
struct IndexedType {}; struct IndexedType {};
@ -41,45 +37,45 @@ struct Caster {};
template <std::size_t... Is, typename... Ts> template <std::size_t... Is, typename... Ts>
struct Caster<std::index_sequence<Is...>, Ts...> : IndexedType<Is, Ts>... {}; struct Caster<std::index_sequence<Is...>, Ts...> : IndexedType<Is, Ts>... {};
} // namespace impl export template <typename... Ts, typename... TTs>
template <typename... Ts, typename... TTs>
consteval auto operator==(const TypeList<Ts...>& first, const TypeList<TTs...>& second) -> bool { consteval auto operator==(const TypeList<Ts...>& first, const TypeList<TTs...>& second) -> bool {
return std::same_as<decltype(first), decltype(second)>; return std::same_as<decltype(first), decltype(second)>;
}; };
template <typename... Ts, typename... TTs> export template <typename... Ts, typename... TTs>
consteval auto operator+(const TypeList<Ts...>&, const TypeList<TTs...>&) -> TypeList<Ts..., TTs...> { consteval auto operator+(const TypeList<Ts...>&, const TypeList<TTs...>&) -> TypeList<Ts..., TTs...> {
return {}; return {};
}; };
template <std::size_t I, typename... Ts> export template <std::size_t I, typename... Ts>
consteval auto Get(TypeList<Ts...>) -> decltype([]<typename T>(impl::IndexedType<I, T>&&) -> T { consteval auto Get(TypeList<Ts...>) -> decltype([]<typename T>(IndexedType<I, T>&&) -> T {
}(impl::Caster<std::index_sequence_for<Ts...>, Ts...>{})); }(Caster<std::index_sequence_for<Ts...>, Ts...>{}));
template <typename T, typename... Ts> export template <typename T, typename... Ts>
consteval auto Find(TypeList<Ts...>) -> std::size_t { consteval auto Find(TypeList<Ts...>) -> std::size_t {
std::array<bool, sizeof...(Ts)> arr{std::same_as<T, Ts>...}; std::array<bool, sizeof...(Ts)> arr{std::same_as<T, Ts>...};
return std::ranges::find(arr, true) - arr.begin(); return std::ranges::find(arr, true) - arr.begin();
}; };
template <template <typename...> typename F, typename... Ts> export template <template <typename...> typename F, typename... Ts>
consteval auto Find(TypeList<Ts...>) -> std::size_t { consteval auto Find(TypeList<Ts...>) -> std::size_t {
std::array<bool, sizeof...(Ts)> arr{F<Ts>::value...}; std::array<bool, sizeof...(Ts)> arr{F<Ts>::value...};
return std::ranges::find(arr, true) - arr.begin(); return std::ranges::find(arr, true) - arr.begin();
}; };
template <typename... Ts> export template <typename... Ts>
consteval auto Reverse(TypeList<Ts...> list) { consteval auto Reverse(TypeList<Ts...> list) {
return [&]<auto... Is>(std::index_sequence<Is...>) -> TypeList<decltype(Get<sizeof...(Ts) - Is - 1>(list))...> { return [&]<auto... Is>(std::index_sequence<Is...>) -> TypeList<decltype(Get<sizeof...(Ts) - Is - 1>(list))...> {
return {}; return {};
}(std::index_sequence_for<Ts...>()); }(std::index_sequence_for<Ts...>());
}; };
template <typename... Ts> export template <typename... Ts>
consteval auto Transform(TypeList<Ts...>, auto&& f) -> TypeList<decltype(f(TypeList<Ts>{}))...> { consteval auto Transform(TypeList<Ts...>, auto&& f) -> TypeList<decltype(f(TypeList<Ts>{}))...> {
return {}; return {};
}; };
template <typename... Ts>
export template <typename... Ts>
consteval auto FilterTypeList(TypeList<Ts...>, auto&& f) { consteval auto FilterTypeList(TypeList<Ts...>, auto&& f) {
return ((kTypeList<> + return ((kTypeList<> +
[](auto&& list) { [](auto&& list) {
@ -92,12 +88,12 @@ consteval auto FilterTypeList(TypeList<Ts...>, auto&& f) {
...); ...);
}; };
template <typename... Ts> export template <typename... Ts>
consteval auto Size(TypeList<Ts...>) -> std::size_t { consteval auto Size(TypeList<Ts...>) -> std::size_t {
return sizeof...(Ts); return sizeof...(Ts);
}; };
template <std::size_t N, std::size_t From = 0, typename... Ts> export template <std::size_t N, std::size_t From = 0, typename... Ts>
consteval auto TakeFrom(TypeList<Ts...> list) consteval auto TakeFrom(TypeList<Ts...> list)
requires(N + From <= sizeof...(Ts)) requires(N + From <= sizeof...(Ts))
{ {
@ -106,12 +102,12 @@ consteval auto TakeFrom(TypeList<Ts...> list)
}(std::make_index_sequence<N>()); }(std::make_index_sequence<N>());
}; };
template <typename T, std::size_t N, typename... Ts> export template <typename T, std::size_t N, typename... Ts>
consteval auto PushTo(TypeList<Ts...> list) { consteval auto PushTo(TypeList<Ts...> list) {
return TakeFrom<N>(list) + kType<T> + TakeFrom<sizeof...(Ts) - N, N>(list); return TakeFrom<N>(list) + kType<T> + TakeFrom<sizeof...(Ts) - N, N>(list);
}; };
template <std::size_t N, typename... Ts> export template <std::size_t N, typename... Ts>
consteval auto Erase(TypeList<Ts...> list) { consteval auto Erase(TypeList<Ts...> list) {
return TakeFrom<N>(list) + TakeFrom<sizeof...(Ts) - N - 1, N + 1>(list); return TakeFrom<N>(list) + TakeFrom<sizeof...(Ts) - N - 1, N + 1>(list);
}; };

12
src/utempl.cpp Normal file
View file

@ -0,0 +1,12 @@
export module utempl;
export import utempl.type_list;
export import utempl.tuple;
export import utempl.string;
export import utempl.menu;
export import utempl.loopholes;
export import utempl.attributes;
export import utempl.meta_info;
export import utempl.overloaded;
export import utempl.utils;
export import utempl.go_interface;
export import utempl.optional;

View file

@ -1,57 +1,39 @@
#pragma once export module utempl.utils;
#include <fmt/format.h>
#include <optional> import std;
#include <ranges> import fmt;
#include <utempl/constexpr_string.hpp> import utempl.string;
#include <utempl/overloaded.hpp> import utempl.tuple;
#include <utempl/tuple.hpp> import utempl.type_list;
import utempl.overloaded;
namespace utempl { namespace utempl {
template <typename T> template <typename T>
using ForwardType = decltype(std::forward<T>(std::declval<T>())); using ForwardType = decltype(std::forward<T>(std::declval<T>()));
template <auto Value> export template <auto Value>
struct Wrapper { constexpr Wrapper<Value> kWrapper;
static constexpr auto kValue = Value;
inline constexpr auto operator==(auto&& arg) const {
return arg == Value;
};
consteval operator decltype(Value)() const { // NOLINT
return Value;
};
consteval auto operator*() const {
return Value;
};
};
template <auto Value> export template <typename T>
inline constexpr Wrapper<Value> kWrapper;
template <typename T>
requires std::same_as<T, void> || requires { T{}; } requires std::same_as<T, void> || requires { T{}; }
constexpr auto kDefaultCreator = [] { constexpr auto kDefaultCreator = [] {
return T{}; return T{};
}; };
template <> export template <>
constexpr auto kDefaultCreator<void> = [] {}; constexpr auto kDefaultCreator<void> = [] {};
namespace impl {
template <std::size_t N> template <std::size_t N>
struct kSeq { struct kSeqType {
template <typename F> template <typename F>
friend constexpr auto operator|(F&& f, const kSeq<N>&) -> decltype(auto) { friend constexpr auto operator|(F&& f, const kSeqType<N>&) -> decltype(auto) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) { return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {
return std::forward<F>(f)(kWrapper<Is>...); return std::forward<F>(f)(kWrapper<Is>...);
}(std::make_index_sequence<N>()); }(std::make_index_sequence<N>());
}; };
}; };
} // namespace impl
template <typename From, typename To> template <typename From, typename To>
concept ImplicitConvertibleTo = std::same_as<From, To> || requires(From from) { [](To) {}(from); }; concept ImplicitConvertibleTo = std::same_as<From, To> || requires(From from) { [](To) {}(from); };
@ -65,8 +47,8 @@ concept Function = []<typename R, typename... Ts>(TypeList<R(Ts...)>) {
static_assert(Function<decltype([]() {}), void()>); static_assert(Function<decltype([]() {}), void()>);
template <std::size_t N> export template <std::size_t N>
inline constexpr impl::kSeq<N> kSeq; constexpr kSeqType<N> kSeq;
template <ConstexprString string, typename T = std::size_t> template <ConstexprString string, typename T = std::size_t>
consteval auto ParseNumber() -> T { consteval auto ParseNumber() -> T {
@ -80,15 +62,15 @@ consteval auto ParseNumber() -> T {
}; };
namespace literals { namespace literals {
template <char... cs> export template <char... cs>
consteval auto operator"" _c() { consteval auto operator"" _c() {
return Wrapper<ParseNumber<ConstexprString<sizeof...(cs)>({cs...})>()>{}; return Wrapper<ParseNumber<ConstexprString<sizeof...(cs)>({cs...})>()>{};
}; };
} // namespace literals } // namespace literals
template <std::size_t I, typename... Ts> export template <std::size_t I, typename... Ts>
inline constexpr auto Arg(Ts&&... args) -> decltype(auto) constexpr auto Arg(Ts&&... args) -> decltype(auto)
requires(I < sizeof...(Ts)) requires(I < sizeof...(Ts))
{ {
return [&]<auto... Is>(std::index_sequence<Is...>) -> decltype(auto) { return [&]<auto... Is>(std::index_sequence<Is...>) -> decltype(auto) {
@ -98,55 +80,55 @@ inline constexpr auto Arg(Ts&&... args) -> decltype(auto)
}(std::make_index_sequence<I>()); }(std::make_index_sequence<I>());
}; };
template <std::size_t Count> export template <std::size_t Count>
inline constexpr auto Times(auto&& f) { constexpr auto Times(auto&& f) {
[&]<auto... Is>(std::index_sequence<Is...>) { [&]<auto... Is>(std::index_sequence<Is...>) {
(Arg<0>(f, Is)(), ...); (Arg<0>(f, Is)(), ...);
}(std::make_index_sequence<Count>()); }(std::make_index_sequence<Count>());
}; };
template <typename T> export template <typename T>
inline constexpr std::size_t kTupleSize = []() -> std::size_t { constexpr std::size_t kTupleSize = []() -> std::size_t {
static_assert(!sizeof(T), "Not Found"); static_assert(!sizeof(T), "Not Found");
return 0; return 0;
}(); }();
template <typename T> export template <typename T>
inline constexpr std::size_t kTupleSize<T&&> = kTupleSize<std::remove_reference_t<T>>; constexpr std::size_t kTupleSize<T&&> = kTupleSize<std::remove_reference_t<T>>;
template <typename T> export template <typename T>
inline constexpr std::size_t kTupleSize<T&> = kTupleSize<std::remove_reference_t<T>>; constexpr std::size_t kTupleSize<T&> = kTupleSize<std::remove_reference_t<T>>;
template <typename T> export template <typename T>
inline constexpr std::size_t kTupleSize<const T> = kTupleSize<std::remove_cv_t<T>>; constexpr std::size_t kTupleSize<const T> = kTupleSize<std::remove_cv_t<T>>;
template <template <typename...> typename M, typename... Ts> export template <template <typename...> typename M, typename... Ts>
inline constexpr std::size_t kTupleSize<M<Ts...>> = sizeof...(Ts); constexpr std::size_t kTupleSize<M<Ts...>> = sizeof...(Ts);
template <template <typename, std::size_t> typename Array, typename T, std::size_t N> export template <template <typename, std::size_t> typename Array, typename T, std::size_t N>
inline constexpr std::size_t kTupleSize<Array<T, N>> = N; constexpr std::size_t kTupleSize<Array<T, N>> = N;
template <typename T> export template <typename T>
struct TupleMaker {}; struct TupleMaker {};
template <typename... Ts> export template <typename... Ts>
struct TupleMaker<std::tuple<Ts...>> { struct TupleMaker<std::tuple<Ts...>> {
template <typename... Args> template <typename... Args>
static inline constexpr auto Make(Args&&... args) { static constexpr auto Make(Args&&... args) {
return std::tuple{std::forward<Args>(args)...}; return std::tuple{std::forward<Args>(args)...};
}; };
}; };
template <typename... Ts> export template <typename... Ts>
struct TupleMaker<Tuple<Ts...>> { struct TupleMaker<Tuple<Ts...>> {
template <typename... Args> template <typename... Args>
static inline constexpr auto Make(Args&&... args) { static constexpr auto Make(Args&&... args) {
return Tuple{std::forward<Args>(args)...}; return Tuple{std::forward<Args>(args)...};
}; };
}; };
template <typename T, std::size_t N> export template <typename T, std::size_t N>
struct TupleMaker<std::array<T, N>> { struct TupleMaker<std::array<T, N>> {
template <typename Arg, typename... Args> template <typename Arg, typename... Args>
static inline constexpr auto Make(Arg&& arg, Args&&... args) static constexpr auto Make(Arg&& arg, Args&&... args)
requires(std::same_as<std::remove_cvref_t<Arg>, std::remove_cvref_t<Args>> && ...) requires(std::same_as<std::remove_cvref_t<Arg>, std::remove_cvref_t<Args>> && ...)
{ {
return std::array{std::forward<Arg>(arg), std::forward<Args>(args)...}; return std::array{std::forward<Arg>(arg), std::forward<Args>(args)...};
@ -156,40 +138,38 @@ struct TupleMaker<std::array<T, N>> {
}; };
}; };
template <typename T> export template <typename T>
struct TupleTieMaker {}; struct TupleTieMaker {};
template <typename... Ts> export template <typename... Ts>
struct TupleTieMaker<std::tuple<Ts...>> { struct TupleTieMaker<std::tuple<Ts...>> {
template <typename... Args> template <typename... Args>
static inline constexpr auto Make(Args&... args) -> std::tuple<Args...> { static constexpr auto Make(Args&... args) -> std::tuple<Args...> {
return {args...}; return {args...};
}; };
}; };
template <typename... Ts> export template <typename... Ts>
struct TupleTieMaker<Tuple<Ts...>> { struct TupleTieMaker<Tuple<Ts...>> {
template <typename... Args> template <typename... Args>
static inline constexpr auto Make(Args&... args) -> Tuple<Args...> { static constexpr auto Make(Args&... args) -> Tuple<Args...> {
return {args...}; return {args...};
}; };
}; };
template <typename T = Tuple<>, typename... Args> export template <typename T = Tuple<>, typename... Args>
inline constexpr auto MakeTuple(Args&&... args) -> decltype(TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...)) { constexpr auto MakeTuple(Args&&... args) -> decltype(TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...)) {
return TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...); return TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...);
}; };
template <typename T = Tuple<>, typename... Args> export template <typename T = Tuple<>, typename... Args>
inline constexpr auto MakeTie(Args&... args) -> decltype(TupleTieMaker<std::remove_cvref_t<T>>::Make(args...)) { constexpr auto MakeTie(Args&... args) -> decltype(TupleTieMaker<std::remove_cvref_t<T>>::Make(args...)) {
return TupleTieMaker<std::remove_cvref_t<T>>::Make(args...); return TupleTieMaker<std::remove_cvref_t<T>>::Make(args...);
}; };
template <typename T> template <typename T>
concept HasMakeTie = requires(int& arg) { MakeTie<T>(arg); }; concept HasMakeTie = requires(int& arg) { MakeTie<T>(arg); };
namespace impl {
template <typename T> template <typename T>
struct Getter { struct Getter {
friend consteval auto Magic(Getter<T>); friend consteval auto Magic(Getter<T>);
@ -215,7 +195,7 @@ consteval auto TrueF(auto&&...) {
}; };
template <typename T> template <typename T>
inline constexpr const SafeTupleChecker<T> kSafeTupleChecker; constexpr const SafeTupleChecker<T> kSafeTupleChecker;
template <typename T, bool = false> template <typename T, bool = false>
struct IsSafeTuple { struct IsSafeTuple {
@ -230,19 +210,17 @@ struct IsSafeTuple<
static constexpr bool value = false; static constexpr bool value = false;
}; };
} // namespace impl export template <typename T>
constexpr bool kForceEnableTuple = false;
template <typename T> export template <typename T, std::size_t N>
inline constexpr bool kForceEnableTuple = false; constexpr bool kForceEnableTuple<std::array<T, N>> = true;
template <typename T, std::size_t N> export template <typename T>
inline constexpr bool kForceEnableTuple<std::array<T, N>> = true;
template <typename T>
concept TupleLike = kForceEnableTuple<std::remove_cvref_t<T>> || concept TupleLike = kForceEnableTuple<std::remove_cvref_t<T>> ||
(requires { Get<0>(MakeTuple<T>(42)); } && impl::IsSafeTuple<std::remove_cvref_t<T>>::value); // NOLINT (requires { Get<0>(MakeTuple<T>(42)); } && IsSafeTuple<std::remove_cvref_t<T>>::value); // NOLINT
template <typename F, typename Tuple> export template <typename F, typename Tuple>
concept TupleTransformer = requires(F f, Tuple&& tuple) { concept TupleTransformer = requires(F f, Tuple&& tuple) {
{ f(std::move(tuple)) }; { f(std::move(tuple)) };
}; };
@ -252,58 +230,58 @@ struct LazyTuple {
F f; F f;
using ResultType = std::invoke_result_t<F>; using ResultType = std::invoke_result_t<F>;
std::optional<ResultType> result{std::nullopt}; std::optional<ResultType> result{std::nullopt};
inline constexpr auto Evaluate() { constexpr auto Evaluate() {
if(!this->result) { if(!this->result) {
this->result.emplace(this->f()); this->result.emplace(this->f());
}; };
}; };
inline constexpr auto operator()() -> decltype(auto) { constexpr auto operator()() -> decltype(auto) {
this->Evaluate(); this->Evaluate();
return *this->result; return *this->result;
}; };
inline constexpr auto operator()() const -> decltype(auto) { constexpr auto operator()() const -> decltype(auto) {
return this->f(); return this->f();
}; };
template <typename T> template <typename T>
inline constexpr auto operator==(T&& other) constexpr auto operator==(T&& other)
requires requires(ResultType result) { result == std::forward<T>(other); } requires requires(ResultType result) { result == std::forward<T>(other); }
{ {
return (*this)() == other; return (*this)() == other;
}; };
template <typename T> template <typename T>
inline constexpr auto operator==(T&& other) const constexpr auto operator==(T&& other) const
requires requires(ResultType result) { result == std::forward<T>(other); } requires requires(ResultType result) { result == std::forward<T>(other); }
{ {
return (*this)() == other; return (*this)() == other;
}; };
inline constexpr operator std::invoke_result_t<F>() { // NOLINT constexpr operator std::invoke_result_t<F>() { // NOLINT
return (*this)(); return (*this)();
}; };
inline constexpr operator std::invoke_result_t<F>() const { // NOLINT constexpr operator std::invoke_result_t<F>() const { // NOLINT
return (*this)(); return (*this)();
}; };
template <std::size_t I> template <std::size_t I>
friend inline constexpr auto Get(LazyTuple&& tuple) -> decltype(auto) friend constexpr auto Get(LazyTuple&& tuple) -> decltype(auto)
requires TupleLike<ResultType> requires TupleLike<ResultType>
{ {
return Get<I>(std::move(tuple)()); return Get<I>(std::move(tuple)());
}; };
template <std::size_t I> template <std::size_t I>
friend inline constexpr auto Get(const LazyTuple&& tuple) -> decltype(auto) friend constexpr auto Get(const LazyTuple&& tuple) -> decltype(auto)
requires TupleLike<ResultType> requires TupleLike<ResultType>
{ {
return Get<I>(std::move(tuple)()); return Get<I>(std::move(tuple)());
}; };
template <std::size_t I> template <std::size_t I>
friend inline constexpr auto Get(LazyTuple& tuple) -> decltype(auto) friend constexpr auto Get(LazyTuple& tuple) -> decltype(auto)
requires TupleLike<ResultType> requires TupleLike<ResultType>
{ {
return Get<I>(tuple()); return Get<I>(tuple());
}; };
template <std::size_t I> template <std::size_t I>
friend inline constexpr auto Get(const LazyTuple& tuple) -> decltype(auto) friend constexpr auto Get(const LazyTuple& tuple) -> decltype(auto)
requires TupleLike<ResultType> requires TupleLike<ResultType>
{ {
return Get<I>(tuple()); return Get<I>(tuple());
@ -319,22 +297,20 @@ struct LazyTuple {
template <std::invocable F> template <std::invocable F>
struct TupleMaker<LazyTuple<F>> { struct TupleMaker<LazyTuple<F>> {
template <typename... Ts> template <typename... Ts>
static inline constexpr auto Make(Ts&&... args) static constexpr auto Make(Ts&&... args)
requires requires { TupleMaker<typename LazyTuple<F>::ResultType>::Make(std::forward<Ts>(args)...); } requires requires { TupleMaker<typename LazyTuple<F>::ResultType>::Make(std::forward<Ts>(args)...); }
{ {
return TupleMaker<typename LazyTuple<F>::ResultType>::Make(std::forward<Ts>(args)...); return TupleMaker<typename LazyTuple<F>::ResultType>::Make(std::forward<Ts>(args)...);
}; };
}; };
template <TupleLike Tuple, TupleTransformer<Tuple> FF> export template <TupleLike Tuple, TupleTransformer<Tuple> FF>
inline constexpr auto operator|(Tuple&& tuple, FF&& f) { constexpr auto operator|(Tuple&& tuple, FF&& f) {
return LazyTuple{[tuple = std::forward<Tuple>(tuple), f = std::forward<FF>(f)]() -> decltype(auto) { return LazyTuple{[tuple = std::forward<Tuple>(tuple), f = std::forward<FF>(f)]() -> decltype(auto) {
return f(std::move(tuple)); return f(std::move(tuple));
}}; }};
}; };
namespace impl {
template <typename F, typename Tuple> template <typename F, typename Tuple>
concept UnpackConcept = []<std::size_t... Is>(std::index_sequence<Is...>) { concept UnpackConcept = []<std::size_t... Is>(std::index_sequence<Is...>) {
return std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))...>; return std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))...>;
@ -355,32 +331,30 @@ concept ForEachConcept = []<std::size_t... Is>(std::index_sequence<Is...>) {
return (std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))> && ...); return (std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))> && ...);
}(std::make_index_sequence<kTupleSize<Tuple>>()); }(std::make_index_sequence<kTupleSize<Tuple>>());
} // namespace impl export template <TupleLike Tuple, UnpackConcept<Tuple> F>
constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) {
template <TupleLike Tuple, impl::UnpackConcept<Tuple> F>
inline constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) {
return [&](auto... is) -> decltype(auto) { return [&](auto... is) -> decltype(auto) {
return f(Get<is>(std::forward<Tuple>(tuple))...); return f(Get<is>(std::forward<Tuple>(tuple))...);
} | kSeq<kTupleSize<Tuple>>; } | kSeq<kTupleSize<Tuple>>;
}; };
template <typename F> export template <typename F>
constexpr auto Unpack(F&& f) { constexpr auto Unpack(F&& f) {
return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) -> decltype(auto) return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) -> decltype(auto)
requires impl::UnpackConcept<F, Tuple> requires UnpackConcept<F, Tuple>
{ {
return Unpack(std::forward<Tuple>(tuple), std::move(f)); return Unpack(std::forward<Tuple>(tuple), std::move(f));
}; };
}; };
template <TupleLike Tuple, TupleLike R = Tuple, impl::TransformConcept<Tuple> F> export template <TupleLike Tuple, TupleLike R = Tuple, TransformConcept<Tuple> F>
constexpr auto Transform(Tuple&& container, F&& f, TypeList<R> = {}) { constexpr auto Transform(Tuple&& container, F&& f, TypeList<R> = {}) {
return Unpack(std::forward<Tuple>(container), [&]<typename... Ts>(Ts&&... args) { return Unpack(std::forward<Tuple>(container), [&]<typename... Ts>(Ts&&... args) {
return MakeTuple<R>(f(std::forward<Ts>(args))...); return MakeTuple<R>(f(std::forward<Ts>(args))...);
}); });
}; };
template <typename F, typename R = void> export template <typename F, typename R = void>
constexpr auto Transform(F&& f, TypeList<R> result = {}) { constexpr auto Transform(F&& f, TypeList<R> result = {}) {
return [f = std::forward<F>(f), result]<TupleLike TTuple, typename RR = decltype([] { return [f = std::forward<F>(f), result]<TupleLike TTuple, typename RR = decltype([] {
if constexpr(std::same_as<R, void>) { if constexpr(std::same_as<R, void>) {
@ -389,23 +363,23 @@ constexpr auto Transform(F&& f, TypeList<R> result = {}) {
return kType<R>; return kType<R>;
}; };
}())::Type>(TTuple&& tuple) }())::Type>(TTuple&& tuple)
requires impl::TransformConcept<F, TTuple> requires TransformConcept<F, TTuple>
{ {
return Transform(std::forward<TTuple>(tuple), std::move(f), kType<RR>); return Transform(std::forward<TTuple>(tuple), std::move(f), kType<RR>);
}; };
}; };
template <TupleLike Tuple, TupleLike R = Tuple, impl::TransformConcept<Tuple> F> export template <TupleLike Tuple, TupleLike R = Tuple, TransformConcept<Tuple> F>
constexpr auto Map(Tuple&& tuple, F&& f, TypeList<R> result = {}) { constexpr auto Map(Tuple&& tuple, F&& f, TypeList<R> result = {}) {
return Transform(std::forward<Tuple>(tuple), std::forward<F>(f), result); return Transform(std::forward<Tuple>(tuple), std::forward<F>(f), result);
}; };
template <typename F, typename R = void> export template <typename F, typename R = void>
constexpr auto Map(F&& f, TypeList<R> result = {}) -> decltype(Transform(std::forward<F>(f), result)) { constexpr auto Map(F&& f, TypeList<R> result = {}) -> decltype(Transform(std::forward<F>(f), result)) {
return Transform(std::forward<F>(f), result); return Transform(std::forward<F>(f), result);
}; };
template <auto Tuple, TupleLike To = decltype(Tuple)> export template <auto Tuple, TupleLike To = decltype(Tuple)>
consteval auto PackConstexprWrapper() consteval auto PackConstexprWrapper()
requires TupleLike<decltype(Tuple)> requires TupleLike<decltype(Tuple)>
{ {
@ -414,35 +388,33 @@ consteval auto PackConstexprWrapper()
}(std::make_index_sequence<kTupleSize<decltype(Tuple)>>()); }(std::make_index_sequence<kTupleSize<decltype(Tuple)>>());
}; };
template <TupleLike Tuple> export template <TupleLike Tuple>
constexpr auto Reverse(Tuple&& tuple) { constexpr auto Reverse(Tuple&& tuple) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) { return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return MakeTuple<Tuple>(Get<kTupleSize<Tuple> - Is - 1>(std::forward<Tuple>(tuple))...); return MakeTuple<Tuple>(Get<kTupleSize<Tuple> - Is - 1>(std::forward<Tuple>(tuple))...);
}(std::make_index_sequence<kTupleSize<Tuple>>()); }(std::make_index_sequence<kTupleSize<Tuple>>());
}; };
consteval auto Reverse() { export consteval auto Reverse() {
return []<TupleLike Tuple>(Tuple&& tuple) { return []<TupleLike Tuple>(Tuple&& tuple) {
return Reverse(std::forward<Tuple>(tuple)); return Reverse(std::forward<Tuple>(tuple));
}; };
}; };
namespace impl {
template <typename...> template <typename...>
struct LeftFold; struct LeftFoldHelper;
template <typename T> template <typename T>
struct LeftFold<T> { struct LeftFoldHelper<T> {
T data; T data;
}; };
template <typename T, typename F> template <typename T, typename F>
struct LeftFold<T, F> { struct LeftFoldHelper<T, F> {
T data; T data;
F f; F f;
template <typename TT> template <typename TT>
constexpr auto operator|(LeftFold<TT>&& other) -> LeftFold<std::invoke_result_t<F, T, TT>, F> { constexpr auto operator|(LeftFoldHelper<TT>&& other) -> LeftFoldHelper<std::invoke_result_t<F, T, TT>, F> {
return {.data = std::forward<F>(this->f)(std::forward<T>(this->data), std::forward<TT>(other.data)), .f = std::forward<F>(this->f)}; return {.data = std::forward<F>(this->f)(std::forward<T>(this->data), std::forward<TT>(other.data)), .f = std::forward<F>(this->f)};
}; };
}; };
@ -471,40 +443,38 @@ concept LeftFoldConcept = decltype(Unpack(std::declval<Tuple>(), []<typename...
return kWrapper<decltype((LeftFoldIsOk<F, T>{} | ... | LeftFoldIsOk<F, Ts>{}))::value>; return kWrapper<decltype((LeftFoldIsOk<F, T>{} | ... | LeftFoldIsOk<F, Ts>{}))::value>;
}))::kValue; }))::kValue;
} // namespace impl export template <TupleLike Tuple, std::move_constructible T, LeftFoldConcept<T, Tuple> F = decltype(kDefaultCreator<void>)&>
template <TupleLike Tuple, std::move_constructible T, impl::LeftFoldConcept<T, Tuple> F = decltype(kDefaultCreator<void>)&>
constexpr auto LeftFold(Tuple&& tuple, T&& init, F&& f = kDefaultCreator<void>) -> decltype(auto) { constexpr auto LeftFold(Tuple&& tuple, T&& init, F&& f = kDefaultCreator<void>) -> decltype(auto) {
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) -> decltype(auto) { return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) -> decltype(auto) {
return (impl::LeftFold<ForwardType<T>, ForwardType<F>>{.data = std::forward<T>(init), .f = std::forward<F>(f)} | ... | return (LeftFoldHelper<ForwardType<T>, ForwardType<F>>{.data = std::forward<T>(init), .f = std::forward<F>(f)} | ... |
impl::LeftFold<ForwardType<Ts>>{.data = std::forward<Ts>(args)}) LeftFoldHelper<ForwardType<Ts>>{.data = std::forward<Ts>(args)})
.data; .data;
}); });
}; };
template <TupleLike Tuple, std::move_constructible T, impl::LeftFoldConcept<T, Tuple> F> export template <TupleLike Tuple, std::move_constructible T, LeftFoldConcept<T, Tuple> F>
constexpr auto Reduce(Tuple&& tuple, T&& init, F&& f) -> decltype(auto) { constexpr auto Reduce(Tuple&& tuple, T&& init, F&& f) -> decltype(auto) {
return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), std::forward<F>(f)); return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), std::forward<F>(f));
}; };
template <typename T, typename F> export template <typename T, typename F>
constexpr auto Reduce(T&& init, F&& f) -> decltype(auto) { constexpr auto Reduce(T&& init, F&& f) -> decltype(auto) {
return [init = std::forward<T>(init), f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) -> decltype(auto) return [init = std::forward<T>(init), f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) -> decltype(auto)
requires impl::LeftFoldConcept<F, T, Tuple> requires LeftFoldConcept<F, T, Tuple>
{ {
return Reduce(std::forward<Tuple>(tuple), std::move(init), std::move(f)); return Reduce(std::forward<Tuple>(tuple), std::move(init), std::move(f));
}; };
}; };
template <TupleLike Tuple, TupleLike Tuple2> export template <TupleLike Tuple, TupleLike Tuple2>
inline constexpr auto TupleCat(Tuple&& tuple, Tuple2&& tuple2) { constexpr auto TupleCat(Tuple&& tuple, Tuple2&& tuple2) {
return [&]<auto... Is, auto... IIs>(std::index_sequence<Is...>, std::index_sequence<IIs...>) { return [&]<auto... Is, auto... IIs>(std::index_sequence<Is...>, std::index_sequence<IIs...>) {
return MakeTuple<Tuple>(Get<Is>(std::forward<Tuple>(tuple))..., Get<IIs>(std::forward<Tuple2>(tuple2))...); return MakeTuple<Tuple>(Get<Is>(std::forward<Tuple>(tuple))..., Get<IIs>(std::forward<Tuple2>(tuple2))...);
}(std::make_index_sequence<kTupleSize<Tuple>>(), std::make_index_sequence<kTupleSize<Tuple2>>()); }(std::make_index_sequence<kTupleSize<Tuple>>(), std::make_index_sequence<kTupleSize<Tuple2>>());
}; };
template <TupleLike... Tuples> export template <TupleLike... Tuples>
inline constexpr auto TupleCat(Tuples&&... tuples) constexpr auto TupleCat(Tuples&&... tuples)
requires(sizeof...(tuples) >= 1) requires(sizeof...(tuples) >= 1)
{ {
return LeftFold(Tuple{std::forward<Tuples>(tuples)...}, return LeftFold(Tuple{std::forward<Tuples>(tuples)...},
@ -514,18 +484,18 @@ inline constexpr auto TupleCat(Tuples&&... tuples)
}); });
}; };
template <TupleLike... Tuples, typename F> export template <TupleLike... Tuples, typename F>
inline constexpr auto Unpack(Tuples&&... tuples, F&& f) -> decltype(Unpack(TupleCat(std::forward<Tuples>(tuples)...), std::forward<F>(f))) { constexpr auto Unpack(Tuples&&... tuples, F&& f) -> decltype(Unpack(TupleCat(std::forward<Tuples>(tuples)...), std::forward<F>(f))) {
return Unpack(TupleCat(std::forward<Tuples>(tuples)...), std::forward<F>(f)); return Unpack(TupleCat(std::forward<Tuples>(tuples)...), std::forward<F>(f));
}; };
template <typename... Ts> export template <typename... Ts>
inline constexpr auto Tie(Ts&... args) -> Tuple<Ts&...> { constexpr auto Tie(Ts&... args) -> Tuple<Ts&...> {
return {args...}; return {args...};
}; };
template <template <typename...> typename F, TupleLike Tuple> template <template <typename...> typename F, TupleLike Tuple>
inline constexpr bool kEveryElement = [](auto... is) { constexpr bool kEveryElement = [](auto... is) {
return (F<std::decay_t<decltype(Get<is>(std::declval<Tuple>()))>>::value && ...); return (F<std::decay_t<decltype(Get<is>(std::declval<Tuple>()))>>::value && ...);
} | kSeq<kTupleSize<Tuple>>; } | kSeq<kTupleSize<Tuple>>;
@ -541,8 +511,8 @@ consteval auto PartialCallerF(TypeList<Ts...>) {
}; };
}; };
template <TupleLike Tuple, std::move_constructible T> export template <TupleLike Tuple, std::move_constructible T>
inline constexpr auto FirstOf(Tuple&& tuple, T&& init) constexpr auto FirstOf(Tuple&& tuple, T&& init)
requires kEveryElement<std::is_invocable, Tuple> requires kEveryElement<std::is_invocable, Tuple>
{ {
return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), []<typename TT, typename F>(TT&& value, F&& f) -> TT { return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), []<typename TT, typename F>(TT&& value, F&& f) -> TT {
@ -553,23 +523,19 @@ inline constexpr auto FirstOf(Tuple&& tuple, T&& init)
}); });
}; };
template <typename T> export template <typename T>
inline constexpr auto FirstOf(T&& init) { constexpr auto FirstOf(T&& init) {
return [init = std::forward<T>(init)]<TupleLike Tuple>(Tuple&& tuple) { return [init = std::forward<T>(init)]<TupleLike Tuple>(Tuple&& tuple) {
return FirstOf(std::forward<Tuple>(tuple), std::move(init)); return FirstOf(std::forward<Tuple>(tuple), std::move(init));
}; };
}; };
namespace impl {
template <typename F, typename Tuple> template <typename F, typename Tuple>
concept FilterConcept = decltype(Unpack(std::declval<Tuple>(), []<typename... Ts>(Ts&&...) { concept FilterConcept = decltype(Unpack(std::declval<Tuple>(), []<typename... Ts>(Ts&&...) {
return kWrapper<(Function<F, bool(Ts)> && ...)>; return kWrapper<(Function<F, bool(Ts)> && ...)>;
}))::kValue; }))::kValue;
} // namespace impl export template <TupleLike Tuple, FilterConcept<Tuple> F>
template <TupleLike Tuple, impl::FilterConcept<Tuple> F>
constexpr auto Filter(Tuple&& tuple, F&& f) { constexpr auto Filter(Tuple&& tuple, F&& f) {
return LeftFold( return LeftFold(
std::forward<Tuple>(tuple), MakeTuple<Tuple>(), [&]<TupleLike Accumulator, typename T>(Accumulator&& accumulator, T&& add) { std::forward<Tuple>(tuple), MakeTuple<Tuple>(), [&]<TupleLike Accumulator, typename T>(Accumulator&& accumulator, T&& add) {
@ -581,16 +547,16 @@ constexpr auto Filter(Tuple&& tuple, F&& f) {
}); });
}; };
template <typename F> export template <typename F>
constexpr auto Filter(F&& f) { constexpr auto Filter(F&& f) {
return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple)
requires impl::FilterConcept<F, Tuple> requires FilterConcept<F, Tuple>
{ {
return Filter(std::forward<Tuple>(tuple), std::move(f)); return Filter(std::forward<Tuple>(tuple), std::move(f));
}; };
}; };
template <TupleLike Tuple, impl::ForEachConcept<Tuple> F> export template <TupleLike Tuple, ForEachConcept<Tuple> F>
constexpr auto ForEach(Tuple&& tuple, F&& f) { constexpr auto ForEach(Tuple&& tuple, F&& f) {
Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) { Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
(f(std::forward<Ts>(args)), ...); (f(std::forward<Ts>(args)), ...);
@ -601,23 +567,23 @@ template <typename F, typename... Ts>
struct Curryer { struct Curryer {
F f; F f;
Tuple<Ts...> data; Tuple<Ts...> data;
inline constexpr operator std::invoke_result_t<F, Ts...>() const { // NOLINT constexpr operator std::invoke_result_t<F, Ts...>() const { // NOLINT
return [&]<auto... Is>(std::index_sequence<Is...>) { return [&]<auto... Is>(std::index_sequence<Is...>) {
return this->f(Get<Is>(this->data)...); return this->f(Get<Is>(this->data)...);
}(std::index_sequence_for<Ts...>()); }(std::index_sequence_for<Ts...>());
}; };
template <typename T> template <typename T>
inline constexpr auto operator()(T&& arg) const -> Curryer<F, Ts..., std::remove_cvref_t<T>> { constexpr auto operator()(T&& arg) const -> Curryer<F, Ts..., std::remove_cvref_t<T>> {
return {.f = this->f, .data = this->data + Tuple{std::forward<T>(arg)}}; return {.f = this->f, .data = this->data + Tuple{std::forward<T>(arg)}};
}; };
}; };
template <typename F> export template <typename F>
inline constexpr auto Curry(F&& f) -> Curryer<std::remove_cvref_t<F>> { constexpr auto Curry(F&& f) -> Curryer<std::remove_cvref_t<F>> {
return {.f = std::forward<F>(f), .data = Tuple{}}; return {.f = std::forward<F>(f), .data = Tuple{}};
}; };
template <TupleLike Tuple, typename T> export template <TupleLike Tuple, typename T>
inline constexpr auto Find(Tuple&& tuple, T&& find) -> std::size_t { constexpr auto Find(Tuple&& tuple, T&& find) -> std::size_t {
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) { return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
using Type = std::remove_cvref_t<T>; using Type = std::remove_cvref_t<T>;
std::array<bool, kTupleSize<Tuple>> bs{Overloaded( std::array<bool, kTupleSize<Tuple>> bs{Overloaded(
@ -640,8 +606,8 @@ inline constexpr auto Find(Tuple&& tuple, T&& find) -> std::size_t {
}); });
}; };
template <std::size_t N, TupleLike Tuple> export template <std::size_t N, TupleLike Tuple>
inline constexpr auto Take(Tuple&& tuple) { constexpr auto Take(Tuple&& tuple) {
if constexpr(std::is_lvalue_reference_v<Tuple> && HasMakeTie<Tuple>) { if constexpr(std::is_lvalue_reference_v<Tuple> && HasMakeTie<Tuple>) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) { return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return MakeTie<Tuple>(Get<Is>(std::forward<Tuple>(tuple))...); return MakeTie<Tuple>(Get<Is>(std::forward<Tuple>(tuple))...);
@ -653,22 +619,22 @@ inline constexpr auto Take(Tuple&& tuple) {
}; };
}; };
template <std::size_t N> export template <std::size_t N>
consteval auto Take() { consteval auto Take() {
return [&]<TupleLike Tuple>(Tuple&& tuple) { return [&]<TupleLike Tuple>(Tuple&& tuple) {
return Take<N>(std::forward<Tuple>(tuple)); return Take<N>(std::forward<Tuple>(tuple));
}; };
}; };
template <TupleLike Tuple, std::move_constructible T> export template <TupleLike Tuple, std::move_constructible T>
inline constexpr auto operator<<(Tuple&& tuple, T&& t) { constexpr auto operator<<(Tuple&& tuple, T&& t) {
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) { return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
return MakeTuple<Tuple>(std::forward<Ts>(args)..., std::forward<T>(t)); return MakeTuple<Tuple>(std::forward<Ts>(args)..., std::forward<T>(t));
}); });
}; };
template <std::size_t N, TupleLike Tuple = Tuple<>, typename T> export template <std::size_t N, TupleLike Tuple = Tuple<>, typename T>
inline constexpr auto Generate(T&& value) constexpr auto Generate(T&& value)
requires(std::copyable<T> || N == 1 && std::move_constructible<T>) requires(std::copyable<T> || N == 1 && std::move_constructible<T>)
{ {
if constexpr(N == 1) { if constexpr(N == 1) {
@ -680,7 +646,7 @@ inline constexpr auto Generate(T&& value)
}; };
}; };
template <TupleLike Tuple> export template <TupleLike Tuple>
constexpr auto Enumerate(Tuple&& tuple) { constexpr auto Enumerate(Tuple&& tuple) {
return Unpack(std::forward<Tuple>(tuple), [](auto&&... vs) { return Unpack(std::forward<Tuple>(tuple), [](auto&&... vs) {
return [&](auto... is) { return [&](auto... is) {
@ -689,8 +655,6 @@ constexpr auto Enumerate(Tuple&& tuple) {
}); });
}; };
namespace impl {
template <typename Key, typename KeysTuple> template <typename Key, typename KeysTuple>
concept ComparableSwitchConcept = decltype(Unpack(std::declval<KeysTuple>(), []<typename... Ts>(Ts&&... args) { concept ComparableSwitchConcept = decltype(Unpack(std::declval<KeysTuple>(), []<typename... Ts>(Ts&&... args) {
return kWrapper<(requires { args == std::declval<Key>(); } && ...)>; return kWrapper<(requires { args == std::declval<Key>(); } && ...)>;
@ -701,15 +665,13 @@ concept CallableSwitchConcept = std::same_as<R, void> || decltype(Unpack(std::de
return kWrapper<(Function<F, std::optional<R>(Ts)> && ...)>; return kWrapper<(Function<F, std::optional<R>(Ts)> && ...)>;
}))::kValue; }))::kValue;
} // namespace impl export template <typename R = void,
TupleLike KeysTuple,
template <typename R = void, TupleLike ValuesTuple,
TupleLike KeysTuple, ComparableSwitchConcept<KeysTuple> Key,
TupleLike ValuesTuple, CallableSwitchConcept<ValuesTuple, R> F,
impl::ComparableSwitchConcept<KeysTuple> Key, Function<R()> Default = std::add_lvalue_reference_t<decltype(kDefaultCreator<R>)>>
impl::CallableSwitchConcept<ValuesTuple, R> F, constexpr auto Switch(KeysTuple&& keysTuple, ValuesTuple&& valuesTuple, Key&& key, F&& f, Default&& def = kDefaultCreator<R>) -> R
Function<R()> Default = decltype(kDefaultCreator<R>)>
constexpr auto Switch(KeysTuple&& keysTuple, ValuesTuple&& valuesTuple, Key&& key, F&& f, Default&& def = {}) -> R
requires(std::move_constructible<R> || std::same_as<R, void>) && (kTupleSize<KeysTuple> == kTupleSize<ValuesTuple>) requires(std::move_constructible<R> || std::same_as<R, void>) && (kTupleSize<KeysTuple> == kTupleSize<ValuesTuple>)
{ {
return Unpack(std::forward<KeysTuple>(keysTuple), [&]<typename... Keys>(Keys&&... keys) { return Unpack(std::forward<KeysTuple>(keysTuple), [&]<typename... Keys>(Keys&&... keys) {
@ -740,14 +702,14 @@ constexpr auto Switch(KeysTuple&& keysTuple, ValuesTuple&& valuesTuple, Key&& ke
}); });
}; };
template <std::size_t N, typename R = Tuple<>> export template <std::size_t N, typename R = Tuple<>>
consteval auto GetIndexesTuple() { consteval auto GetIndexesTuple() {
return [](auto... is) { return [](auto... is) {
return MakeTuple<R>(*is...); return MakeTuple<R>(*is...);
} | kSeq<N>; } | kSeq<N>;
}; };
template <typename R = Tuple<>, typename... Ts> export template <typename R = Tuple<>, typename... Ts>
consteval auto GetTuple(TypeList<Ts...>, TypeList<R> = {}) { consteval auto GetTuple(TypeList<Ts...>, TypeList<R> = {}) {
return MakeTuple<R>(kType<Ts>...); return MakeTuple<R>(kType<Ts>...);
}; };

View file

@ -1,5 +1,7 @@
module;
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <utempl/go_interface.hpp> export module tests.go_interface;
import utempl.go_interface;
namespace utempl { namespace utempl {
@ -30,5 +32,4 @@ TEST(GoInterface, Equal) {
EXPECT_NE(obj, SomeStruct{1}); EXPECT_NE(obj, SomeStruct{1});
EXPECT_EQ(obj, SomeStruct2{1}); EXPECT_EQ(obj, SomeStruct2{1});
}; };
} // namespace utempl } // namespace utempl

View file

@ -1,5 +1,7 @@
module;
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <utempl/menu.hpp> export module tests.menu;
import utempl.menu;
namespace utempl { namespace utempl {
@ -8,14 +10,14 @@ TEST(Menu, Basic) {
std::istringstream stream("t\n"); std::istringstream stream("t\n");
int value = 0; int value = 0;
menu::Menu{} menu::Menu{}
.With<{"t", "This is t"}>([&]{ .With<{"t", "This is t"}>([&] {
std::cout << "Success!" << std::endl; std::cout << "Success!" << std::endl;
value = 1; value = 1;
}) })
.Run<"[{0}]{2} - ({1})\n">(stream); .Run<"[{0}]{2} - ({1})\n">(stream);
auto captured = testing::internal::GetCapturedStdout(); auto captured = testing::internal::GetCapturedStdout();
EXPECT_EQ(captured, "[t] - (This is t)\n|> Success!\n"); EXPECT_EQ(captured, "[t] - (This is t)\n|> Success!\n");
EXPECT_EQ(value, 1); EXPECT_EQ(value, 1);
}; };
} // namespace utempl } // namespace utempl

View file

@ -1,16 +1,20 @@
module;
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <utempl/overloaded.hpp> export module tests.overloaded;
import utempl.overloaded;
namespace utempl { namespace utempl {
TEST(Overloaded, Basic) { TEST(Overloaded, Basic) {
constexpr auto f = Overloaded([](int){ constexpr auto f = Overloaded(
return 1; [](int) {
}, [](auto&&){ return 1;
return 2; },
}); [](auto&&) {
return 2;
});
EXPECT_EQ(f(1), 1); EXPECT_EQ(f(1), 1);
EXPECT_EQ(f(""), 2); EXPECT_EQ(f(""), 2);
}; };
} // namespace utempl } // namespace utempl