diff --git a/.clang-format b/.clang-format index 9aaf52be23..bc7beb36a7 100644 --- a/.clang-format +++ b/.clang-format @@ -6,4 +6,8 @@ LineEnding: LF InsertNewlineAtEOF: true RequiresClausePosition: SingleLine IndentRequiresClause: false +AttributeMacros: [CPP_member, CPP_auto_member, CPP_fun, CPP_auto_fun] +# TODO Update to Clang-format-17 and then we can have much nicer CPP_... macros. +#Macros: +# - CPP_template_2(x)=template requires ... diff --git a/benchmark/JoinAlgorithmBenchmark.cpp b/benchmark/JoinAlgorithmBenchmark.cpp index 553785609f..49d8059e83 100644 --- a/benchmark/JoinAlgorithmBenchmark.cpp +++ b/benchmark/JoinAlgorithmBenchmark.cpp @@ -770,27 +770,29 @@ class GeneralInterfaceImplementation : public BenchmarkInterface { }; }; auto generateBiggerEqualLambdaDesc = - [](const ad_utility::isInstantiation< - ad_utility::ConstConfigOptionProxy> auto& option, - const auto& minimumValue, bool canBeEqual) { - return absl::StrCat("'", option.getConfigOption().getIdentifier(), - "' must be bigger than", - canBeEqual ? ", or equal to," : "", " ", - minimumValue, "."); - }; + []>>( + const OptionType& option, const auto& minimumValue, + bool canBeEqual) { + return absl::StrCat("'", option.getConfigOption().getIdentifier(), + "' must be bigger than", + canBeEqual ? ", or equal to," : "", " ", minimumValue, + "."); + }; // Object with a `operator()` for the `<=` operator. auto lessEqualLambda = std::less_equal{}; auto generateLessEqualLambdaDesc = - [](const ad_utility::isInstantiation< - ad_utility::ConstConfigOptionProxy> auto& lhs, - const ad_utility::isInstantiation< - ad_utility::ConstConfigOptionProxy> auto& rhs) { - return absl::StrCat("'", lhs.getConfigOption().getIdentifier(), - "' must be smaller than, or equal to, " - "'", - rhs.getConfigOption().getIdentifier(), "'."); - }; + []>>( + const OptionType& lhs, const OptionType& rhs) { + return absl::StrCat("'", lhs.getConfigOption().getIdentifier(), + "' must be smaller than, or equal to, " + "'", + rhs.getConfigOption().getIdentifier(), "'."); + }; // Adding the validators. 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/benchmark/util/ResultTableColumnOperations.h b/benchmark/util/ResultTableColumnOperations.h index 46883c6208..3967ea77b0 100644 --- a/benchmark/util/ResultTableColumnOperations.h +++ b/benchmark/util/ResultTableColumnOperations.h @@ -21,8 +21,9 @@ Column number together with the type of value, that can be found inside the column. Note, that **all** entries in the column must have the same type, because of `ResultTable::getEntry`. */ -template Type> -struct ColumnNumWithType { +CPP_template(typename Type)( + requires ad_utility::SameAsAnyTypeIn< + Type, ResultTable::EntryType>) struct ColumnNumWithType { using ColumnType = Type; const size_t columnNum_; }; diff --git a/src/backports/concepts.h b/src/backports/concepts.h index 3049c55c5c..17a2f3e5c4 100644 --- a/src/backports/concepts.h +++ b/src/backports/concepts.h @@ -5,6 +5,8 @@ #pragma once #include + +#include "backports/cppTemplate2.h" #ifndef QLEVER_CPP_17 #include #endif @@ -32,18 +34,34 @@ // NOTE: The macros are variadic to allow for commas in the argument, like in // the second example above. +// Additionally define the macros `CPP_template_2` and `CPP_and_2` that can +// be used to constrain member functions of classes where the outer class +// has already been constrained with `CPP_template`. For a detailed example, see +// the `test/backports/ConceptsTest.cpp` file. + #ifdef QLEVER_CPP_17 #define QL_CONCEPT_OR_NOTHING(...) #define QL_CONCEPT_OR_TYPENAME(...) typename +#define CPP_template_2 CPP_template_2_SFINAE +#define CPP_and_2 CPP_and_2_sfinae +#define CPP_and_def CPP_and_sfinae_def +#define CPP_and_2_def CPP_and_2_def_sfinae +#define CPP_variadic_template CPP_template_NO_DEFAULT_SFINAE +#define CPP_member_def CPP_member_def_sfinae #else #define QL_CONCEPT_OR_NOTHING(...) __VA_ARGS__ #define QL_CONCEPT_OR_TYPENAME(...) __VA_ARGS__ +#define CPP_template_2 CPP_template +#define CPP_and_2 CPP_and +#define CPP_and_def CPP_and +#define CPP_and_2_def CPP_and +#define CPP_variadic_template CPP_template +#define CPP_member_def CPP_member #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 { +namespace ql::concepts { #ifdef QLEVER_CPP_17 using namespace ::concepts; @@ -51,5 +69,4 @@ using namespace ::concepts; using namespace std; #endif -} // namespace concepts -} // namespace ql +} // namespace ql::concepts diff --git a/src/backports/cppTemplate2.h b/src/backports/cppTemplate2.h new file mode 100644 index 0000000000..a5c89b2c16 --- /dev/null +++ b/src/backports/cppTemplate2.h @@ -0,0 +1,60 @@ +// Copyright 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Author: Johannes Kalmbach + +#pragma once +#include + +// The internal reimplementation of a `CPP_variadic_template` macro +// that can be used for variadic template functions. +#define CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_0(...) \ + , std::enable_if_t = 0 > CPP_PP_IGNORE_CXX2A_COMPAT_END + +#define CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_3_requires + +#define CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_WHICH_(FIRST, ...) \ + CPP_PP_EVAL(CPP_PP_CHECK, \ + CPP_PP_CAT(CPP_TEMPLATE_SFINAE_PROBE_CONCEPT_, FIRST)) + +#define CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_(...) \ + CPP_PP_CAT(CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_, \ + CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_WHICH_(__VA_ARGS__, )) \ + (__VA_ARGS__) + +#define CPP_template_NO_DEFAULT_SFINAE(...) \ + CPP_PP_IGNORE_CXX2A_COMPAT_BEGIN \ + template <__VA_ARGS__ CPP_TEMPLATE_NO_DEFAULT_SFINAE_AUX_ + +// The internal reimplementation of a `CPP_template_2` and `CPP_and_2` macro +// that can be used for class members when the outer class has already been +// constrained using `CPP_template`. +#define CPP_TEMPLATE_2_SFINAE_AUX_0(...) \ + , bool CPP_true_2 = true, \ + std::enable_if_t < \ + CPP_PP_CAT(CPP_TEMPLATE_SFINAE_AUX_3_, __VA_ARGS__) && \ + CPP_BOOL(CPP_true_2), \ + int > = 0 > CPP_PP_IGNORE_CXX2A_COMPAT_END + +#define CPP_TEMPLATE_2_SFINAE_AUX_3_requires + +#define CPP_TEMPLATE_2_SFINAE_AUX_WHICH_(FIRST, ...) \ + CPP_PP_EVAL(CPP_PP_CHECK, \ + CPP_PP_CAT(CPP_TEMPLATE_SFINAE_PROBE_CONCEPT_, FIRST)) + +#define CPP_TEMPLATE_2_SFINAE_AUX_(...) \ + CPP_PP_CAT(CPP_TEMPLATE_2_SFINAE_AUX_, \ + CPP_TEMPLATE_2_SFINAE_AUX_WHICH_(__VA_ARGS__, )) \ + (__VA_ARGS__) + +#define CPP_template_2_SFINAE(...) \ + CPP_PP_IGNORE_CXX2A_COMPAT_BEGIN \ + template <__VA_ARGS__ CPP_TEMPLATE_2_SFINAE_AUX_ + +/// INTERNAL ONLY +#define CPP_and_def_sfinae &&CPP_BOOL(CPP_true), int >, std::enable_if_t < +#define CPP_and_2_sfinae &&CPP_BOOL(CPP_true_2), int > = 0, std::enable_if_t < +#define CPP_and_2_def_sfinae &&CPP_BOOL(CPP_true_2), int >, std::enable_if_t < + +#define CPP_member_def_sfinae \ + template diff --git a/src/engine/Bind.cpp b/src/engine/Bind.cpp index 95de8a4dfe..a0e7e78c9a 100644 --- a/src/engine/Bind.cpp +++ b/src/engine/Bind.cpp @@ -27,6 +27,9 @@ size_t Bind::getCostEstimate() { return _subtree->getCostEstimate() + _subtree->getSizeEstimate(); } +// We delegate the limit to the child operation, so we always support it. +bool Bind::supportsLimit() const { return true; } + float Bind::getMultiplicity(size_t col) { // this is the newly added column if (col == getResultWidth() - 1) { @@ -93,6 +96,7 @@ IdTable Bind::cloneSubView(const IdTable& idTable, // _____________________________________________________________________________ ProtoResult Bind::computeResult(bool requestLaziness) { + _subtree->setLimit(getLimit()); LOG(DEBUG) << "Get input to BIND operation..." << std::endl; std::shared_ptr subRes = _subtree->getResult(requestLaziness); LOG(DEBUG) << "Got input to Bind operation." << std::endl; diff --git a/src/engine/Bind.h b/src/engine/Bind.h index 34c515fb54..0abd5b2cec 100644 --- a/src/engine/Bind.h +++ b/src/engine/Bind.h @@ -30,6 +30,7 @@ class Bind : public Operation { [[nodiscard]] size_t getResultWidth() const override; std::vector getChildren() override; size_t getCostEstimate() override; + bool supportsLimit() const override; private: uint64_t getSizeEstimateBeforeLimit() override; diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index e81c834303..98faba8743 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -14,5 +14,6 @@ add_library(engine CartesianProductJoin.cpp TextIndexScanForWord.cpp TextIndexScanForEntity.cpp TextLimit.cpp LazyGroupBy.cpp GroupByHashMapOptimization.cpp SpatialJoin.cpp CountConnectedSubgraphs.cpp SpatialJoinAlgorithms.cpp PathSearch.cpp ExecuteUpdate.cpp - Describe.cpp GraphStoreProtocol.cpp) + Describe.cpp GraphStoreProtocol.cpp + QueryExecutionContext.cpp) qlever_target_link_libraries(engine util index parser sparqlExpressions http SortPerformanceEstimator Boost::iostreams s2) diff --git a/src/engine/ExecuteUpdate.cpp b/src/engine/ExecuteUpdate.cpp index 3637410d22..7e43ae1ef3 100644 --- a/src/engine/ExecuteUpdate.cpp +++ b/src/engine/ExecuteUpdate.cpp @@ -176,9 +176,31 @@ ExecuteUpdate::computeGraphUpdateQuads( cancellationHandle->throwIfCancelled(); } } + sortAndRemoveDuplicates(toInsert); + sortAndRemoveDuplicates(toDelete); + metadata.inUpdate_ = DeltaTriplesCount{static_cast(toInsert.size()), + static_cast(toDelete.size())}; + toDelete = setMinus(toDelete, toInsert); metadata.triplePreparationTime_ = timer.msecs(); return { IdTriplesAndLocalVocab{std::move(toInsert), std::move(localVocabInsert)}, IdTriplesAndLocalVocab{std::move(toDelete), std::move(localVocabDelete)}}; } + +// _____________________________________________________________________________ +void ExecuteUpdate::sortAndRemoveDuplicates( + std::vector>& container) { + ql::ranges::sort(container); + container.erase(std::unique(container.begin(), container.end()), + container.end()); +} + +// _____________________________________________________________________________ +std::vector> ExecuteUpdate::setMinus( + const std::vector>& a, const std::vector>& b) { + std::vector> reducedToDelete; + reducedToDelete.reserve(a.size()); + ql::ranges::set_difference(a, b, std::back_inserter(reducedToDelete)); + return reducedToDelete; +} diff --git a/src/engine/ExecuteUpdate.h b/src/engine/ExecuteUpdate.h index 0f7e462c44..34b6b8177a 100644 --- a/src/engine/ExecuteUpdate.h +++ b/src/engine/ExecuteUpdate.h @@ -17,6 +17,7 @@ struct UpdateMetadata { Milliseconds triplePreparationTime_ = Zero; Milliseconds insertionTime_ = Zero; Milliseconds deletionTime_ = Zero; + std::optional inUpdate_; }; class ExecuteUpdate { @@ -71,4 +72,16 @@ class ExecuteUpdate { const CancellationHandle& cancellationHandle, UpdateMetadata& metadata); FRIEND_TEST(ExecuteUpdate, computeGraphUpdateQuads); + + // After the operation the vector is sorted and contains no duplicate + // elements. + static void sortAndRemoveDuplicates(std::vector>& container); + FRIEND_TEST(ExecuteUpdate, sortAndRemoveDuplicates); + + // For two sorted vectors `A` and `B` return a new vector + // that contains the element of `A\B`. + // Precondition: the inputs must be sorted. + static std::vector> setMinus(const std::vector>& a, + const std::vector>& b); + FRIEND_TEST(ExecuteUpdate, setMinus); }; diff --git a/src/engine/ExportQueryExecutionTrees.cpp b/src/engine/ExportQueryExecutionTrees.cpp index 90813a3a2e..f50bca9f61 100644 --- a/src/engine/ExportQueryExecutionTrees.cpp +++ b/src/engine/ExportQueryExecutionTrees.cpp @@ -901,7 +901,7 @@ cppcoro::generator ExportQueryExecutionTrees::computeResult( auto inner = ad_utility::ConstexprSwitch(compute, mediaType); + sparqlJson, qleverJson>{}(compute, mediaType); return convertStreamGeneratorForChunkedTransfer(std::move(inner)); } diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 7f20b58108..4ac9bc5aea 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -104,10 +104,10 @@ ProtoResult Filter::computeResult(bool requestLaziness) { } // _____________________________________________________________________________ -template Table> -IdTable Filter::filterIdTable(std::vector sortedBy, - Table&& idTable, - const LocalVocab& localVocab) const { +CPP_template_def(typename Table)(requires ad_utility::SimilarTo) + IdTable Filter::filterIdTable(std::vector sortedBy, + Table&& idTable, + const LocalVocab& localVocab) const { size_t width = idTable.numColumns(); IdTable result{width, getExecutionContext()->getAllocator()}; @@ -120,10 +120,11 @@ IdTable Filter::filterIdTable(std::vector sortedBy, } // _____________________________________________________________________________ -template Table> -void Filter::computeFilterImpl(IdTable& dynamicResultTable, Table&& inputTable, - const LocalVocab& localVocab, - std::vector sortedBy) const { +CPP_template_def(int WIDTH, typename Table)( + requires ad_utility::SimilarTo) void Filter:: + computeFilterImpl(IdTable& dynamicResultTable, Table&& inputTable, + const LocalVocab& localVocab, + std::vector sortedBy) const { AD_CONTRACT_CHECK(inputTable.numColumns() == WIDTH || WIDTH == 0); IdTableStatic resultTable = std::move(dynamicResultTable).toStatic(WIDTH)>(); diff --git a/src/engine/Filter.h b/src/engine/Filter.h index 2ac01e1845..35700c4cb0 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -70,13 +70,16 @@ class Filter : public Operation { ProtoResult computeResult(bool requestLaziness) override; // Perform the actual filter operation of the data provided. - template Table> - void computeFilterImpl(IdTable& dynamicResultTable, Table&& input, - const LocalVocab& localVocab, - std::vector sortedBy) const; + CPP_template(int WIDTH, typename Table)( + requires ad_utility::SimilarTo< + Table, IdTable>) void computeFilterImpl(IdTable& dynamicResultTable, + Table&& input, + const LocalVocab& localVocab, + std::vector + sortedBy) const; // Run `computeFilterImpl` on the provided IdTable - template Table> - IdTable filterIdTable(std::vector sortedBy, Table&& idTable, - const LocalVocab& localVocab) const; + CPP_template(typename Table)(requires ad_utility::SimilarTo) + IdTable filterIdTable(std::vector sortedBy, Table&& idTable, + const LocalVocab& localVocab) const; }; diff --git a/src/engine/OptionalJoin.h b/src/engine/OptionalJoin.h index 91d1a502d9..7685bd8708 100644 --- a/src/engine/OptionalJoin.h +++ b/src/engine/OptionalJoin.h @@ -58,14 +58,8 @@ class OptionalJoin : public Operation { return {_left.get(), _right.get()}; } - /** - * @brief Joins two result tables on any number of columns, inserting the - * special value ID_NO_VALUE for any entries marked as optional. - * @param a - * @param b - * @param joinColumns - * @param result - */ + // Joins two result tables on any number of columns, inserting the special + // value `Id::makeUndefined()` for any entries marked as optional. void optionalJoin( const IdTable& left, const IdTable& right, const std::vector>& joinColumns, diff --git a/src/engine/QueryExecutionContext.cpp b/src/engine/QueryExecutionContext.cpp new file mode 100644 index 0000000000..b458206613 --- /dev/null +++ b/src/engine/QueryExecutionContext.cpp @@ -0,0 +1,11 @@ +// Copyright 2025, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Robin Textor-Falconi + +#include "engine/QueryExecutionContext.h" + +#include "global/RuntimeParameters.h" + +bool QueryExecutionContext::areWebSocketUpdatesEnabled() { + return RuntimeParameters().get<"websocket-updates-enabled">(); +} diff --git a/src/engine/QueryExecutionContext.h b/src/engine/QueryExecutionContext.h index 1e891a398f..501f2ba538 100644 --- a/src/engine/QueryExecutionContext.h +++ b/src/engine/QueryExecutionContext.h @@ -5,8 +5,6 @@ #pragma once -#include - #include #include @@ -19,7 +17,6 @@ #include "index/Index.h" #include "util/Cache.h" #include "util/ConcurrentCache.h" -#include "util/Synchronized.h" // The value of the `QueryResultCache` below. It consists of a `Result` together // with its `RuntimeInfo`. @@ -151,6 +148,9 @@ class QueryExecutionContext { return areWebsocketUpdatesEnabled_; } + private: + static bool areWebSocketUpdatesEnabled(); + private: const Index& _index; @@ -168,6 +168,5 @@ class QueryExecutionContext { std::function updateCallback_; // Cache the state of that runtime parameter to reduce the contention of the // mutex. - bool areWebsocketUpdatesEnabled_ = - RuntimeParameters().get<"websocket-updates-enabled">(); + bool areWebsocketUpdatesEnabled_ = areWebSocketUpdatesEnabled(); }; diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index cef01b1129..79a3783f86 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -12,7 +12,7 @@ #include #include "engine/Sort.h" -#include "parser/RdfEscaping.h" +#include "global/RuntimeParameters.h" using std::string; diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index afb15ac841..6fcb72add8 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -83,8 +83,6 @@ void Server::initialize(const string& indexBaseName, bool useText, LOG(INFO) << "Access token for restricted API calls is \"" << accessToken_ << "\"" << std::endl; - LOG(INFO) << "The server is ready, listening for requests on port " - << std::to_string(port_) << " ..." << std::endl; } // _____________________________________________________________________________ @@ -166,6 +164,9 @@ void Server::run(const string& indexBaseName, bool useText, bool usePatterns, // Initialize the index initialize(indexBaseName, useText, usePatterns, loadAllPermutations); + LOG(INFO) << "The server is ready, listening for requests on port " + << std::to_string(httpServer.getPort()) << " ..." << std::endl; + // Start listening for connections on the server. httpServer.run(); } @@ -297,12 +298,15 @@ Awaitable Server::process( [this] { // Use `this` explicitly to silence false-positive errors on the // captured `this` being unused. - this->index_.deltaTriplesManager().clear(); + return this->index_.deltaTriplesManager().modify( + [](auto& deltaTriples) { + deltaTriples.clear(); + return deltaTriples.getCounts(); + }); }, handle); - co_await std::move(coroutine); - response = createOkResponse("Delta triples have been cleared", request, - MediaType::textPlain); + auto countAfterClear = co_await std::move(coroutine); + response = createJsonResponse(nlohmann::json{countAfterClear}, request); } else if (auto cmd = checkParameter("cmd", "get-settings")) { logCommand(cmd, "get server settings"); response = createJsonResponse(RuntimeParameters().toMap(), request); @@ -468,8 +472,8 @@ auto Server::cancelAfterDeadline( // _____________________________________________________________________________ auto Server::setupCancellationHandle( const ad_utility::websocket::QueryId& queryId, TimeLimit timeLimit) - -> ad_utility::isInstantiation< - CancellationHandleAndTimeoutTimerCancel> auto { + -> QL_CONCEPT_OR_NOTHING(ad_utility::isInstantiation< + CancellationHandleAndTimeoutTimerCancel>) auto { auto cancellationHandle = queryRegistry_.getCancellationHandle(queryId); AD_CORRECTNESS_CHECK(cancellationHandle); cancellationHandle->startWatchDog(); @@ -821,6 +825,10 @@ json Server::createResponseMetadataForUpdate( response["delta-triples"]["after"] = nlohmann::json(countAfter); response["delta-triples"]["difference"] = nlohmann::json(countAfter - countBefore); + if (updateMetadata.inUpdate_.has_value()) { + response["delta-triples"]["operation"] = + json(updateMetadata.inUpdate_.value()); + } response["time"]["planning"] = formatTime(runtimeInfoWholeOp.timeQueryPlanning); response["time"]["where"] = diff --git a/src/engine/Server.h b/src/engine/Server.h index 9383d50c0d..24e969279a 100644 --- a/src/engine/Server.h +++ b/src/engine/Server.h @@ -97,8 +97,10 @@ class Server { using SharedCancellationHandle = ad_utility::SharedCancellationHandle; - template CancelTimeout> - struct CancellationHandleAndTimeoutTimerCancel { + CPP_template(typename CancelTimeout)( + requires ad_utility::isInstantiation< + CancelTimeout, + absl::Cleanup>) struct CancellationHandleAndTimeoutTimerCancel { SharedCancellationHandle handle_; /// Object of type `absl::Cleanup` that when destroyed cancels the timer /// that would otherwise invoke the cancellation of the `handle_` via the @@ -109,10 +111,11 @@ class Server { // Clang doesn't seem to be able to automatically deduce the type correctly. // and GCC 11 thinks deduction guides are not allowed within classes. #ifdef __clang__ - template CancelTimeout> - CancellationHandleAndTimeoutTimerCancel(SharedCancellationHandle, - CancelTimeout) - -> CancellationHandleAndTimeoutTimerCancel; + CPP_template(typename CancelTimeout)( + requires ad_utility::isInstantiation) + CancellationHandleAndTimeoutTimerCancel(SharedCancellationHandle, + CancelTimeout) + -> CancellationHandleAndTimeoutTimerCancel; #endif /// Handle a single HTTP request. Check whether a file request or a query was @@ -241,8 +244,8 @@ class Server { /// member can be invoked to cancel the imminent cancellation via timeout. auto setupCancellationHandle(const ad_utility::websocket::QueryId& queryId, TimeLimit timeLimit) - -> ad_utility::isInstantiation< - CancellationHandleAndTimeoutTimerCancel> auto; + -> QL_CONCEPT_OR_NOTHING(ad_utility::isInstantiation< + CancellationHandleAndTimeoutTimerCancel>) auto; /// Check if the access token is valid. Return true if the access token /// exists and is valid. Return false if there's no access token passed. diff --git a/src/engine/idTable/IdTableRow.h b/src/engine/idTable/IdTableRow.h index cba93df96e..bf546f7cdb 100644 --- a/src/engine/idTable/IdTableRow.h +++ b/src/engine/idTable/IdTableRow.h @@ -385,8 +385,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/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index a23ce3da95..96c8a106d9 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -58,8 +58,13 @@ requires isVectorResult auto idGenerator(const S& values, size_t targetSize, // For the `Variable` and `SetOfIntervals` class, the generator from the // `sparqlExpressions` module already yields the `ValueIds`. -template S> -auto idGenerator(S input, size_t targetSize, const EvaluationContext* context) { +CPP_template(typename S)( + requires ad_utility::SimilarToAny< + S, Variable, + ad_utility::SetOfIntervals>) auto idGenerator(S input, + size_t targetSize, + const EvaluationContext* + context) { return sparqlExpression::detail::makeGenerator(std::move(input), targetSize, context); } diff --git a/src/engine/sparqlExpressions/SparqlExpressionTypes.h b/src/engine/sparqlExpressions/SparqlExpressionTypes.h index b54277f2c9..5f3a16a086 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionTypes.h +++ b/src/engine/sparqlExpressions/SparqlExpressionTypes.h @@ -115,8 +115,10 @@ concept SingleExpressionResult = ad_utility::SimilarToAnyTypeIn; // Copy an expression result. -inline ExpressionResult copyExpressionResult( - ad_utility::SimilarTo auto&& result) { +CPP_template(typename ResultT)( + requires ad_utility::SimilarTo) inline ExpressionResult + copyExpressionResult(ResultT&& result) { auto copyIfCopyable = [](const R& x) -> ExpressionResult { if constexpr (requires { R{AD_FWD(x)}; }) { @@ -332,11 +334,12 @@ std::optional evaluateOnSpecializedFunctionsIfPossible( // for multiple `ValueGetters` because there can be multiple `ValueGetters` as // well as zero or more `SpezializedFunctions`, but there can only be a single // parameter pack in C++. -template < - size_t NumOperands, - ad_utility::isInstantiation - FunctionAndValueGettersT, - ad_utility::isInstantiation... SpecializedFunctions> +template ) + FunctionAndValueGettersT, + QL_CONCEPT_OR_TYPENAME(ad_utility::isInstantiation< + SpecializedFunction>)... SpecializedFunctions> struct Operation { private: using OriginalValueGetters = typename FunctionAndValueGettersT::ValueGetters; diff --git a/src/global/Id.h b/src/global/Id.h index a62cf36142..af93705ba8 100644 --- a/src/global/Id.h +++ b/src/global/Id.h @@ -3,16 +3,12 @@ // Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) #pragma once -#include - #include -#include #include "global/ValueId.h" -#include "util/Exception.h" using Id = ValueId; -typedef uint16_t Score; +using Score = uint16_t; // TODO Make the following ID and index types strong. using ColumnIndex = uint64_t; @@ -23,92 +19,3 @@ using WordIndex = uint64_t; using WordOrEntityIndex = uint64_t; using TextBlockIndex = uint64_t; using CompressionCode = uint64_t; - -// Integers, that are probably not integers but strong IDs or indices, but their -// true nature is still to be discovered. -using UnknownIndex = uint64_t; - -// A value to use when the result should be empty (e.g. due to an optional join) -// The highest two values are used as sentinels. -static const Id ID_NO_VALUE = Id::makeUndefined(); - -namespace ad_utility { - -// An exception that is thrown when an integer overflow occurs in the -// `MilestoneIdManager` -class MilestoneIdOverflowException : public std::exception { - private: - std::string _message; - - public: - explicit MilestoneIdOverflowException(std::string message) - : _message{std::move(message)} {} - [[nodiscard]] const char* what() const noexcept override { - return _message.c_str(); - } -}; - -// Manages two kinds of IDs: plain IDs (unsigned 64-bit integers, just called -// "IDs" in the following), and milestone IDs (unsigned 64-bit integers that are -// multiples of `distanceBetweenMilestones`. This class has the functionality to -// find the next milestone of plain ID, to check whether an ID is amilestone ID -// and to convert milestone IDs from and to a local ID space. -template -class MilestoneIdManager { - private: - // The next free ID; - uint64_t _nextId{0}; - // The last ID that was assigned. Used for overflow detection. - uint64_t _previousId{0}; - - public: - MilestoneIdManager() = default; - - // The maximum number of milestone Ids. - constexpr static uint64_t numMilestoneIds = - std::numeric_limits::max() / distanceBetweenMilestones; - - // Get the smallest milestone ID that is larger than all (milestone and - // non-milestone) previously obtained IDs. - uint64_t getNextMilestoneId() { - if (!isMilestoneId(_nextId)) { - _nextId = (milestoneIdFromLocal(milestoneIdToLocal(_nextId) + 1)); - } - return getNextId(); - } - - // Get the smallest ID that is larger than all previously obtained IDs. - uint64_t getNextId() { - if (_nextId < _previousId) { - throw MilestoneIdOverflowException{absl::StrCat( - "Overflow while assigning Ids from a MilestoneIdManager. The " - "previous " - "milestone Id was ", - _previousId, " the next id would be ", _nextId, - ". The maximum number of milestones is ", numMilestoneIds, ".")}; - } - _previousId = _nextId; - _nextId++; - return _previousId; - } - - // Is this ID a milestone id, equivalently: Is this ID a multiple of - // `distanceBetweenMilestones`? - constexpr static bool isMilestoneId(uint64_t id) { - return id % distanceBetweenMilestones == 0; - } - - // Convert a milestone ID to its "local" ID by dividing it by - // `distanceBetweenMilestones` (the i-th milestone ID will become `i`). - constexpr static uint64_t milestoneIdToLocal(uint64_t id) { - return id / distanceBetweenMilestones; - } - - // Convert "local" ID to milestone ID by multiplying it with - // `distanceBetweenMilestones`. - constexpr static uint64_t milestoneIdFromLocal(uint64_t id) { - return id * distanceBetweenMilestones; - } -}; - -} // namespace ad_utility diff --git a/src/global/Pattern.h b/src/global/Pattern.h index 1005add22d..b69540e7c6 100644 --- a/src/global/Pattern.h +++ b/src/global/Pattern.h @@ -12,6 +12,7 @@ #include #include +#include "backports/concepts.h" #include "global/Id.h" #include "util/ExceptionHandling.h" #include "util/File.h" @@ -101,11 +102,16 @@ class CompactVectorOfStrings { /** * @brief Fills this CompactVectorOfStrings with input. * @param The input from which to build the vector. + * Note: In C++20 mode we use a `requires clause`, in C++17 mode we use a + * static assert. Both work, as there is only one overload of `build`. */ template - requires requires(T t) { + QL_CONCEPT_OR_NOTHING(requires requires(T t) { { *(t.begin()->begin()) } -> ad_utility::SimilarTo; - } void build(const T& input) { + }) + void build(const T& input) { + static_assert( + ad_utility::SimilarTobegin())), data_type>); // 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/CompressedRelation.cpp b/src/index/CompressedRelation.cpp index 63c51bd464..db84e58661 100644 --- a/src/index/CompressedRelation.cpp +++ b/src/index/CompressedRelation.cpp @@ -752,13 +752,14 @@ size_t CompressedRelationReader::getResultSizeOfScan( } // ____________________________________________________________________________ -IdTable CompressedRelationReader::getDistinctColIdsAndCountsImpl( - ad_utility::InvocableWithConvertibleReturnType< - Id, const CompressedBlockMetadata::PermutedTriple&> auto idGetter, - const ScanSpecification& scanSpec, - const std::vector& allBlocksMetadata, - const CancellationHandle& cancellationHandle, - const LocatedTriplesPerBlock& locatedTriplesPerBlock) const { +CPP_template_def(typename IdGetter)( + requires ad_utility::InvocableWithConvertibleReturnType< + IdGetter, Id, const CompressedBlockMetadata::PermutedTriple&>) IdTable + CompressedRelationReader::getDistinctColIdsAndCountsImpl( + IdGetter idGetter, const ScanSpecification& scanSpec, + const std::vector& allBlocksMetadata, + const CancellationHandle& cancellationHandle, + const LocatedTriplesPerBlock& locatedTriplesPerBlock) const { // The result has two columns: one for the distinct `Id`s and one for their // counts. IdTableStatic<2> table(allocator_); diff --git a/src/index/CompressedRelation.h b/src/index/CompressedRelation.h index 96fefef2be..1ee4d2967d 100644 --- a/src/index/CompressedRelation.h +++ b/src/index/CompressedRelation.h @@ -746,13 +746,14 @@ class CompressedRelationReader { // The common implementation for `getDistinctCol0IdsAndCounts` and // `getCol1IdsAndCounts`. - IdTable getDistinctColIdsAndCountsImpl( - ad_utility::InvocableWithConvertibleReturnType< - Id, const CompressedBlockMetadata::PermutedTriple&> auto idGetter, - const ScanSpecification& scanSpec, - const std::vector& allBlocksMetadata, - const CancellationHandle& cancellationHandle, - const LocatedTriplesPerBlock& locatedTriplesPerBlock) const; + CPP_template(typename IdGetter)( + requires ad_utility::InvocableWithConvertibleReturnType< + IdGetter, Id, const CompressedBlockMetadata::PermutedTriple&>) IdTable + getDistinctColIdsAndCountsImpl( + IdGetter idGetter, const ScanSpecification& scanSpec, + const std::vector& allBlocksMetadata, + const CancellationHandle& cancellationHandle, + const LocatedTriplesPerBlock& locatedTriplesPerBlock) const; }; // TODO diff --git a/src/index/DeltaTriples.cpp b/src/index/DeltaTriples.cpp index e8d35fa3b3..c3ecad2bde 100644 --- a/src/index/DeltaTriples.cpp +++ b/src/index/DeltaTriples.cpp @@ -150,9 +150,9 @@ void DeltaTriples::modifyTriplesImpl(CancellationHandle cancellationHandle, TriplesToHandlesMap& targetMap, TriplesToHandlesMap& inverseMap) { rewriteLocalVocabEntriesAndBlankNodes(triples); - ql::ranges::sort(triples); - auto first = std::unique(triples.begin(), triples.end()); - triples.erase(first, triples.end()); + AD_EXPENSIVE_CHECK(ql::ranges::is_sorted(triples)); + AD_EXPENSIVE_CHECK(std::unique(triples.begin(), triples.end()) == + triples.end()); std::erase_if(triples, [&targetMap](const IdTriple<0>& triple) { return targetMap.contains(triple); }); @@ -248,6 +248,8 @@ template void DeltaTriplesManager::modify( std::function const&); template nlohmann::json DeltaTriplesManager::modify( const std::function&); +template DeltaTriplesCount DeltaTriplesManager::modify( + const std::function&); // _____________________________________________________________________________ void DeltaTriplesManager::clear() { modify(&DeltaTriples::clear); } diff --git a/src/index/DeltaTriples.h b/src/index/DeltaTriples.h index 3a2037768c..aa1f3e67e7 100644 --- a/src/index/DeltaTriples.h +++ b/src/index/DeltaTriples.h @@ -40,8 +40,8 @@ class SharedLocatedTriplesSnapshot // A class for keeping track of the number of triples of the `DeltaTriples`. struct DeltaTriplesCount { - size_t triplesInserted_; - size_t triplesDeleted_; + int64_t triplesInserted_; + int64_t triplesDeleted_; /// Output as json. The signature of this function is mandated by the json /// library to allow for implicit conversion. @@ -146,8 +146,12 @@ class DeltaTriples { void clear(); // The number of delta triples added and subtracted. - size_t numInserted() const { return triplesInserted_.size(); } - size_t numDeleted() const { return triplesDeleted_.size(); } + int64_t numInserted() const { + return static_cast(triplesInserted_.size()); + } + int64_t numDeleted() const { + return static_cast(triplesDeleted_.size()); + } DeltaTriplesCount getCounts() const; // Insert triples. diff --git a/src/index/IndexBuilderTypes.h b/src/index/IndexBuilderTypes.h index 85f3f5d195..715a9e6d5a 100644 --- a/src/index/IndexBuilderTypes.h +++ b/src/index/IndexBuilderTypes.h @@ -262,7 +262,8 @@ auto getIdMapLambdas( * - All Ids are assigned according to itemArray[idx] */ const auto itemMapLamdaCreator = [&itemArray, indexPtr](const size_t idx) { - return [&map = *itemArray[idx], indexPtr](ad_utility::Rvalue auto&& tr) { + return [&map = *itemArray[idx], + indexPtr](QL_CONCEPT_OR_NOTHING(ad_utility::Rvalue) auto&& tr) { auto lt = indexPtr->tripleToInternalRepresentation(AD_FWD(tr)); OptionalIds res; // get Ids for the actual triple and store them in the result. diff --git a/src/index/StringSortComparator.h b/src/index/StringSortComparator.h index 651b857e6b..b5cf70c6d4 100644 --- a/src/index/StringSortComparator.h +++ b/src/index/StringSortComparator.h @@ -52,8 +52,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/index/VocabularyMerger.h b/src/index/VocabularyMerger.h index ae72da46c6..0048baea51 100644 --- a/src/index/VocabularyMerger.h +++ b/src/index/VocabularyMerger.h @@ -68,8 +68,8 @@ struct VocabularyMetaData { bool contains(Id id) const { return begin_ <= id && id < end_; } private: - Id begin_ = ID_NO_VALUE; - Id end_ = ID_NO_VALUE; + Id begin_ = Id::makeUndefined(); + Id end_ = Id::makeUndefined(); std::string prefix_; bool beginWasSeen_ = false; }; diff --git a/src/index/VocabularyMergerImpl.h b/src/index/VocabularyMergerImpl.h index 1abd7ac783..6c83555c7f 100644 --- a/src/index/VocabularyMergerImpl.h +++ b/src/index/VocabularyMergerImpl.h @@ -95,7 +95,7 @@ auto VocabularyMerger::mergeVocabulary(const std::string& basename, auto mergedWords = ad_utility::parallelMultiwayMerge( - 0.8 * memoryToUse, generators, lessThanForQueue); + 0.8 * memoryToUse, std::move(generators), lessThanForQueue); ad_utility::ProgressBar progressBar{metaData_.numWordsTotal(), "Words merged: "}; for (QueueWord& currentWord : ql::views::join(mergedWords)) { diff --git a/src/index/vocabulary/CompressionWrappers.h b/src/index/vocabulary/CompressionWrappers.h index c299aa6119..a88f8bfbd3 100644 --- a/src/index/vocabulary/CompressionWrappers.h +++ b/src/index/vocabulary/CompressionWrappers.h @@ -5,6 +5,7 @@ #pragma once #include "backports/algorithm.h" +#include "backports/concepts.h" #include "index/PrefixHeuristic.h" #include "index/vocabulary/PrefixCompressor.h" #include "util/FsstCompressor.h" @@ -25,6 +26,7 @@ CPP_requires( std::vector, std::vector>, ad_utility::SimilarTo(t)), Decoder>)); + template CPP_concept BulkResultForDecoder = CPP_requires_ref(BulkResultForDecoder_, T, Decoder); diff --git a/src/parser/RdfParser.cpp b/src/parser/RdfParser.cpp index 9cf4bffe93..c100a4b1dc 100644 --- a/src/parser/RdfParser.cpp +++ b/src/parser/RdfParser.cpp @@ -151,16 +151,13 @@ bool TurtleParser::objectList() { // ______________________________________________________ template bool TurtleParser::verb() { - return predicateSpecialA() || predicate(); + return predicate() || predicateSpecialA(); } // ___________________________________________________________________ template bool TurtleParser::predicateSpecialA() { - tok_.skipWhitespaceAndComments(); - if (auto [success, word] = tok_.template getNextToken(); - success) { - (void)word; + if (parseTerminal()) { activePredicate_ = TripleComponent::Iri::fromIriref( ""); return true; diff --git a/src/parser/RdfParser.h b/src/parser/RdfParser.h index d65c05934d..063cfc35e3 100644 --- a/src/parser/RdfParser.h +++ b/src/parser/RdfParser.h @@ -429,6 +429,7 @@ class TurtleParser : public RdfParserBase { FRIEND_TEST(RdfParserTest, booleanLiteralLongForm); FRIEND_TEST(RdfParserTest, collection); FRIEND_TEST(RdfParserTest, iriref); + FRIEND_TEST(RdfParserTest, specialPredicateA); }; template diff --git a/src/util/Algorithm.h b/src/util/Algorithm.h index 50e23f8244..105c33ed7b 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())); @@ -148,10 +150,11 @@ CPP_template(typename Range)(requires ql::ranges::forward_range< // Return a new `std::input` that is obtained by applying the `function` to each // of the elements of the `input`. -template -requires(ad_utility::isArray> && - std::invocable) -auto transformArray(Array&& input, Function function) { +CPP_template(typename Array, typename Function)( + requires ad_utility::isArray> CPP_and std::invocable< + Function, + typename Array::value_type>) auto transformArray(Array&& input, + Function function) { return std::apply( [&function](auto&&... vals) { return std::array{std::invoke(function, AD_FWD(vals))...}; @@ -163,10 +166,10 @@ auto transformArray(Array&& input, Function function) { // but an iterator (first argument) and a value (second argument). The // implementation is copied from libstdc++ which has this function as an // internal detail, but doesn't expose it to the outside. -template -constexpr ForwardIterator lower_bound_iterator(ForwardIterator first, - ForwardIterator last, - const Tp& val, Compare comp) { +CPP_template(typename ForwardIterator, typename Tp, typename Compare)( + requires std::forward_iterator) constexpr ForwardIterator + lower_bound_iterator(ForwardIterator first, ForwardIterator last, + const Tp& val, Compare comp) { using DistanceType = std::iterator_traits::difference_type; DistanceType len = std::distance(first, last); @@ -189,10 +192,10 @@ constexpr ForwardIterator lower_bound_iterator(ForwardIterator first, // but a value (first argument) and an iterator (second argument). The // implementation is copied from libstdc++ which has this function as an // internal detail, but doesn't expose it to the outside. -template -constexpr ForwardIterator upper_bound_iterator(ForwardIterator first, - ForwardIterator last, - const Tp& val, Compare comp) { +CPP_template(typename ForwardIterator, typename Tp, typename Compare)( + requires std::forward_iterator) constexpr ForwardIterator + upper_bound_iterator(ForwardIterator first, ForwardIterator last, + const Tp& val, Compare comp) { using DistanceType = std::iterator_traits::difference_type; DistanceType len = std::distance(first, last); diff --git a/src/util/AllocatorWithLimit.h b/src/util/AllocatorWithLimit.h index 1a52d0532f..8aa4e72f92 100644 --- a/src/util/AllocatorWithLimit.h +++ b/src/util/AllocatorWithLimit.h @@ -7,7 +7,6 @@ #include -#include #include #include @@ -174,9 +173,8 @@ class AllocatorWithLimit { } AllocatorWithLimit() = delete; - template - requires(!std::same_as) - AllocatorWithLimit(const AllocatorWithLimit& other) + CPP_template(typename U)(requires(!std::same_as)) + AllocatorWithLimit(const AllocatorWithLimit& other) : memoryLeft_{other.getMemoryLeft()}, clearOnAllocation_(other.clearOnAllocation()){}; diff --git a/src/util/AsioHelpers.h b/src/util/AsioHelpers.h index 6ae990a44d..31456c823e 100644 --- a/src/util/AsioHelpers.h +++ b/src/util/AsioHelpers.h @@ -26,8 +26,8 @@ namespace detail { // function is called directly, but the invocation of the handler is posted to // the associated executor of the `handler`, or to the `executor` if no such // associated executor exists. -template -struct CallFunctionAndPassToHandler { +CPP_template(typename Executor, typename Function, typename Handler)( + requires std::invocable) struct CallFunctionAndPassToHandler { Executor executor_; Function function_; Handler handler_; @@ -71,11 +71,12 @@ struct CallFunctionAndPassToHandler { }; // Explicit deduction guides, we need objects and not references as the template // parameters. -template -requires std::invocable> -CallFunctionAndPassToHandler(Executor&&, Function&&, Handler&&) - -> CallFunctionAndPassToHandler< - std::decay_t, std::decay_t, std::decay_t>; +CPP_template(typename Executor, typename Function, + typename Handler)(requires std::invocable>) + CallFunctionAndPassToHandler(Executor&&, Function&&, Handler&&) + -> CallFunctionAndPassToHandler, + std::decay_t, + std::decay_t>; } // namespace detail // Run the `function` on the `executor` (e.g. a strand for synchronization or @@ -85,11 +86,14 @@ CallFunctionAndPassToHandler(Executor&&, Function&&, Handler&&) // Note: If no executor is associated with the `completionToken`, then the // handler will also be run on the `executor` that is passed to this function as // there is no other way of running it. -template -requires std::is_default_constructible_v> || - std::is_void_v> -auto runFunctionOnExecutor(Executor executor, Function function, - CompletionToken& completionToken) { +CPP_template(typename Executor, typename CompletionToken, + typename Function)(requires std::invocable CPP_and( + std::is_default_constructible_v> || + std::is_void_v>)) auto runFunctionOnExecutor(Executor executor, + Function function, + CompletionToken& + completionToken) { using Value = std::invoke_result_t; static constexpr bool isVoid = std::is_void_v; diff --git a/src/util/Cache.h b/src/util/Cache.h index 23750eea16..e4da8e8dda 100644 --- a/src/util/Cache.h +++ b/src/util/Cache.h @@ -53,12 +53,12 @@ static constexpr auto size_t_max = std::numeric_limits::max(); @tparam ValueSizeGetter function Value -> MemorySize to determine the actual size of a value for statistics */ -template