From 97c195aaf9e249f43a6417f99ea93f7e94831f39 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Wed, 18 Dec 2024 01:29:35 +0100 Subject: [PATCH] Add macros `QL_CONCEPT_OR_NOTHING` and `QL_CONCEPT_OR_TEMPLATE` (#1686) The two macros can be used for concepts that improve the semantics and safety of the code, but are not necessary for compilation. They can be dropped or replaced by something simpler when compiling with C++17. The macro `QL_CONCEPT_OR_NOTHING` can be used for concepts that can be omitted when compiling with C++17. For example, `QL_CONCEPT_OR_NOTHING(std::view) auto x = someFunction()`. The macro `QL_CONCEPT_OR_TEMPLATE` can be used for concepts that can be replaced by `typename` when compiling with C++17. For example, `template ) T> void f(){...}`. --- benchmark/JoinAlgorithmBenchmark.cpp | 3 +- benchmark/infrastructure/Benchmark.h | 5 +- .../BenchmarkMeasurementContainer.cpp | 2 +- .../BenchmarkMeasurementContainer.h | 2 +- src/backports/algorithm.h | 13 +---- src/backports/concepts.h | 58 +++++++++++++++---- .../sparqlExpressions/LiteralExpression.h | 9 ++- src/util/ConfigManager/ConfigManager.cpp | 8 ++- src/util/ConfigManager/ConfigManager.h | 14 +++-- src/util/ConfigManager/ConfigOptionProxy.h | 8 +-- src/util/FsstCompressor.h | 17 ++++-- src/util/Iterators.h | 6 +- src/util/JoinAlgorithms/JoinAlgorithms.h | 5 +- src/util/TypeTraits.h | 4 +- src/util/Views.h | 3 +- test/ConfigOptionProxyTest.cpp | 6 +- 16 files changed, 101 insertions(+), 62 deletions(-) diff --git a/benchmark/JoinAlgorithmBenchmark.cpp b/benchmark/JoinAlgorithmBenchmark.cpp index 18945aa41c..b06202dbf4 100644 --- a/benchmark/JoinAlgorithmBenchmark.cpp +++ b/benchmark/JoinAlgorithmBenchmark.cpp @@ -1328,7 +1328,8 @@ class GeneralInterfaceImplementation : public BenchmarkInterface { */ bool addNewRowToBenchmarkTable( ResultTable* table, - const ad_utility::SameAsAny auto changingParameterValue, + const QL_CONCEPT_OR_NOTHING( + ad_utility::SameAsAny) auto changingParameterValue, ad_utility::InvocableWithExactReturnType auto stopFunction, diff --git a/benchmark/infrastructure/Benchmark.h b/benchmark/infrastructure/Benchmark.h index ae5b93d410..569fb226da 100644 --- a/benchmark/infrastructure/Benchmark.h +++ b/benchmark/infrastructure/Benchmark.h @@ -14,6 +14,7 @@ #include "../benchmark/infrastructure/BenchmarkMeasurementContainer.h" #include "../benchmark/infrastructure/BenchmarkMetadata.h" +#include "backports/concepts.h" #include "util/ConfigManager/ConfigManager.h" #include "util/CopyableUniquePtr.h" #include "util/Exception.h" @@ -67,8 +68,8 @@ class BenchmarkResults { @param constructorArgs Arguments to pass to the constructor of the object, that the new `CopyableUniquePtr` will own. */ - template < - ad_utility::SameAsAny EntryType> + template ) EntryType> static EntryType& addEntryToContainerVector( PointerVector& targetVector, auto&&... constructorArgs) { targetVector.push_back(ad_utility::make_copyable_unique( diff --git a/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp b/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp index c7736e2e59..04bd28f41a 100644 --- a/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp +++ b/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp @@ -371,7 +371,7 @@ std::ostream& operator<<(std::ostream& os, const ResultGroup& resultGroup) { } // ____________________________________________________________________________ -template T> +template void ResultGroup::deleteEntryImpl(T& entry) { // The vector, that holds our entries. auto& vec = [this]() -> auto& { diff --git a/benchmark/infrastructure/BenchmarkMeasurementContainer.h b/benchmark/infrastructure/BenchmarkMeasurementContainer.h index 6703bb3224..7700e1fd18 100644 --- a/benchmark/infrastructure/BenchmarkMeasurementContainer.h +++ b/benchmark/infrastructure/BenchmarkMeasurementContainer.h @@ -375,7 +375,7 @@ class ResultGroup : public BenchmarkMetadataGetter { private: // The implementation for the general deletion of entries. - template T> + template void deleteEntryImpl(T& entry); }; diff --git a/src/backports/algorithm.h b/src/backports/algorithm.h index 90b4e2884c..86b8ecec8d 100644 --- a/src/backports/algorithm.h +++ b/src/backports/algorithm.h @@ -9,6 +9,8 @@ #include #include +#include "backports/concepts.h" + // The following defines namespaces `ql::ranges` and `ql::views` that are almost // drop-in replacements for `std::ranges` and `std::views`. In C++20 mode (when // the `QLEVER_CPP_17` macro is not used), these namespaces are simply aliases @@ -19,7 +21,6 @@ // currently not aware of, because they only affect functions that we currently // don't use. For those, the following header can be expanded in the future. #ifndef QLEVER_CPP_17 -#include #include #endif @@ -46,14 +47,4 @@ using namespace std::views; #endif } // namespace views -// The namespace `ql::concepts` includes concepts that are contained in the -// C++20 standard as well as in `range-v3`. -namespace concepts { -#ifdef QLEVER_CPP_17 -using namespace ::concepts; -#else -using namespace std; -#endif -} // namespace concepts - } // namespace ql diff --git a/src/backports/concepts.h b/src/backports/concepts.h index ad0159da32..3049c55c5c 100644 --- a/src/backports/concepts.h +++ b/src/backports/concepts.h @@ -1,17 +1,55 @@ -// Copyright 2024, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Johannes Kalmbach +// Copyright 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Author: Johannes Kalmbach #pragma once +#include +#ifndef QLEVER_CPP_17 +#include +#endif + // Define the following macros: -// `QL_OPT_CONCEPT(arg)` which expands to `arg` in C++20 mode, and to nothing in -// C++17 mode. It can be used to easily opt out of concepts that are only used -// for documentation and increased safety and not for overload resolution. -// Example usage: -// `(QL_OPT_CONCEPT(std::view) auto x = someFunction();` +// +// `QL_CONCEPT_OR_NOTHING(arg)`: expands to `arg` in C++20 mode, and to +// nothing in C++17 mode. It can be used to easily opt out of concepts that are +// only used for documentation and increased safety and not for overload +// resolution. +// +// `QL_CONCEPT_OR_TYPENAME(arg)`: expands to `arg` in C++20 mode, and to +// `typename` in C++17 mode. Example usage: +// +// Example usages: +// +// `QL_CONCEPT_OR_NOTHING(std::view) auto x = someFunction();` +// +// `QL_CONCEPT_OR_NOTHING(SameAsAny)` +// +// `void f(QL_CONCEPT_OR_NOTHING(std::view) auto x) {...}` +// +// `template ) T> void f(){...}` +// +// NOTE: The macros are variadic to allow for commas in the argument, like in +// the second example above. + #ifdef QLEVER_CPP_17 -#define QL_OPT_CONCEPT(arg) +#define QL_CONCEPT_OR_NOTHING(...) +#define QL_CONCEPT_OR_TYPENAME(...) typename #else -#define QL_OPT_CONCEPT(arg) arg +#define QL_CONCEPT_OR_NOTHING(...) __VA_ARGS__ +#define QL_CONCEPT_OR_TYPENAME(...) __VA_ARGS__ #endif + +// The namespace `ql::concepts` includes concepts that are contained in the +// C++20 standard as well as in `range-v3`. +namespace ql { +namespace concepts { + +#ifdef QLEVER_CPP_17 +using namespace ::concepts; +#else +using namespace std; +#endif + +} // namespace concepts +} // namespace ql diff --git a/src/engine/sparqlExpressions/LiteralExpression.h b/src/engine/sparqlExpressions/LiteralExpression.h index 4d9be1db5b..7da6739c75 100644 --- a/src/engine/sparqlExpressions/LiteralExpression.h +++ b/src/engine/sparqlExpressions/LiteralExpression.h @@ -40,11 +40,10 @@ class LiteralExpression : public SparqlExpression { // Evaluating just returns the constant/literal value. ExpressionResult evaluate(EvaluationContext* context) const override { // Common code for the `Literal` and `Iri` case. - auto getIdOrString = - [this, - &context](const ad_utility::SameAsAny auto& s) - -> ExpressionResult { + auto getIdOrString = [this, &context](const U& s) + -> CPP_ret(ExpressionResult)( + requires ad_utility::SameAsAny) { if (auto ptr = cachedResult_.load(std::memory_order_relaxed)) { return *ptr; } diff --git a/src/util/ConfigManager/ConfigManager.cpp b/src/util/ConfigManager/ConfigManager.cpp index 9ff8c4d3d8..3691c2588a 100644 --- a/src/util/ConfigManager/ConfigManager.cpp +++ b/src/util/ConfigManager/ConfigManager.cpp @@ -23,6 +23,7 @@ #include #include "backports/algorithm.h" +#include "backports/concepts.h" #include "util/Algorithm.h" #include "util/ComparisonWithNan.h" #include "util/ConfigManager/ConfigExceptions.h" @@ -254,7 +255,8 @@ requires std::is_object_v auto ConfigManager::allHashMapEntries( } // ____________________________________________________________________________ -template ReturnReference> +template ) + ReturnReference> std::vector> ConfigManager::configurationOptionsImpl( SimilarTo> auto& @@ -855,7 +857,7 @@ bool ConfigManager::containsOption(const ConfigOption& opt) const { } // ____________________________________________________________________________ -template +template void ConfigManager::ConfigurationDocValidatorAssignment::addEntryUnderKey( const T& key, const ConfigOptionValidatorManager& manager) { getHashMapBasedOnType()[&key].push_back(&manager); @@ -869,7 +871,7 @@ template void ConfigManager::ConfigurationDocValidatorAssignment:: const ConfigOptionValidatorManager&); // ____________________________________________________________________________ -template +template auto ConfigManager::ConfigurationDocValidatorAssignment::getEntriesUnderKey( const T& key) const -> ValueGetterReturnType { // The concerned hash map. diff --git a/src/util/ConfigManager/ConfigManager.h b/src/util/ConfigManager/ConfigManager.h index 63d44eed04..48691e96c5 100644 --- a/src/util/ConfigManager/ConfigManager.h +++ b/src/util/ConfigManager/ConfigManager.h @@ -18,6 +18,7 @@ #include #include +#include "backports/concepts.h" #include "util/ConfigManager/ConfigExceptions.h" #include "util/ConfigManager/ConfigOption.h" #include "util/ConfigManager/ConfigOptionProxy.h" @@ -34,7 +35,7 @@ namespace ConfigManagerImpl { // Shorthand concepts, to reduce code duplication. class ConfigManager; template -concept ConfigOptionOrManager = SameAsAny; +CPP_concept ConfigOptionOrManager = SameAsAny; /* Manages a bunch of `ConfigOption`s. @@ -579,7 +580,8 @@ class ConfigManager { @tparam ReturnReference Should be either `ConfigOption&`, or `const ConfigOption&`. */ - template ReturnReference> + template ) ReturnReference> static std::vector> configurationOptionsImpl( SimilarTo> auto& @@ -716,7 +718,7 @@ class ConfigManager { @brief Add a validator to the list of validators, that are assigned to a `ConfigOption`/`ConfigManager`. */ - template + template void addEntryUnderKey(const T& key, const ConfigOptionValidatorManager& manager); @@ -726,12 +728,12 @@ class ConfigManager { @returns If there is no entry for `Key`, return an empty `std::vector`. */ - template + template ValueGetterReturnType getEntriesUnderKey(const T& key) const; private: // Return either `configOption_` or `configManager_`, based on type. - template + template constexpr const MemoryAdressHashMap& getHashMapBasedOnType() const { if constexpr (std::same_as) { return configOption_; @@ -739,7 +741,7 @@ class ConfigManager { return configManager_; } } - template + template constexpr MemoryAdressHashMap& getHashMapBasedOnType() { if constexpr (std::same_as) { return configOption_; diff --git a/src/util/ConfigManager/ConfigOptionProxy.h b/src/util/ConfigManager/ConfigOptionProxy.h index 87a50c7be7..3050b4fcea 100644 --- a/src/util/ConfigManager/ConfigOptionProxy.h +++ b/src/util/ConfigManager/ConfigOptionProxy.h @@ -27,10 +27,10 @@ access to the referenced config option. @tparam ConfigOptionType The kind of config option, this proxy will reference to. Must be `ConfigOption`, or `const ConfigOption`. */ -template < - SupportedConfigOptionType T, - ad_utility::SameAsAny ConfigOptionType> -class ConfigOptionProxyImplementation { +CPP_template(typename T, typename ConfigOptionType)( + requires SupportedConfigOptionType CPP_and ad_utility::SameAsAny< + ConfigOptionType, ConfigOption, + const ConfigOption>) class ConfigOptionProxyImplementation { ConfigOptionType* option_; public: diff --git a/src/util/FsstCompressor.h b/src/util/FsstCompressor.h index b5c54c4d4d..e663bbf057 100644 --- a/src/util/FsstCompressor.h +++ b/src/util/FsstCompressor.h @@ -13,6 +13,7 @@ #include #include +#include "util/Concepts.h" #include "util/Exception.h" #include "util/Log.h" #include "util/TypeTraits.h" @@ -22,12 +23,16 @@ namespace detail { // `const unsigned char*` which is used below because FSST always works on // unsigned character types. Note that this is one of the few cases where a // `reinterpret_cast` is safe. -constexpr auto castToUnsignedPtr = - [] T>(T ptr) { - using Res = std::conditional_t, - const unsigned char*, unsigned char*>; - return reinterpret_cast(ptr); - }; +struct CastToUnsignedPtr { + CPP_template(typename T)( + requires ad_utility::SameAsAny) auto + operator()(T ptr) const { + using Res = std::conditional_t, + const unsigned char*, unsigned char*>; + return reinterpret_cast(ptr); + }; +}; +constexpr CastToUnsignedPtr castToUnsignedPtr{}; } // namespace detail // A simple C++ wrapper around the C-API of the `FSST` library. It consists of diff --git a/src/util/Iterators.h b/src/util/Iterators.h index 1337b63e27..93ae1b51f4 100644 --- a/src/util/Iterators.h +++ b/src/util/Iterators.h @@ -2,13 +2,13 @@ // Chair of Algorithms and Data Structures. // Author: Johannes Kalmbach -#ifndef QLEVER_ITERATORS_H -#define QLEVER_ITERATORS_H +#pragma once #include #include #include +#include "backports/algorithm.h" #include "util/Enums.h" #include "util/TypeTraits.h" @@ -399,5 +399,3 @@ class InputRangeTypeErased { using iterator = typename InputRangeFromGet::Iterator; }; } // namespace ad_utility - -#endif // QLEVER_ITERATORS_H diff --git a/src/util/JoinAlgorithms/JoinAlgorithms.h b/src/util/JoinAlgorithms/JoinAlgorithms.h index 36af501651..89e1b6611f 100644 --- a/src/util/JoinAlgorithms/JoinAlgorithms.h +++ b/src/util/JoinAlgorithms/JoinAlgorithms.h @@ -9,6 +9,7 @@ #include #include "backports/algorithm.h" +#include "backports/concepts.h" #include "engine/idTable/IdTable.h" #include "global/Id.h" #include "util/Generator.h" @@ -762,8 +763,8 @@ struct BlockZipperJoinImpl { #if defined(Side) || defined(Blocks) #error Side or Blocks are already defined #endif -#define Side SameAsAny auto -#define Blocks SameAsAny auto +#define Side QL_CONCEPT_OR_NOTHING(SameAsAny) auto +#define Blocks QL_CONCEPT_OR_NOTHING(SameAsAny) auto // Type alias for the result of the projection. Elements from the left and // right input must be projected to the same type. diff --git a/src/util/TypeTraits.h b/src/util/TypeTraits.h index 7d39e4aa70..8390667a85 100644 --- a/src/util/TypeTraits.h +++ b/src/util/TypeTraits.h @@ -12,7 +12,7 @@ #include #include -#include "backports/algorithm.h" +#include "backports/concepts.h" #include "util/Forward.h" namespace ad_utility { @@ -136,7 +136,7 @@ concept SimilarToAny = (... || isSimilar); /// True iff `T` is the same as any of the `Ts...`. template -concept SameAsAny = (... || ql::concepts::same_as); +CPP_concept SameAsAny = (... || ql::concepts::same_as); /* The implementation for `SimilarToAnyTypeIn` and `SameAsAnyTypeIn` (see below diff --git a/src/util/Views.h b/src/util/Views.h index 74e6eec14c..ddcf867d3f 100644 --- a/src/util/Views.h +++ b/src/util/Views.h @@ -343,7 +343,8 @@ CPP_template(typename Range, typename ElementType)( generator> reChunkAtSeparator( Range generator, ElementType separator) { std::vector buffer; - for (QL_OPT_CONCEPT(ql::ranges::input_range) auto const& chunk : generator) { + for (QL_CONCEPT_OR_NOTHING(ql::ranges::input_range) auto const& chunk : + generator) { for (ElementType c : chunk) { if (c == separator) { co_yield std::span{buffer.data(), buffer.size()}; diff --git a/test/ConfigOptionProxyTest.cpp b/test/ConfigOptionProxyTest.cpp index 29f9ed3fa3..a62e055af7 100644 --- a/test/ConfigOptionProxyTest.cpp +++ b/test/ConfigOptionProxyTest.cpp @@ -22,9 +22,9 @@ namespace ad_utility { @tparam OptionType Exists to define, if the test should be done with `ConfigOption`, or `const ConfigOption`. */ -template