#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/StringRef.h" #include "revng/ADT/Concepts.h" #include "revng/ADT/TypeList.h" namespace compile_time { /// Calls \ref Callable with unpacked sequence of \ref IterationCount. /// Example: /// ```cpp /// compile_time::callWithIndexSequence<3>([]() { /// // The parameter pack I is composed of 0, 1, 2 in this example /// }); /// ``` template constexpr auto callWithIndexSequence(CallableType &&Callable) { auto Runner = [&Callable](std::index_sequence) { return Callable.template operator()(); }; return Runner(std::make_index_sequence{}); } /// Calls \ref Callable with unpacked sequence of the size of tuple-like /// \ref TupleType. See the documentation for the size_t counterpart for usage. template constexpr auto callWithIndexSequence(CallableType &&Callable) { return callWithIndexSequence>(Callable); } namespace detail { template using RVHelper = decltype(std::declval().template operator()<0>()); template requires(IterationCount > 0) constexpr auto repeat(CallableType &&Callable) { return callWithIndexSequence([&Callable]() { if constexpr (std::is_same_v, void>) (Callable.template operator()(), ...); else return std::tie(Callable.template operator()()...); }); } } // namespace detail /// Calls \ref Callable \ref IterationCount times. template constexpr auto repeat(CallableType &&Callable) { if constexpr (IterationCount == 0) return; else return detail::repeat(std::forward(Callable)); } /// Calls \ref Callable \ref IterationCount times, while applying logical AND /// operation to the return values. template constexpr bool repeatAnd(CallableType &&Callable) { return callWithIndexSequence([&Callable]() { return (Callable.template operator()() && ...); }); } /// Calls \ref Callable \ref IterationCount times, while applying logical OR /// operation to the return values. template constexpr bool repeatOr(CallableType &&Callable) { return callWithIndexSequence([&Callable]() { return (Callable.template operator()() || ...); }); } /// Calls \ref Callable \ref IterationCount times, returns the amount of times /// the \ref Callable returned a truthy value. template constexpr size_t count(CallableType &&Callable) { return callWithIndexSequence([&Callable]() { return ((Callable.template operator()() ? 1 : 0) + ...); }); } /// Calls \ref Callable \ref IterationCount times, makes sure at most one of /// those invocations has a non-zero return value, then returns its index if /// there is one, or `std::nullopt` if there's none. template constexpr std::optional select(CallableType &&Callable) { return callWithIndexSequence([&Callable]() { std::array Results; ((Results[I] = Callable.template operator()()), ...); if constexpr (std::ranges::count(Results, true) == 1) { auto It = std::ranges::find(Results, true); return std::distance(Results.begin(), It); } else { return std::nullopt; } }); } namespace detail { template inline constexpr bool split(std::array &Result, llvm::StringRef Separator, llvm::StringRef Input) { size_t Position = Input.find(Separator); if constexpr (I < N - 1) { if (Position == llvm::StringRef::npos) return false; Result[I] = Input.substr(0, Position); return split(Result, Separator, Input.substr(Position + 1)); } else { if (Position != llvm::StringRef::npos) return false; Result[I] = Input; return true; } } } // namespace detail /// I'm forced to implement my own split because `llvm::StringRef`'s alternative /// is not `constexpr`-compatible. /// /// This also uses `llvm::StringRef` instead of `llvm::StringRef` because its /// `find` member is constexpr - hence at least that member doesn't have to be /// reimplemented template inline constexpr std::optional> split(llvm::StringRef Separator, llvm::StringRef Input) { if (std::array Result; detail::split(Result, Separator, Input)) return Result; else return std::nullopt; } namespace detail { template struct ArrayTraits {}; template struct ArrayTraits { using value_type = T; static constexpr size_t Size = N; }; template struct ArrayTraits> { using value_type = T; static constexpr size_t Size = N; }; } // namespace detail /// Helper struct that reports the value_type and Size of an array at /// compile-time template using ArrayTraits = detail::ArrayTraits>; namespace detail { template struct FunctionTraits {}; template struct FunctionTraits { using ReturnType = ReturnT; using Arguments = TypeList; }; template struct FunctionTraits { using ReturnType = ReturnT; using Arguments = TypeList; }; } // namespace detail /// Helper using that will return a struct defining the return type and /// arguments of a function. Supports both function references and function /// pointers. template requires std::is_function_v> or std::is_function_v> using FunctionTraits = detail::FunctionTraits; /// Helper function that converts a pack of booleans to their constexpr /// counterpart, this is useful to e.g. have a type depending on a command-line /// option. Do note that the compiler will expand this to all the possible /// combinations, so given N booleans there will be 2**N template expansions. template requires(std::is_same_v and ...) inline constexpr auto invokeCombination(CallableT &&Callable, bool Bool, BoolType... BoolRest) { if constexpr (sizeof...(BoolType) > 0) { // Recursively call this functions with the remaining booleans if (Bool) { auto NextCallable = [&Callable]() { return Callable.template operator()(); }; return invokeCombination(NextCallable, BoolRest...); } else { auto NextCallable = [&Callable]() { return Callable.template operator()(); }; return invokeCombination(NextCallable, BoolRest...); } } else { // We got to the last boolean, call `Callable` with the correct value if (Bool) return Callable.template operator()(); else return Callable.template operator()(); } } } // namespace compile_time