From e50ec72e939ecb496da6f18a88295cb4c70459d2 Mon Sep 17 00:00:00 2001 From: Giuseppe Picciuca Date: Fri, 31 Jan 2025 10:48:25 +0100 Subject: [PATCH] Backport of some concepts from util to C++17 --- benchmark/BenchmarkExamples.cpp | 16 +- .../BenchmarkMeasurementContainer.h | 4 +- benchmark/infrastructure/BenchmarkToJson.cpp | 12 +- src/engine/idTable/IdTableRow.h | 6 +- src/global/Pattern.h | 28 +- src/index/StringSortComparator.h | 4 +- src/util/Algorithm.h | 6 +- src/util/CancellationHandle.h | 31 +- src/util/ConfigManager/ConfigManager.cpp | 124 ++++---- src/util/ConfigManager/ConfigManager.h | 271 ++++++++++-------- src/util/ConfigManager/ConfigOption.h | 34 ++- src/util/ConfigManager/ConfigOptionProxy.h | 10 +- src/util/ConfigManager/Validator.h | 66 ++--- src/util/ConstexprSmallString.h | 2 +- src/util/Exception.h | 7 +- src/util/Generator.h | 15 +- src/util/ProgramOptionsHelpers.h | 5 +- src/util/Serializer/SerializeArrayOrTuple.h | 16 +- src/util/Serializer/SerializeOptional.h | 2 +- src/util/Serializer/SerializeVector.h | 2 +- src/util/Serializer/Serializer.h | 151 +++++++--- src/util/Timer.h | 5 +- src/util/TypeTraits.h | 36 ++- src/util/Views.h | 13 +- test/ConfigManagerTest.cpp | 29 +- test/ValidatorTest.cpp | 7 +- 26 files changed, 543 insertions(+), 359 deletions(-) diff --git a/benchmark/BenchmarkExamples.cpp b/benchmark/BenchmarkExamples.cpp index e419b96feb..4523bc1034 100644 --- a/benchmark/BenchmarkExamples.cpp +++ b/benchmark/BenchmarkExamples.cpp @@ -53,14 +53,14 @@ class ConfigOptions : public BenchmarkInterface { manager.addOption("date", "The current date.", &dateString_, "22.3.2023"s); - auto numSigns = - manager.addOption("num-signs", "The number of street signs.", - &numberOfStreetSigns_, 10000); - manager.addValidator([](const int& num) { return num >= 0; }, - "The number of street signs must be at least 0!", - "Negative numbers, or floating point numbers, are not " - "allowed for the configuration option \"num-signs\".", - numSigns); + // auto numSigns = + // manager.addOption("num-signs", "The number of street signs.", + // &numberOfStreetSigns_, 10000); + // manager.addValidator([](const int& num) -> bool { return num >= 0; }, + // "The number of street signs must be at least 0!", + // "Negative numbers, or floating point numbers, are not " + // "allowed for the configuration option \"num-signs\".", + // numSigns); manager.addOption("coin-flip-try", "The number of successful coin flips.", &wonOnTryX_, {false, false, false, false, false}); diff --git a/benchmark/infrastructure/BenchmarkMeasurementContainer.h b/benchmark/infrastructure/BenchmarkMeasurementContainer.h index 7700e1fd18..98988f776f 100644 --- a/benchmark/infrastructure/BenchmarkMeasurementContainer.h +++ b/benchmark/infrastructure/BenchmarkMeasurementContainer.h @@ -227,8 +227,8 @@ class ResultTable : public BenchmarkMetadataGetter { @param row, column Which table entry to read. Starts with `(0,0)`. */ - template T> - T getEntry(const size_t row, const size_t column) const { + CPP_template(typename T)(requires ad_utility::SameAsAnyTypeIn) T + getEntry(const size_t row, const size_t column) const { AD_CONTRACT_CHECK(row < numRows() && column < numColumns()); static_assert(!ad_utility::isSimilar); diff --git a/benchmark/infrastructure/BenchmarkToJson.cpp b/benchmark/infrastructure/BenchmarkToJson.cpp index b5430304bf..897f30c731 100644 --- a/benchmark/infrastructure/BenchmarkToJson.cpp +++ b/benchmark/infrastructure/BenchmarkToJson.cpp @@ -31,12 +31,12 @@ provided translation function for the vector entries. @tparam TranslationFunction Has to be a function, that takes `VectorType` and returns a `nlohmann:json` object. */ -template - TranslationFunction> -static nlohmann::json transformIntoJsonArray( - const std::vector& vec, - TranslationFunction translationFunction) { +CPP_template(typename VectorType, typename TranslationFunction)( + requires ad_utility::InvocableWithExactReturnType< + TranslationFunction, nlohmann::ordered_json, + VectorType>) static nlohmann::json + transformIntoJsonArray(const std::vector& vec, + TranslationFunction translationFunction) { /* Without explicit instantiation, `nlohmann::nlohmann::ordered_json` is not guaranteed, to always interpret a `push_back` correctly. For instance, diff --git a/src/engine/idTable/IdTableRow.h b/src/engine/idTable/IdTableRow.h index 23290d2305..d661c4fff8 100644 --- a/src/engine/idTable/IdTableRow.h +++ b/src/engine/idTable/IdTableRow.h @@ -381,8 +381,10 @@ class RowReference // The `cbegin` and `cend` functions are implicitly inherited from `Base`. // __________________________________________________________________________ - template R> - friend void swap(R&& a, R&& b) requires(!isConst) { + CPP_template(typename R)( + requires ad_utility::SimilarTo) friend void swap(R&& a, + R&& b) + requires(!isConst) { return Base::swapImpl(AD_FWD(a), AD_FWD(b)); } diff --git a/src/global/Pattern.h b/src/global/Pattern.h index 1005add22d..bdf8ea8412 100644 --- a/src/global/Pattern.h +++ b/src/global/Pattern.h @@ -13,6 +13,7 @@ #include #include "global/Id.h" +#include "backports/concepts.h" #include "util/ExceptionHandling.h" #include "util/File.h" #include "util/Generator.h" @@ -71,6 +72,27 @@ struct CompactStringVectorWriter; } +#ifdef QLEVER_CPP_17 +template +struct IsIteratorOfIterator : std::false_type {}; + +template +struct IsIteratorOfIterator< + T, std::void_t> + : std::true_type {}; + +template +CPP_concept IteratorOfIterator = + (IsIteratorOfIterator::value && + ad_utility::SimilarTo); +#else +template +concept IteratorOfIterator = requires(T t) { + { *(t.begin()->begin()) } -> ad_utility::SimilarTo; +}; +#endif + /** * @brief Stores a list of variable length data of a single type (e.g. * c-style strings). The data is stored in a single contiguous block @@ -102,10 +124,8 @@ class CompactVectorOfStrings { * @brief Fills this CompactVectorOfStrings with input. * @param The input from which to build the vector. */ - template - requires requires(T t) { - { *(t.begin()->begin()) } -> ad_utility::SimilarTo; - } void build(const T& input) { + CPP_template(typename T)( + requires IteratorOfIterator) void build(const T& input) { // Also make room for the end offset of the last element. _offsets.reserve(input.size() + 1); size_t dataSize = 0; diff --git a/src/index/StringSortComparator.h b/src/index/StringSortComparator.h index 81829f226e..dfcbca0a25 100644 --- a/src/index/StringSortComparator.h +++ b/src/index/StringSortComparator.h @@ -51,8 +51,8 @@ class LocaleManager { using U8String = std::basic_string; using U8StringView = std::basic_string_view; - template T> - class SortKeyImpl { + CPP_template(typename T)(requires ad_utility::SimilarToAny< + T, U8String, U8StringView>) class SortKeyImpl { public: SortKeyImpl() = default; explicit SortKeyImpl(U8StringView sortKey) : sortKey_(sortKey) {} diff --git a/src/util/Algorithm.h b/src/util/Algorithm.h index 50e23f8244..50c1314c46 100644 --- a/src/util/Algorithm.h +++ b/src/util/Algorithm.h @@ -58,8 +58,10 @@ bool contains_if(const Container& container, const Predicate& predicate) { * @param destination Vector& to which to append * @param source Vector&& to append */ -template > U> -void appendVector(std::vector& destination, U&& source) { +CPP_template(typename T, typename U)( + requires ad_utility::SimilarTo< + std::vector, U>) void appendVector(std::vector& destination, + U&& source) { destination.insert(destination.end(), ad_utility::makeForwardingIterator(source.begin()), ad_utility::makeForwardingIterator(source.end())); diff --git a/src/util/CancellationHandle.h b/src/util/CancellationHandle.h index 28a445684c..8addadb30b 100644 --- a/src/util/CancellationHandle.h +++ b/src/util/CancellationHandle.h @@ -2,8 +2,8 @@ // Chair of Algorithms and Data Structures. // Author: Robin Textor-Falconi -#ifndef QLEVER_CANCELLATIONHANDLE_H -#define QLEVER_CANCELLATIONHANDLE_H +#ifndef SRC_UTIL_CANCELLATION_HANDLE_H +#define SRC_UTIL_CANCELLATION_HANDLE_H #include #include @@ -156,10 +156,14 @@ class CancellationHandle { /// in the console that would otherwise be triggered by the watchdog. /// NOTE: The parameter state is expected to be one of `CHECK_WINDOW_MISSED` /// or `WAITING_FOR_CHECK`, otherwise it will violate the correctness check. - void pleaseWatchDog(CancellationState state, - ad_utility::source_location location, - const ad_utility::InvocableWithConvertibleReturnType< - std::string_view> auto& stageInvocable) + CPP_template(typename StateFunc)( + requires ad_utility::InvocableWithConvertibleReturnType< + StateFunc, + std::string_view>) void pleaseWatchDog(CancellationState state, + ad_utility::source_location + location, + const StateFunc& + stageInvocable) requires WatchDogEnabled { using DurationType = std::remove_const_t; @@ -215,12 +219,13 @@ class CancellationHandle { /// nothing otherwise. If `WatchDogEnabled` is true, this will log a warning /// if this check is not called frequently enough. It will contain the /// filename and line of the caller of this method. - template - Func = decltype(detail::printNothing)> - AD_ALWAYS_INLINE void throwIfCancelled( - [[maybe_unused]] ad_utility::source_location location = - ad_utility::source_location::current(), - const Func& stageInvocable = detail::printNothing) { + CPP_template(typename Func = decltype(detail::printNothing))( + requires ad_utility::InvocableWithConvertibleReturnType) + AD_ALWAYS_INLINE void throwIfCancelled( + [[maybe_unused]] ad_utility::source_location location = + ad_utility::source_location::current(), + const Func& stageInvocable = detail::printNothing) { if constexpr (CancellationEnabled) { auto state = cancellationState_.load(std::memory_order_relaxed); if (state == CancellationState::NOT_CANCELLED) [[likely]] { @@ -296,4 +301,4 @@ class CancellationHandle { using SharedCancellationHandle = std::shared_ptr>; } // namespace ad_utility -#endif // QLEVER_CANCELLATIONHANDLE_H +#endif // SRC_UTIL_CANCELLATION_HANDLE_H diff --git a/src/util/ConfigManager/ConfigManager.cpp b/src/util/ConfigManager/ConfigManager.cpp index 3691c2588a..8ae5d0a727 100644 --- a/src/util/ConfigManager/ConfigManager.cpp +++ b/src/util/ConfigManager/ConfigManager.cpp @@ -45,8 +45,10 @@ ConfigManager::HashMapEntry::HashMapEntry(Data&& data) : data_{std::make_unique(std::move(data))} {} // ____________________________________________________________________________ -template T> -bool ConfigManager::HashMapEntry::implHolds() const { +CPP_template_def(typename T)( + requires SameAsAnyTypeIn< + T, ConfigManager::HashMapEntry::Data>) bool ConfigManager:: + HashMapEntry::implHolds() const { // Make sure, that it is not a null pointer. AD_CORRECTNESS_CHECK(data_); @@ -64,10 +66,13 @@ bool ConfigManager::HashMapEntry::holdsSubManager() const { } // ____________________________________________________________________________ -template ReturnType> -requires std::is_object_v std::optional -ConfigManager::HashMapEntry::getConfigOptionOrSubManager( - ad_utility::SimilarTo auto& instance) { +CPP_template_def(typename ReturnType, typename InstanceType)( + requires SimilarToAnyTypeIn + CPP_and std::is_object_v + CPP_and ad_utility::SimilarTo) + std::optional ConfigManager::HashMapEntry:: + getConfigOptionOrSubManager(InstanceType& instance) { using DecayReturnType = std::decay_t; /* We cheat a bit, by using `implHolds`, because so we can reduce the amount @@ -104,32 +109,31 @@ std::optional ConfigManager::HashMapEntry::getSubManager() } // ____________________________________________________________________________ -template -requires std::invocable && - std::invocable -decltype(auto) ConfigManager::HashMapEntry::visit(Visitor&& vis) { +CPP_template_def(typename Visitor)( + requires std::invocable CPP_and + std::invocable) decltype(auto) + ConfigManager::HashMapEntry::visit(Visitor&& vis) { return visitImpl(AD_FWD(vis), data_); } -template -requires std::invocable && - std::invocable -decltype(auto) ConfigManager::HashMapEntry::visit(Visitor&& vis) const { +CPP_template_def(typename Visitor)( + requires std::invocable CPP_and + std::invocable) decltype(auto) + ConfigManager::HashMapEntry::visit(Visitor&& vis) const { return visitImpl(AD_FWD(vis), data_); } // ____________________________________________________________________________ -template < - typename Visitor, - ad_utility::SimilarTo> - PointerType> -requires std::invocable< - Visitor, std::conditional_t, - const ConfigOption&, ConfigOption&>> && - std::invocable< - Visitor, std::conditional_t, - const ConfigManager&, ConfigManager&>> -decltype(auto) ConfigManager::HashMapEntry::visitImpl(Visitor&& vis, - PointerType& data) { +CPP_template_def(typename Visitor, typename PointerType)( + requires ad_utility::SimilarTo< + std::unique_ptr, PointerType> + CPP_and std::invocable< + Visitor, std::conditional_t, + const ConfigOption&, ConfigOption&>> + CPP_and std::invocable< + Visitor, std::conditional_t, + const ConfigManager&, + ConfigManager&>>) decltype(auto) + ConfigManager::HashMapEntry::visitImpl(Visitor&& vis, PointerType& data) { // Make sure, that it is not a null pointer. AD_CORRECTNESS_CHECK(data); @@ -150,13 +154,14 @@ void ConfigManager::verifyHashMapEntry(std::string_view jsonPathToEntry, } // ____________________________________________________________________________ -template -requires ad_utility::InvocableWithExactReturnType< - Visitor, void, std::string_view, ConfigManager&> && - ad_utility::InvocableWithExactReturnType< - Visitor, void, std::string_view, ConfigOption&> -void ConfigManager::visitHashMapEntries(Visitor&& vis, bool sortByCreationOrder, - std::string_view pathPrefix) const { +CPP_template_def(typename Visitor)( + requires ad_utility::InvocableWithExactReturnType< + Visitor, void, std::string_view, ConfigManager&> + CPP_and ad_utility::InvocableWithExactReturnType< + Visitor, void, std::string_view, + ConfigOption&>) void ConfigManager:: + visitHashMapEntries(Visitor&& vis, bool sortByCreationOrder, + std::string_view pathPrefix) const { // For less code duplication. using Pair = decltype(configurationOptions_)::value_type; @@ -188,17 +193,18 @@ void ConfigManager::visitHashMapEntries(Visitor&& vis, bool sortByCreationOrder, } // ____________________________________________________________________________ -template < - SimilarTo> +CPP_template_def(typename HashMapType, typename Callable)( + requires SimilarTo< + ad_utility::HashMap, HashMapType> -requires std::is_object_v auto ConfigManager::allHashMapEntries( - HashMapType& hashMap, std::string_view pathPrefix, - const ad_utility::InvocableWithSimilarReturnType< - bool, const HashMapEntry&> auto& predicate) - -> std::conditional_t< - std::is_const_v, - const std::vector>, - std::vector>> { + CPP_and std::is_object_v) auto ConfigManager:: + allHashMapEntries(HashMapType& hashMap, std::string_view pathPrefix, + const Callable& predicate) + -> std::conditional_t< + std::is_const_v, + const std::vector< + std::pair>, + std::vector>> { std::conditional_t< std::is_const_v, std::vector>, @@ -255,12 +261,13 @@ requires std::is_object_v auto ConfigManager::allHashMapEntries( } // ____________________________________________________________________________ -template ) - ReturnReference> -std::vector> -ConfigManager::configurationOptionsImpl( - SimilarTo> auto& - configurationOptions) { +CPP_template_def(typename ConfigOptions, typename ReturnReference)( + requires SameAsAny + CPP_and SimilarTo< + ad_utility::HashMap, + ConfigOptions>) + std::vector> ConfigManager:: + configurationOptionsImpl(ConfigOptions& configurationOptions) { return ad_utility::transform( allHashMapEntries( configurationOptions, "", @@ -274,13 +281,15 @@ ConfigManager::configurationOptionsImpl( // ____________________________________________________________________________ std::vector> ConfigManager::configurationOptions() { - return configurationOptionsImpl(configurationOptions_); + return configurationOptionsImpl(configurationOptions_); } // ____________________________________________________________________________ std::vector> ConfigManager::configurationOptions() const { - return configurationOptionsImpl(configurationOptions_); + return configurationOptionsImpl(configurationOptions_); } // ____________________________________________________________________________ @@ -857,9 +866,10 @@ bool ConfigManager::containsOption(const ConfigOption& opt) const { } // ____________________________________________________________________________ -template -void ConfigManager::ConfigurationDocValidatorAssignment::addEntryUnderKey( - const T& key, const ConfigOptionValidatorManager& manager) { +CPP_template_def(typename T)( + requires ConfigOptionOrManager) void ConfigManager:: + ConfigurationDocValidatorAssignment::addEntryUnderKey( + const T& key, const ConfigOptionValidatorManager& manager) { getHashMapBasedOnType()[&key].push_back(&manager); } // Explicit instantiation for `ConfigOption` and `ConfigManager`. @@ -871,9 +881,9 @@ template void ConfigManager::ConfigurationDocValidatorAssignment:: const ConfigOptionValidatorManager&); // ____________________________________________________________________________ -template -auto ConfigManager::ConfigurationDocValidatorAssignment::getEntriesUnderKey( - const T& key) const -> ValueGetterReturnType { +CPP_template(typename T)(requires ConfigOptionOrManager) auto ConfigManager:: + ConfigurationDocValidatorAssignment::getEntriesUnderKey(const T& key) const + -> ValueGetterReturnType { // The concerned hash map. const MemoryAdressHashMap& hashMap{getHashMapBasedOnType()}; diff --git a/src/util/ConfigManager/ConfigManager.h b/src/util/ConfigManager/ConfigManager.h index 48691e96c5..dacd7093f2 100644 --- a/src/util/ConfigManager/ConfigManager.h +++ b/src/util/ConfigManager/ConfigManager.h @@ -89,19 +89,19 @@ class ConfigManager { size_t getInitializationId() const; // Wrapper for calling `std::visit` on the saved `Data`. - template - requires std::invocable && - std::invocable - decltype(auto) visit(Visitor&& vis); - template - requires std::invocable && - std::invocable - decltype(auto) visit(Visitor&& vis) const; + CPP_template(typename Visitor)( + requires std::invocable CPP_and + std::invocable) decltype(auto) + visit(Visitor&& vis); + CPP_template(typename Visitor)( + requires std::invocable CPP_and + std::invocable) decltype(auto) + visit(Visitor&& vis) const; private: // Implementation for `holdsConfigOption` and `holdsSubManager`. - template T> - bool implHolds() const; + CPP_template(typename T)( + requires SameAsAnyTypeIn) bool implHolds() const; /* @brief Implementation for `getConfigOption` and `getSubManager`. You can @@ -113,25 +113,30 @@ class ConfigManager { @SameAsAnym instance The `HashMapEntry` you want this from. */ - template ReturnType> - requires std::is_object_v - static std::optional getConfigOptionOrSubManager( - ad_utility::SimilarTo auto& instance); + CPP_template(typename ReturnType, typename InstanceType)( + requires SimilarToAnyTypeIn CPP_and + std::is_object_v + CPP_and ad_utility::SimilarTo) static std:: + optional getConfigOptionOrSubManager( + InstanceType& instance); /* @brief The implementation for `visit`. Follows the same signature as `std::variant::visit`: */ - template > - PointerType> - requires std::invocable, - const ConfigOption&, ConfigOption&>> && - std::invocable, - const ConfigManager&, ConfigManager&>> - static decltype(auto) visitImpl(Visitor&& vis, PointerType& data); + CPP_template(typename Visitor, typename PointerType)( + requires ad_utility::SimilarTo< + std::unique_ptr, PointerType> + CPP_and std::invocable< + Visitor, std::conditional_t, + const ConfigOption&, ConfigOption&>> + CPP_and std::invocable< + Visitor, + std::conditional_t, + const ConfigManager&, + ConfigManager&>>) static decltype(auto) + visitImpl(Visitor&& vis, PointerType& data); }; /* @@ -173,11 +178,12 @@ class ConfigManager { @return A reference to the newly created configuration option. This reference will stay valid, even after adding more options. */ - template - ConstConfigOptionProxy addOption( - const std::vector& pathToOption, - std::string_view optionDescription, - OptionType* variableToPutValueOfTheOptionIn) { + CPP_template(typename OptionType)( + requires SupportedConfigOptionType) + ConstConfigOptionProxy addOption( + const std::vector& pathToOption, + std::string_view optionDescription, + OptionType* variableToPutValueOfTheOptionIn) { return addOptionImpl(pathToOption, optionDescription, variableToPutValueOfTheOptionIn, std::optional(std::nullopt)); @@ -201,13 +207,14 @@ class ConfigManager { @return A reference to the newly created configuration option. This reference will stay valid, even after adding more options. */ - template DefaultValueType = OptionType> - ConstConfigOptionProxy addOption( - const std::vector& pathToOption, - std::string_view optionDescription, - OptionType* variableToPutValueOfTheOptionIn, - DefaultValueType defaultValue) { + CPP_template(typename OptionType, typename DefaultValueType = OptionType)( + requires SupportedConfigOptionType CPP_and + std::same_as) + ConstConfigOptionProxy addOption( + const std::vector& pathToOption, + std::string_view optionDescription, + OptionType* variableToPutValueOfTheOptionIn, + DefaultValueType defaultValue) { return addOptionImpl(pathToOption, optionDescription, variableToPutValueOfTheOptionIn, std::optional(std::move(defaultValue))); @@ -221,10 +228,11 @@ class ConfigManager { @return A reference to the newly created configuration option. This reference will stay valid, even after adding more options. */ - template - ConstConfigOptionProxy addOption( - std::string optionName, std::string_view optionDescription, - OptionType* variableToPutValueOfTheOptionIn) { + CPP_template(typename OptionType)( + requires SupportedConfigOptionType) + ConstConfigOptionProxy addOption( + std::string optionName, std::string_view optionDescription, + OptionType* variableToPutValueOfTheOptionIn) { return addOption( std::vector{std::move(optionName)}, optionDescription, variableToPutValueOfTheOptionIn); @@ -238,12 +246,13 @@ class ConfigManager { @return A reference to the newly created configuration option. This reference will stay valid, even after adding more options. */ - template DefaultValueType = OptionType> - ConstConfigOptionProxy addOption( - std::string optionName, std::string_view optionDescription, - OptionType* variableToPutValueOfTheOptionIn, - DefaultValueType defaultValue) { + CPP_template(typename OptionType, typename DefaultValueType = OptionType)( + requires SupportedConfigOptionType CPP_and + std::same_as) + ConstConfigOptionProxy addOption( + std::string optionName, std::string_view optionDescription, + OptionType* variableToPutValueOfTheOptionIn, + DefaultValueType defaultValue) { return addOption( std::vector{std::move(optionName)}, optionDescription, variableToPutValueOfTheOptionIn, std::move(defaultValue)); @@ -314,19 +323,24 @@ class ConfigManager { values will be passed to the validator function as function arguments. Will keep the same order. */ - template - void addValidator( - ValidatorFunction auto validatorFunction, - std::string errorMessage, std::string validatorDescriptor, - ConstConfigOptionProxy< - ValidatorParameterTypes>... configOptionsToBeChecked) - requires(sizeof...(configOptionsToBeChecked) > 0) { + CPP_template(typename ValidatorFunc, typename... ValidatorParameterTypes)( + requires(sizeof...(ValidatorParameterTypes) > 0) CPP_and ValidatorFunction< + ValidatorFunc, + ValidatorParameterTypes...>) void addValidator(ValidatorFunc + validatorFunction, + std::string + errorMessage, + std::string + validatorDescriptor, + ConstConfigOptionProxy< + ValidatorParameterTypes>... configOptionsToBeChecked) { addValidatorImpl( "addValidator", [](ConstConfigOptionProxy opt) { return opt.getConfigOption().template getValue>(); }, - transformValidatorIntoExceptionValidator( + transformValidatorIntoExceptionValidator( validatorFunction, std::move(errorMessage)), std::move(validatorDescriptor), configOptionsToBeChecked...); } @@ -349,14 +363,16 @@ class ConfigManager { values of which will be passed to the exception validator function as function arguments. Will keep the same order. */ - template - void addValidator( - ExceptionValidatorFunction auto - exceptionValidatorFunction, - std::string exceptionValidatorDescriptor, - ConstConfigOptionProxy< - ExceptionValidatorParameterTypes>... configOptionsToBeChecked) - requires(sizeof...(configOptionsToBeChecked) > 0) { + CPP_template(typename ExceptionalValidatorFunc, + typename... ExceptionValidatorParameterTypes)( + requires(sizeof...(ExceptionValidatorParameterTypes) > 0) CPP_and ExceptionValidatorFunction< + ExceptionalValidatorFunc, + ExceptionValidatorParameterTypes...>) void addValidator(ExceptionalValidatorFunc + exceptionValidatorFunction, + std::string + exceptionValidatorDescriptor, + ConstConfigOptionProxy< + ExceptionValidatorParameterTypes>... configOptionsToBeChecked) { addValidatorImpl( "addValidator", [](ConstConfigOptionProxy opt) { @@ -380,21 +396,23 @@ class ConfigManager { will be passed to the validator function as function arguments. Will keep the same order. */ - template - void addOptionValidator( - ValidatorT validatorFunction, std::string errorMessage, - std::string validatorDescriptor, - isInstantiation auto... configOptionsToBeChecked) - requires( - sizeof...(configOptionsToBeChecked) > 0 && - ValidatorFunction) { + CPP_template(typename ValidatorFunc, typename... ConfigOptions)(requires( + sizeof...(ConfigOptions) > + 0)) void addOptionValidator(ValidatorFunc validatorFunction, + std::string errorMessage, + std::string validatorDescriptor, + ConfigOptions&&... configOptionsToBeChecked) + QL_CONCEPT_OR_NOTHING( + requires(ValidatorFunction< + ValidatorFunc, + decltype(configOptionsToBeChecked.getConfigOption())...>)) { addValidatorImpl( "addOptionValidator", [](ConstConfigOptionProxy opt) { return opt.getConfigOption(); }, transformValidatorIntoExceptionValidator< + ValidatorFunc, decltype(configOptionsToBeChecked.getConfigOption())...>( validatorFunction, std::move(errorMessage)), std::move(validatorDescriptor), configOptionsToBeChecked...); @@ -414,15 +432,17 @@ class ConfigManager { will be passed to the validator function as function arguments. Will keep the same order. */ - template - void addOptionValidator( - ExceptionValidatorT exceptionValidatorFunction, - std::string exceptionValidatorDescriptor, - isInstantiation auto... configOptionsToBeChecked) - requires(sizeof...(configOptionsToBeChecked) > 0 && - ExceptionValidatorFunction< + CPP_template(typename ExceptionValidatorT, + typename... ConfigOptions)(requires( + sizeof...(ConfigOptions) > + 0)) void addOptionValidator(ExceptionValidatorT + exceptionValidatorFunction, + std::string exceptionValidatorDescriptor, + ConfigOptions&&... configOptionsToBeChecked) + QL_CONCEPT_OR_NOTHING( + requires(ExceptionValidatorFunction< ExceptionValidatorT, - decltype(configOptionsToBeChecked.getConfigOption())...>) { + decltype(configOptionsToBeChecked.getConfigOption())...>)) { addValidatorImpl( "addOptionValidator", [](ConstConfigOptionProxy opt) { @@ -460,13 +480,15 @@ class ConfigManager { path, so that information will only be included in generated error messages, if you pass it along. */ - template - requires ad_utility::InvocableWithExactReturnType< - Visitor, void, std::string_view, ConfigManager&> && - ad_utility::InvocableWithExactReturnType< - Visitor, void, std::string_view, ConfigOption&> - void visitHashMapEntries(Visitor&& vis, bool sortByCreationOrder, - std::string_view pathPrefix) const; + CPP_template(typename Visitor)( + requires ad_utility::InvocableWithExactReturnType< + Visitor, void, std::string_view, ConfigManager&> + CPP_and ad_utility::InvocableWithExactReturnType< + Visitor, void, std::string_view, + ConfigOption&>) void visitHashMapEntries(Visitor&& vis, + bool sortByCreationOrder, + std::string_view + pathPrefix) const; /* @brief Collect all `HashMapEntry` contained in the `hashMap`, including the @@ -479,15 +501,20 @@ class ConfigManager { @param predicate Only the `HashMapEntry` for which a true is returned, will be given back. */ - template < - SimilarTo> HashMapType> - requires std::is_object_v static std::conditional_t< - std::is_const_v, - const std::vector>, - std::vector>> - allHashMapEntries(HashMapType& hashMap, std::string_view pathPrefix, - const ad_utility::InvocableWithSimilarReturnType< - bool, const HashMapEntry&> auto& predicate); + CPP_template(typename HashMapType, typename Callable)( + requires SimilarTo, + HashMapType> + CPP_and std::is_object_v) static std:: + conditional_t< + std::is_const_v, + const std::vector>, + std::vector>> allHashMapEntries(HashMapType& + hashMap, + std::string_view + pathPrefix, + const Callable& + predicate); /* @brief Creates the string representation of a valid `nlohmann::json` pointer @@ -540,14 +567,14 @@ class ConfigManager { @return A reference to the newly created configuration option. Will stay valid, even after more options. */ - template < - ad_utility::SameAsAnyTypeIn OptionType> - ConstConfigOptionProxy addOptionImpl( - const std::vector& pathToOption, - std::string_view optionDescription, - OptionType* variableToPutValueOfTheOptionIn, - std::optional defaultValue = - std::optional(std::nullopt)) { + CPP_template(typename OptionType)(requires ad_utility::SameAsAnyTypeIn< + OptionType, ConfigOption::AvailableTypes>) + ConstConfigOptionProxy addOptionImpl( + const std::vector& pathToOption, + std::string_view optionDescription, + OptionType* variableToPutValueOfTheOptionIn, + std::optional defaultValue = + std::optional(std::nullopt)) { verifyPath(pathToOption); /* @@ -580,12 +607,12 @@ class ConfigManager { @tparam ReturnReference Should be either `ConfigOption&`, or `const ConfigOption&`. */ - template ) ReturnReference> - static std::vector> - configurationOptionsImpl( - SimilarTo> auto& - configurationOptions); + CPP_template(typename ConfigOptions, typename ReturnReference)( + requires SameAsAny + CPP_and SimilarTo, + ConfigOptions>) static std:: + vector> configurationOptionsImpl( + ConfigOptions& configurationOptions); /* @brief Return all `ConfigOptionValidatorManager` held by this manager and its @@ -629,9 +656,8 @@ class ConfigManager { will be passed to the exception validator function as function arguments, after being transformed. Will keep the same order. */ - template < - typename TranslationFunction, typename ExceptionValidatorFunc, - isInstantiation... ExceptionValidatorParameter> + template void addValidatorImpl( std::string_view addValidatorFunctionName, TranslationFunction translationFunction, @@ -718,9 +744,11 @@ class ConfigManager { @brief Add a validator to the list of validators, that are assigned to a `ConfigOption`/`ConfigManager`. */ - template - void addEntryUnderKey(const T& key, - const ConfigOptionValidatorManager& manager); + CPP_template(typename T)( + requires ConfigOptionOrManager< + T>) void addEntryUnderKey(const T& key, + const ConfigOptionValidatorManager& + manager); /* @brief Retrieve the list of validators, that are assigned to a @@ -728,21 +756,22 @@ class ConfigManager { @returns If there is no entry for `Key`, return an empty `std::vector`. */ - template - ValueGetterReturnType getEntriesUnderKey(const T& key) const; + CPP_template(typename T)(requires ConfigOptionOrManager) + ValueGetterReturnType getEntriesUnderKey(const T& key) const; private: // Return either `configOption_` or `configManager_`, based on type. - template - constexpr const MemoryAdressHashMap& getHashMapBasedOnType() const { + CPP_template(typename T)(requires ConfigOptionOrManager) constexpr const + MemoryAdressHashMap& getHashMapBasedOnType() const { if constexpr (std::same_as) { return configOption_; } else if constexpr (std::same_as) { return configManager_; } } - template - constexpr MemoryAdressHashMap& getHashMapBasedOnType() { + CPP_template(typename T)( + requires ConfigOptionOrManager< + T>) constexpr MemoryAdressHashMap& getHashMapBasedOnType() { if constexpr (std::same_as) { return configOption_; } else if constexpr (std::same_as) { diff --git a/src/util/ConfigManager/ConfigOption.h b/src/util/ConfigManager/ConfigOption.h index f80fd5183f..4aabcde333 100644 --- a/src/util/ConfigManager/ConfigOption.h +++ b/src/util/ConfigManager/ConfigOption.h @@ -91,8 +91,9 @@ class ConfigOption { bool wasSet() const; // Returns, if this configuration option holds values of the given type. - template Type> - constexpr bool holdsType() const { + CPP_template(typename Type)( + requires ad_utility::SameAsAnyTypeIn< + Type, AvailableTypes>) constexpr bool holdsType() const { return std::holds_alternative>(data_); } @@ -101,8 +102,8 @@ class ConfigOption { exception, should the given value have a different type, than what the configuration option was set to. */ - template T> - void setValue(const T& value) { + CPP_template(typename T)(requires ad_utility::SameAsAnyTypeIn< + T, AvailableTypes>) void setValue(const T& value) { // Only set the variable, that our internal pointer points to, if the given // value is of the right type. if (auto* data = std::get_if>(&data_); data != nullptr) { @@ -127,8 +128,9 @@ class ConfigOption { there is no default value, or `T` is the wrong type, then it will throw an exception. */ - template T> - T getDefaultValue() const { + CPP_template(typename T)( + requires ad_utility::SameAsAnyTypeIn) T + getDefaultValue() const { if (hasDefaultValue() && std::holds_alternative>(data_)) { return std::get>(data_).defaultValue_.value(); } else if (!hasDefaultValue()) { @@ -156,8 +158,9 @@ class ConfigOption { @brief Return the content of the variable, that the internal pointer points to. If `T` is the wrong type, then it will throw an exception. */ - template T> - T getValue() const { + CPP_template(typename T)( + requires ad_utility::SameAsAnyTypeIn) T + getValue() const { if (wasSet() && std::holds_alternative>(data_)) { return *(std::get>(data_).variablePointer_); } else if (!wasSet()) { @@ -212,9 +215,11 @@ class ConfigOption { @param defaultValue The optional default value. An empty `std::optional` is for no default value and a non empty for a default value. */ - template T> - ConfigOption(std::string_view identifier, std::string_view description, - T* variablePointer, std::optional defaultValue = std::nullopt) + CPP_template(typename T)( + requires ad_utility::SameAsAnyTypeIn) + ConfigOption(std::string_view identifier, std::string_view description, + T* variablePointer, + std::optional defaultValue = std::nullopt) : data_{Data{std::move(defaultValue), variablePointer}}, identifier_{identifier}, description_{description} { @@ -251,8 +256,9 @@ class ConfigOption { /* @brief Return the string representation/name of the type. */ - template T> - static std::string availableTypesToString() { + CPP_template(typename T)(requires ad_utility::SameAsAnyTypeIn< + T, AvailableTypes>) static std::string + availableTypesToString() { return availableTypesToString(T{}); } @@ -266,7 +272,7 @@ class ConfigOption { // Shorthand for checking, if `T` is part of the available options. template -concept SupportedConfigOptionType = +CPP_concept SupportedConfigOptionType = ad_utility::SameAsAnyTypeIn; } // namespace ConfigManagerImpl using ConfigManagerImpl::ConfigOption; diff --git a/src/util/ConfigManager/ConfigOptionProxy.h b/src/util/ConfigManager/ConfigOptionProxy.h index 3050b4fcea..beb5650642 100644 --- a/src/util/ConfigManager/ConfigOptionProxy.h +++ b/src/util/ConfigManager/ConfigOptionProxy.h @@ -43,26 +43,26 @@ CPP_template(typename T, typename ConfigOptionType)( // Get access to the configuration option, this is a proxy for. Const access // only for the const version, to make the usage clearer. - ConfigOptionType& getConfigOption() const - requires std::is_const_v { + const ConfigOptionType& getConfigOption() const + QL_CONCEPT_OR_NOTHING(requires(std::is_const_v)) { AD_CORRECTNESS_CHECK(option_ != nullptr); return *option_; } ConfigOptionType& getConfigOption() - requires(!std::is_const_v) { + QL_CONCEPT_OR_NOTHING(requires(!std::is_const_v)) { AD_CORRECTNESS_CHECK(option_ != nullptr); return *option_; } // (Implicit) conversion to `ConfigOptionType&`. explicit(false) operator ConfigOptionType&() const - requires(std::is_const_v) { + QL_CONCEPT_OR_NOTHING(requires(std::is_const_v)) { return getConfigOption(); } explicit(false) operator ConfigOptionType&() - requires(!std::is_const_v) { + QL_CONCEPT_OR_NOTHING(requires(!std::is_const_v)) { return getConfigOption(); } }; diff --git a/src/util/ConfigManager/Validator.h b/src/util/ConfigManager/Validator.h index 8400332b31..9dae93c9fb 100644 --- a/src/util/ConfigManager/Validator.h +++ b/src/util/ConfigManager/Validator.h @@ -26,7 +26,7 @@ types, in the same order and transformed to `const type&`, and returns a bool. */ template -concept ValidatorFunction = +CPP_concept ValidatorFunction = RegularInvocableWithSimilarReturnType; // Simple struct, that holds an error message. For use as the return type of @@ -49,7 +49,7 @@ parameter types, in the same order and transformed to `const type&`, and returns an instance of `std::optional`. */ template -concept ExceptionValidatorFunction = +CPP_concept ExceptionValidatorFunction = RegularInvocableWithSimilarReturnType, const ParameterTypes&...>; @@ -60,10 +60,13 @@ That is, instead of returning `bool`, it now returns @param errorMessage Content for `ErrorMessage`. */ -template -inline auto transformValidatorIntoExceptionValidator( - ValidatorFunction auto validatorFunction, - std::string errorMessage) { +CPP_template(typename ValidatorFunc, typename... ValidatorParameterTypes)( + requires ValidatorFunction< + ValidatorFunc, + ValidatorParameterTypes...>) inline auto transformValidatorIntoExceptionValidator(ValidatorFunc + validatorFunction, + std::string + errorMessage) { // The whole 'transformation' is simply a wrapper. return [validatorFunction = std::move(validatorFunction), errorMessage = ErrorMessage{std::move(errorMessage)}]( @@ -127,22 +130,18 @@ class ConfigOptionValidatorManager { will be passed to the exception validator function as function arguments, after being transformed. Will keep the same order. */ - template ... ExceptionValidatorParameterTypes> - requires(std::invocable && - ...) && - ExceptionValidatorFunction< - ExceptionValidatorFunc, - std::invoke_result_t< - TranslationFunction, - const ExceptionValidatorParameterTypes>...> && - (sizeof...(ExceptionValidatorParameterTypes) > 0) - ConfigOptionValidatorManager( - ExceptionValidatorFunc exceptionValidatorFunction, std::string descriptor, - TranslationFunction translationFunction, - const ExceptionValidatorParameterTypes... configOptionsToBeChecked) + CPP_template(typename TranslationFunction, typename ExceptionValidatorFunc, + typename... ExceptionValidatorParameterTypes)( + requires(... && std::invocable) + CPP_and ExceptionValidatorFunction< + ExceptionValidatorFunc, + std::invoke_result_t...>) + ConfigOptionValidatorManager( + ExceptionValidatorFunc exceptionValidatorFunction, + std::string descriptor, TranslationFunction translationFunction, + const ExceptionValidatorParameterTypes... configOptionsToBeChecked) : descriptor_{std::move(descriptor)}, configOptionsToBeChecked_{ &configOptionsToBeChecked.getConfigOption()...} { @@ -194,20 +193,21 @@ class ConfigOptionValidatorManager { will be passed to the exception validator function as function arguments, after being transformed. Will keep the same order. */ - template ... ValidatorParameterTypes> - requires(std::invocable && - ...) && - ValidatorFunction< + CPP_template(typename TranslationFunction, typename ValidatorFunc, + typename... ValidatorParameterTypes)( + requires(... && std::invocable) + CPP_and ValidatorFunction< ValidatorFunc, std::invoke_result_t...> && - (sizeof...(ValidatorParameterTypes) > 0) ConfigOptionValidatorManager( - ValidatorFunc validatorFunction, std::string errorMessage, - std::string descriptor, TranslationFunction translationFunction, - const ValidatorParameterTypes... configOptionsToBeChecked) + const ValidatorParameterTypes>...> + CPP_and(sizeof...(ValidatorParameterTypes) > 0)) + ConfigOptionValidatorManager( + ValidatorFunc validatorFunction, std::string errorMessage, + std::string descriptor, TranslationFunction translationFunction, + const ValidatorParameterTypes... configOptionsToBeChecked) : ConfigOptionValidatorManager( - transformValidatorIntoExceptionValidator...>( std::move(validatorFunction), std::move(errorMessage)), std::move(descriptor), std::move(translationFunction), diff --git a/src/util/ConstexprSmallString.h b/src/util/ConstexprSmallString.h index ebbe26d5d9..78ca38bd4d 100644 --- a/src/util/ConstexprSmallString.h +++ b/src/util/ConstexprSmallString.h @@ -37,7 +37,7 @@ struct ConstexprSmallString { } /// Construct at runtime from a string_view - ConstexprSmallString(std::string_view input) : _size(input.size()) { + constexpr ConstexprSmallString(std::string_view input) : _size(input.size()) { if (input.size() >= MaxSize) { throw std::runtime_error{ "ConstexprSmallString can only be constructed from strings with a " diff --git a/src/util/Exception.h b/src/util/Exception.h index 0f399084f8..2f2ee60f59 100644 --- a/src/util/Exception.h +++ b/src/util/Exception.h @@ -92,12 +92,13 @@ namespace ad_utility::detail { // builtin numeric types) [first overload] // * A callbable that takes no arguments and returns a string [second overload] template -requires requires(S&& s) { absl::StrCat(AD_FWD(s)); } +QL_CONCEPT_OR_NOTHING(requires requires(S&& s) { absl::StrCat(AD_FWD(s)); }) std::string getMessageImpl(S&& s) { return absl::StrCat(AD_FWD(s)); } -std::string getMessageImpl( - ad_utility::InvocableWithConvertibleReturnType auto&& f) { +CPP_template(typename T)( + requires ad_utility::InvocableWithConvertibleReturnType) + std::string getMessageImpl(T&& f) { return std::invoke(f); } diff --git a/src/util/Generator.h b/src/util/Generator.h index 604ba7e7c3..8eae8cae2c 100644 --- a/src/util/Generator.h +++ b/src/util/Generator.h @@ -2,8 +2,8 @@ // Copyright (c) Lewis Baker, Johannes Kalmbach (functionality to add details). // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// -#ifndef CPPCORO_GENERATOR_HPP_INCLUDED -#define CPPCORO_GENERATOR_HPP_INCLUDED +#ifndef SRC_UTIL_GENERATOR_H +#define SRC_UTIL_GENERATOR_H #include #include @@ -69,8 +69,8 @@ class generator_promise { } // Don't allow any use of 'co_await' inside the generator coroutine. - template - std::suspend_never await_transform(U&& value) = delete; + CPP_template(typename U)(requires(!ad_utility::SimilarTo)) + std::suspend_never await_transform(U&& value) = delete; void rethrow_if_exception() const { if (m_exception) { @@ -87,8 +87,9 @@ class generator_promise { Details& await_resume() noexcept { return promise_.details(); } }; - DetailAwaiter await_transform( - [[maybe_unused]] ad_utility::SimilarTo auto&& detail) { + CPP_template(typename DetailT)( + requires ad_utility::SimilarTo) DetailAwaiter + await_transform([[maybe_unused]] DetailT&& detail) { return {*this}; } @@ -296,4 +297,4 @@ T getSingleElement(generator g) { } } // namespace cppcoro -#endif +#endif // SRC_UTIL_GENERATOR_H diff --git a/src/util/ProgramOptionsHelpers.h b/src/util/ProgramOptionsHelpers.h index bd804504d3..a4cb24b035 100644 --- a/src/util/ProgramOptionsHelpers.h +++ b/src/util/ProgramOptionsHelpers.h @@ -26,8 +26,9 @@ class NonNegative { size_t _value; }; -template NN> -Stream& operator<<(Stream& stream, NN&& nonNegative) { +CPP_template(typename Stream, typename NN)( + requires ad_utility::SimilarTo) Stream& +operator<<(Stream& stream, NN&& nonNegative) { return stream << static_cast(nonNegative); } diff --git a/src/util/Serializer/SerializeArrayOrTuple.h b/src/util/Serializer/SerializeArrayOrTuple.h index da001270be..bcbee73fbf 100644 --- a/src/util/Serializer/SerializeArrayOrTuple.h +++ b/src/util/Serializer/SerializeArrayOrTuple.h @@ -15,9 +15,10 @@ namespace ad_utility::serialization { // Allow trivial serialization for `std::array` if the `value_type` is already // `TriviallySerializable`. -template -requires TriviallySerializable -std::true_type allowTrivialSerialization(T, auto); +CPP_template(typename T, typename U)( + requires ad_utility::SimilarToArray CPP_and + TriviallySerializable) std::true_type + allowTrivialSerialization(T, U); // A helper function to figure out whether all types contained in a tuple are // trivially seraizliable. @@ -31,11 +32,14 @@ consteval bool tupleTriviallySerializableImpl() { } } // namespace detail +template +CPP_concept either_array_or_instantiation = + ad_utility::isArray> || + ad_utility::similarToInstantiation; + // Serialization function for `std::array` and `std::tuple`. AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT( - ((ad_utility::isArray> || - ad_utility::similarToInstantiation< - T, std::tuple>)&&!TriviallySerializable)) { + (either_array_or_instantiation CPP_and !TriviallySerializable)) { using Arr = std::decay_t; // Tuples are not technically not trivially copyable, but for the purpose of // serialization we still can safely `memcpy`. Note that for `std::array` the diff --git a/src/util/Serializer/SerializeOptional.h b/src/util/Serializer/SerializeOptional.h index 2218cf00e2..d8fd026205 100644 --- a/src/util/Serializer/SerializeOptional.h +++ b/src/util/Serializer/SerializeOptional.h @@ -11,7 +11,7 @@ // Serialization for `std::optional` namespace ad_utility::serialization { AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT( - (ad_utility::similarToInstantiation && + (ad_utility::similarToInstantiation CPP_and !std::is_trivially_copyable_v>)) { if constexpr (ReadSerializer) { bool hasValue; diff --git a/src/util/Serializer/SerializeVector.h b/src/util/Serializer/SerializeVector.h index 982e43e2ff..9ae00a9583 100644 --- a/src/util/Serializer/SerializeVector.h +++ b/src/util/Serializer/SerializeVector.h @@ -36,7 +36,7 @@ AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT( /// Incrementally serialize a std::vector to disk without materializing it. /// Call `push` for each of the elements that will become part of the vector. -template +CPP_template(typename T, typename Serializer)(requires WriteSerializer) class VectorIncrementalSerializer { private: Serializer _serializer; diff --git a/src/util/Serializer/Serializer.h b/src/util/Serializer/Serializer.h index 574604ed90..e5e758e357 100644 --- a/src/util/Serializer/Serializer.h +++ b/src/util/Serializer/Serializer.h @@ -4,8 +4,8 @@ // Library for symmetric serialization. -#ifndef QLEVER_SERIALIZER_SERIALIZER -#define QLEVER_SERIALIZER_SERIALIZER +#ifndef SRC_UTIL_SERIALIZER_SERIALIZER_H +#define SRC_UTIL_SERIALIZER_SERIALIZER_H #include "util/Forward.h" #include "util/TypeTraits.h" @@ -57,6 +57,27 @@ class SerializationException : public std::runtime_error { class WriteSerializerTag {}; class ReadSerializerTag {}; +#ifdef QLEVER_CPP_17 +template +using HasWriteSerializerType = typename std::decay_t::SerializerType; + +template +using HasWriteSerializeBytes = decltype(std::declval().serializeBytes( + std::declval(), std::declval())); + +template +struct IsWriteSerializer : std::false_type {}; + +template +struct IsWriteSerializer< + S, std::enable_if_t< + std::is_same_v, WriteSerializerTag> && + std::is_same_v, void>>> : std::true_type { +}; + +template +CPP_concept WriteSerializer = IsWriteSerializer::value; +#else /// A `WriteSerializer` can write from a span of bytes to some resource and has /// a public member type `using SerializerType = WriteSerializerTag`. template @@ -66,7 +87,28 @@ concept WriteSerializer = requires(S s, const char* ptr, size_t numBytes) { } -> std::same_as; { s.serializeBytes(ptr, numBytes) }; }; +#endif + +#ifdef QLEVER_CPP_17 +template +using HasReadSerializerType = typename std::decay_t::SerializerType; + +template +using HasReadSerializeBytes = decltype(std::declval().serializeBytes( + std::declval(), std::declval())); +template +struct IsReadSerializer : std::false_type {}; + +template +struct IsReadSerializer< + S, std::enable_if_t< + std::is_same_v, ReadSerializerTag> && + std::is_same_v, void>>> : std::true_type {}; + +template +CPP_concept ReadSerializer = IsReadSerializer::value; +#else /// A `ReadSerializer` can read to span of bytes from some resource and has a /// public alias `using SerializerType = ReadSerializerTag'`. template @@ -76,16 +118,17 @@ concept ReadSerializer = requires(S s, char* ptr, size_t numBytes) { } -> std::same_as; { s.serializeBytes(ptr, numBytes) }; }; +#endif /// A `Serializer` is either a `WriteSerializer` or a `ReadSerializer` template -concept Serializer = WriteSerializer || ReadSerializer; +CPP_concept Serializer = WriteSerializer || ReadSerializer; /// If we try to serialize from a const object or reference, the serializer must /// be a `WriteSerializer`. The following type trait can be used to check this /// constraint at compile time. -template -static constexpr bool SerializerMatchesConstness = +CPP_template(typename S, typename T)( + requires Serializer) static constexpr bool SerializerMatchesConstness = WriteSerializer || !std::is_const_v>; /** @@ -112,21 +155,25 @@ static constexpr bool SerializerMatchesConstness = * - The second argument to `serialize` is a `T`, `T&`, `const T&`, `T&&` etc. * - If the `arg` is const then the serializer is a `WriteSerializer`. */ -#define AD_SERIALIZE_FUNCTION(T) \ - template U> \ - requires ad_utility::serialization::SerializerMatchesConstness \ - void serialize(S& serializer, U&& arg) +#define AD_SERIALIZE_FUNCTION(T) \ + CPP_template(typename S, typename U)( \ + requires ad_utility::serialization::Serializer CPP_and \ + ad_utility::SimilarTo \ + CPP_and ad_utility::serialization::SerializerMatchesConstness< \ + S, U>) void \ + serialize(S& serializer, U&& arg) /// Similar to `AD_SERIALIZE_FUNCTION` but defines a `friend` function to also /// access private members of a class. It is possible to declare the friend /// using `AD_SERIALIZE_FRIEND_FUNCTION(Type);` and to define it outside of the /// class with `AD_SERIALIZE_FUNCTION(Type){...}` -#define AD_SERIALIZE_FRIEND_FUNCTION(T) \ - template U> \ - requires ad_utility::serialization::SerializerMatchesConstness \ - friend void serialize(S& serializer, U&& arg) +#define AD_SERIALIZE_FRIEND_FUNCTION(T) \ + CPP_template(typename S, typename U)( \ + requires ad_utility::serialization::Serializer CPP_and \ + ad_utility::SimilarTo \ + CPP_and ad_utility::serialization::SerializerMatchesConstness< \ + S, U>) friend void \ + serialize(S& serializer, U&& arg) /** * Define `serialize` functions for all types that fulfill the `Constraint`. @@ -139,21 +186,27 @@ static constexpr bool SerializerMatchesConstness = * `Serializer` , `WriteSerializer` and `ReadSerializer` are also true for * references to serializers. For an example usage see `SerializePair.h`. */ -#define AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT(Constraint) \ - template \ - requires(Constraint) void serialize(S& serializer, T&& arg) +#define AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT(Constraint) \ + CPP_template(typename S, typename T)( \ + requires ad_utility::serialization::Serializer CPP_and \ + Constraint) void \ + serialize(S& serializer, T&& arg) /// Similar to `AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT` but only for /// `WriteSerializer`s. For an example usage see `SerializeVector.h` -#define AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT_WRITE(Constraint) \ - template \ - requires(Constraint) void serialize(S& serializer, T&& arg) +#define AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT_WRITE(Constraint) \ + CPP_template(typename S, typename T)( \ + requires ad_utility::serialization::WriteSerializer CPP_and \ + Constraint) void \ + serialize(S& serializer, T&& arg) /// Similar to `AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT` but only for /// `ReadSerializer`s. For an example usage see `SerializeVector.h` -#define AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT_READ(Constraint) \ - template \ - requires(Constraint) void serialize(S& serializer, T&& arg) +#define AD_SERIALIZE_FUNCTION_WITH_CONSTRAINT_READ(Constraint) \ + CPP_template(typename S, typename T)( \ + requires ad_utility::serialization::ReadSerializer CPP_and \ + Constraint) void \ + serialize(S& serializer, T&& arg) /** * Operator that allows the short hand notation @@ -161,17 +214,22 @@ static constexpr bool SerializerMatchesConstness = * instead of * `serialize(serializer, t)` */ -void operator|(Serializer auto& serializer, auto&& t) { +CPP_template(typename S, typename T)(requires Serializer) void operator|( + S& serializer, T&& t) { serialize(serializer, AD_FWD(t)); } /// Serialization operator for explicitly writing to a serializer. -void operator<<(WriteSerializer auto& serializer, const auto& t) { +CPP_template(typename S, typename T)(requires WriteSerializer) void +operator<<(S& serializer, const T& t) { serializer | t; } /// Serialization operator for explicitly reading from a serializer. -void operator>>(ReadSerializer auto& serializer, auto&& t) { serializer | t; } +CPP_template(typename S, typename T)(requires ReadSerializer) void +operator>>(S& serializer, T&& t) { + serializer | t; +} /// An empty struct that isn't needed by the users of this serialization /// framework. It is used internally to make argument-dependent lookup work. @@ -209,6 +267,31 @@ struct TrivialSerializationHelperTag {}; * }; * void allowTrivialSerialization(std::same_as auto, auto); */ +#ifdef QLEVER_CPP_17 +template +struct HasAllowTrivialSerialization : std::false_type {}; + +template +struct HasAllowTrivialSerialization< + T, Tag, + std::enable_if_t< + !std::is_same_v(), std::declval())), + void>>> : std::true_type {}; + +template +struct IsTriviallySerializable : std::false_type {}; + +template +struct IsTriviallySerializable< + T, std::enable_if_t::value && + std::is_trivially_copyable_v>>> + : std::true_type {}; + +template +CPP_concept TriviallySerializable = IsTriviallySerializable::value; +#else template concept TriviallySerializable = requires(T t, TrivialSerializationHelperTag tag) { @@ -217,11 +300,13 @@ concept TriviallySerializable = // the `ad::serialization` namespace. allowTrivialSerialization(t, tag); } && std::is_trivially_copyable_v>; +#endif /// Serialize function for `TriviallySerializable` types that works by simply /// serializing the binary object representation. -template -void serialize(S& serializer, T&& t) { +CPP_template(typename S, typename T)( + requires Serializer CPP_and + TriviallySerializable) void serialize(S& serializer, T&& t) { if constexpr (WriteSerializer) { serializer.serializeBytes(reinterpret_cast(&t), sizeof(t)); } else { @@ -232,12 +317,12 @@ void serialize(S& serializer, T&& t) { /// Arithmetic types (the builtins like int, char, double) can be trivially /// serialized. -template -requires std::is_arithmetic_v> -[[maybe_unused]] std::true_type allowTrivialSerialization(T, auto) { +CPP_template(typename T, + typename U)(requires std::is_arithmetic_v>) + [[maybe_unused]] std::true_type allowTrivialSerialization(T, U) { return {}; } } // namespace ad_utility::serialization -#endif // QLEVER_SERIALIZER_SERIALIZER +#endif // SRC_UTIL_SERIALIZER_SERIALIZER_H diff --git a/src/util/Timer.h b/src/util/Timer.h index 3c6974bf78..71c6315291 100644 --- a/src/util/Timer.h +++ b/src/util/Timer.h @@ -52,8 +52,9 @@ class Timer { // Convert any `std::chrono::duration` to the underlying `Duration` type // of the `Timer` class. - template T> - static Duration toDuration(T duration) { + CPP_template(typename T)( + requires ad_utility::isInstantiation) static Duration + toDuration(T duration) { return chr::duration_cast(duration); } diff --git a/src/util/TypeTraits.h b/src/util/TypeTraits.h index 8390667a85..455bc76944 100644 --- a/src/util/TypeTraits.h +++ b/src/util/TypeTraits.h @@ -85,7 +85,7 @@ struct FirstWrapper : public std::type_identity {}; /// isInstantiation, std::vector> == true; /// isInstantiation&, std::vector> == false; template typename TemplatedType> -concept isInstantiation = +CPP_concept isInstantiation = detail::IsInstantiationOf::template Instantiation::value; /// The concept is fulfilled iff `T` is `ad_utility::SimilarTo` an @@ -94,9 +94,18 @@ concept isInstantiation = /// similarToInstantiation> == true; /// similarToInstantiation&> == true; template typename TemplatedType> -concept similarToInstantiation = +CPP_concept similarToInstantiation = isInstantiation, TemplatedType>; +/// @brief The concept is fulfilled if `T` is an instantiation of any +/// of the types passed in ...Ts +/// +/// similarToAnyInstantiationOf, +/// std::vector> == true +template typename... Ts> +CPP_concept similarToAnyInstantiationOf = + (... || similarToInstantiation); + /// isVector is true if and only if T is an instantiation of std::vector template constexpr static bool isVector = isInstantiation; @@ -118,7 +127,7 @@ constexpr static bool isArray> = true; // `SimilarToArray` is also true for `std::array<...>&`, etc. template -concept SimilarToArray = isArray>; +CPP_concept SimilarToArray = isArray>; /// Two types are similar, if they are the same when we remove all cv (const or /// volatile) qualifiers and all references @@ -128,11 +137,11 @@ constexpr static bool isSimilar = /// A concept for similarity template -concept SimilarTo = isSimilar; +CPP_concept SimilarTo = isSimilar; /// True iff `T` is similar (see above) to any of the `Ts...`. template -concept SimilarToAny = (... || isSimilar); +CPP_concept SimilarToAny = (... || isSimilar); /// True iff `T` is the same as any of the `Ts...`. template @@ -165,14 +174,15 @@ or `std::variant`). and `T` is `isSimilar` (see above) to any of the type parameters. */ template -concept SimilarToAnyTypeIn = detail::SimilarToAnyTypeInImpl::value; +CPP_concept SimilarToAnyTypeIn = + detail::SimilarToAnyTypeInImpl::value; /* Equivalent to `SimilarToAnyTypeIn` (see above), but checks for exactly matching types via `std::same_as`. */ template -concept SameAsAnyTypeIn = detail::SameAsAnyTypeInImpl::value; +CPP_concept SameAsAnyTypeIn = detail::SameAsAnyTypeInImpl::value; /// A templated bool that is always false, /// independent of the template parameter. @@ -253,7 +263,7 @@ using First = typename detail::FirstWrapper::type; /// Concept for `std::is_invocable_r_v`. template -concept InvocableWithConvertibleReturnType = +CPP_concept InvocableWithConvertibleReturnType = std::is_invocable_r_v; /* @@ -270,7 +280,7 @@ are the same when ignoring `const`, `volatile`, and reference qualifiers. `isSimilar` to `Ret`. */ template -concept InvocableWithSimilarReturnType = +CPP_concept InvocableWithSimilarReturnType = std::invocable && isSimilar, Ret>; @@ -279,7 +289,7 @@ concept InvocableWithSimilarReturnType = `Ret`. */ template -concept InvocableWithExactReturnType = +CPP_concept InvocableWithExactReturnType = std::invocable && std::same_as, Ret>; @@ -293,7 +303,7 @@ invocable type is regular invocable, or not. For more information see: https://en.cppreference.com/w/cpp/concepts/invocable */ template -concept RegularInvocableWithSimilarReturnType = +CPP_concept RegularInvocableWithSimilarReturnType = std::regular_invocable && isSimilar, Ret>; @@ -307,7 +317,7 @@ invocable type is regular invocable, or not. For more information see: https://en.cppreference.com/w/cpp/concepts/invocable */ template -concept RegularInvocableWithExactReturnType = +CPP_concept RegularInvocableWithExactReturnType = std::regular_invocable && std::same_as, Ret>; @@ -316,5 +326,5 @@ concept RegularInvocableWithExactReturnType = // might be lvalue, because the && denotes a forwarding reference. void f(Rvalue // auto&& x) // guaranteed rvalue reference, can safely be moved. template -concept Rvalue = std::is_rvalue_reference_v; +CPP_concept Rvalue = std::is_rvalue_reference_v; } // namespace ad_utility diff --git a/src/util/Views.h b/src/util/Views.h index ddcf867d3f..ef1aca7401 100644 --- a/src/util/Views.h +++ b/src/util/Views.h @@ -275,11 +275,14 @@ auto integerRange(Int upperBound) { // The implementation of `inPlaceTransformView`, see below for details. namespace detail { -template > - Transformation> -requires(ql::ranges::view && ql::ranges::input_range) -auto inPlaceTransformViewImpl(Range range, Transformation transformation) { +CPP_template(typename Range, typename Transformation)( + requires ad_utility::InvocableWithExactReturnType< + Transformation, void, ql::ranges::range_reference_t> + CPP_and ql::ranges::view + CPP_and ql::ranges::input_range< + Range>) auto inPlaceTransformViewImpl(Range range, + Transformation + transformation) { // Take a range and yield pairs of [pointerToElementOfRange, // boolThatIsInitiallyFalse]. The bool is yielded as a reference and if its // value is changed, that change will be stored until the next element is diff --git a/test/ConfigManagerTest.cpp b/test/ConfigManagerTest.cpp index 315e02d7a3..0f9c9afbf9 100644 --- a/test/ConfigManagerTest.cpp +++ b/test/ConfigManagerTest.cpp @@ -1727,10 +1727,12 @@ TEST(ConfigManagerTest, AddExceptionValidator) { std::string validatorExceptionMessage, ConfigManager& m, ConstConfigOptionProxy... optProxy) { + auto exceptionalValidator = + generateDummyNonExceptionValidatorFunction(variant); m.addValidator( - transformValidatorIntoExceptionValidator( - generateDummyNonExceptionValidatorFunction(variant), - std::move(validatorExceptionMessage)), + transformValidatorIntoExceptionValidator( + exceptionalValidator, std::move(validatorExceptionMessage)), "", optProxy...); }); } @@ -1999,16 +2001,17 @@ TEST(ConfigManagerTest, AddOptionNoExceptionValidator) { } TEST(ConfigManagerTest, AddOptionExceptionValidator) { - doAddOptionValidatorTest( - [](auto validatorFunction, - std::string validatorExceptionMessage, - ConfigManager& m, ConstConfigOptionProxy... args) { - m.addOptionValidator( - transformValidatorIntoExceptionValidator< - decltype(args.getConfigOption())...>( - validatorFunction, std::move(validatorExceptionMessage)), - "", args...); - }); + doAddOptionValidatorTest([]( + auto validatorFunction, + std::string validatorExceptionMessage, + ConfigManager& m, + ConstConfigOptionProxy... args) { + m.addOptionValidator( + transformValidatorIntoExceptionValidator< + decltype(validatorFunction), decltype(args.getConfigOption())...>( + validatorFunction, std::move(validatorExceptionMessage)), + "", args...); + }); } /* diff --git a/test/ValidatorTest.cpp b/test/ValidatorTest.cpp index c6d74a3217..d31e00e21b 100644 --- a/test/ValidatorTest.cpp +++ b/test/ValidatorTest.cpp @@ -426,8 +426,9 @@ TEST(ConfigOptionValidatorManagerTest, ValidatorConstructor) { TEST(ValidatorConceptTest, TransformValidatorIntoExceptionValidator) { // Helper function, that check, if a given validator behaves as // wanted, before and after being transformed into an exception validator. - auto checkValidator = []( - ValidatorFunction auto func, + auto checkValidator = []( + // ValidatorFunction auto func, + ValidatorFunc func, const std::tuple& validValues, const std::tuple& nonValidValues, ad_utility::source_location l = @@ -440,7 +441,7 @@ TEST(ValidatorConceptTest, TransformValidatorIntoExceptionValidator) { // Transform and check. auto transformedFunc = - transformValidatorIntoExceptionValidator(func, + transformValidatorIntoExceptionValidator(func, "test"); static_assert(ExceptionValidatorFunction);