aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java27
-rw-r--r--eval/src/vespa/eval/eval/aggr.h6
-rw-r--r--searchlib/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/btree/.gitignore1
-rw-r--r--searchlib/src/tests/btree/CMakeLists.txt8
-rw-r--r--searchlib/src/tests/btree/scanspeed.cpp181
-rw-r--r--vespalib/src/tests/btree/btree_test.cpp77
-rw-r--r--vespalib/src/tests/traits/traits_test.cpp10
-rw-r--r--vespalib/src/vespa/vespalib/btree/btreeiterator.h103
-rw-r--r--vespalib/src/vespa/vespalib/btree/btreenode.h33
-rw-r--r--vespalib/src/vespa/vespalib/util/traits.h6
-rw-r--r--vespalib/src/vespa/vespalib/util/typify.h52
13 files changed, 478 insertions, 33 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
index 0865b72dbbf..07a686a63bd 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
@@ -177,4 +177,10 @@ public class FileDirectory {
destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
}
}
+
+ @Override
+ public String toString() {
+ return "root dir: " + root.getAbsolutePath();
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index 9f89de3035a..1a99259c6ef 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -526,10 +526,10 @@ public class SessionRepository {
/**
* Returns a new session instance for the given session id.
*/
- LocalSession createLocalSessionUsingDistributedApplicationPackage(long sessionId) {
+ Optional<LocalSession> createLocalSessionUsingDistributedApplicationPackage(long sessionId) {
if (applicationRepo.hasLocalSession(sessionId)) {
log.log(Level.FINE, "Local session for session id " + sessionId + " already exists");
- return createSessionFromId(sessionId);
+ return Optional.of(createSessionFromId(sessionId));
}
log.log(Level.INFO, "Creating local session for session id " + sessionId);
@@ -538,16 +538,23 @@ public class SessionRepository {
log.log(Level.FINE, "File reference for session id " + sessionId + ": " + fileReference);
if (fileReference != null) {
File rootDir = new File(Defaults.getDefaults().underVespaHome(componentRegistry.getConfigserverConfig().fileReferencesDir()));
- File sessionDir = new FileDirectory(rootDir).getFile(fileReference);
- if (!sessionDir.exists())
- throw new RuntimeException("File reference for session " + sessionId + " not found (" + sessionDir.getAbsolutePath() + ")");
+ File sessionDir;
+ FileDirectory fileDirectory = new FileDirectory(rootDir);
+ try {
+ sessionDir = fileDirectory.getFile(fileReference);
+ } catch (IllegalArgumentException e) {
+ // We cannot be guaranteed that the file reference exists (it could be that it has not
+ // been downloaded yet), and e.g when bootstrapping we cannot throw an exception in that case
+ log.log(Level.INFO, "File reference for session id " + sessionId + ": " + fileReference + " not found in " + fileDirectory);
+ return Optional.empty();
+ }
ApplicationId applicationId = sessionZKClient.readApplicationId();
- return createLocalSession(sessionDir,
- applicationId,
- sessionId,
- applicationRepo.activeSessionOf(applicationId).orElse(nonExistingActiveSession));
+ return Optional.of(createLocalSession(sessionDir,
+ applicationId,
+ sessionId,
+ applicationRepo.activeSessionOf(applicationId).orElse(nonExistingActiveSession)));
}
- return null;
+ return Optional.empty();
}
// Return Optional instead of faking it with nonExistingActiveSession
diff --git a/eval/src/vespa/eval/eval/aggr.h b/eval/src/vespa/eval/eval/aggr.h
index e7431c2c23b..169f0b1d2af 100644
--- a/eval/src/vespa/eval/eval/aggr.h
+++ b/eval/src/vespa/eval/eval/aggr.h
@@ -2,6 +2,7 @@
#pragma once
+#include <vespa/vespalib/util/typify.h>
#include <vespa/vespalib/stllike/string.h>
#include <vector>
#include <map>
@@ -120,10 +121,7 @@ public:
} // namespave vespalib::eval::aggr
struct TypifyAggr {
- template <template<typename> typename A> struct Result {
- static constexpr bool is_type = false;
- template <typename T> using templ = A<T>;
- };
+ template <template<typename> typename TT> using Result = TypifyResultSimpleTemplate<TT>;
template <typename F> static decltype(auto) resolve(Aggr aggr, F &&f) {
switch (aggr) {
case Aggr::AVG: return f(Result<aggr::Avg>());
diff --git a/searchlib/CMakeLists.txt b/searchlib/CMakeLists.txt
index e9e2087e9d1..c5dd468e4fd 100644
--- a/searchlib/CMakeLists.txt
+++ b/searchlib/CMakeLists.txt
@@ -100,6 +100,7 @@ vespa_define_module(
src/tests/attribute/tensorattribute
src/tests/bitcompression/expgolomb
src/tests/bitvector
+ src/tests/btree
src/tests/bytecomplens
src/tests/common/bitvector
src/tests/common/location
diff --git a/searchlib/src/tests/btree/.gitignore b/searchlib/src/tests/btree/.gitignore
new file mode 100644
index 00000000000..ec4090e3658
--- /dev/null
+++ b/searchlib/src/tests/btree/.gitignore
@@ -0,0 +1 @@
+searchlib_scanspeed_app
diff --git a/searchlib/src/tests/btree/CMakeLists.txt b/searchlib/src/tests/btree/CMakeLists.txt
new file mode 100644
index 00000000000..ff396144c52
--- /dev/null
+++ b/searchlib/src/tests/btree/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchlib_scanspeed_app
+ SOURCES
+ scanspeed.cpp
+ DEPENDS
+ searchlib
+)
+vespa_add_test(NAME searchlib_scanspeed_app COMMAND vespalib_scanspeed_app BENCHMARK)
diff --git a/searchlib/src/tests/btree/scanspeed.cpp b/searchlib/src/tests/btree/scanspeed.cpp
new file mode 100644
index 00000000000..1474edd6b0b
--- /dev/null
+++ b/searchlib/src/tests/btree/scanspeed.cpp
@@ -0,0 +1,181 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/btree/btreeroot.h>
+#include <vespa/vespalib/btree/btreebuilder.h>
+#include <vespa/vespalib/btree/btreenodeallocator.h>
+#include <vespa/vespalib/btree/btree.h>
+#include <vespa/vespalib/btree/btreestore.h>
+#include <vespa/vespalib/btree/btreenodeallocator.hpp>
+#include <vespa/vespalib/btree/btreenode.hpp>
+#include <vespa/vespalib/btree/btreenodestore.hpp>
+#include <vespa/vespalib/btree/btreeiterator.hpp>
+#include <vespa/vespalib/btree/btreeroot.hpp>
+#include <vespa/vespalib/btree/btreebuilder.hpp>
+#include <vespa/vespalib/btree/btree.hpp>
+#include <vespa/vespalib/btree/btreestore.hpp>
+#include <vespa/vespalib/util/time.h>
+#include <vespa/searchlib/common/bitvector.h>
+
+#include <vespa/fastos/app.h>
+
+using vespalib::btree::BTree;
+using vespalib::btree::BTreeNode;
+using vespalib::btree::BTreeTraits;
+
+enum class ScanMethod
+{
+ ITERATOR,
+ FUNCTOR
+};
+
+class ScanSpeed : public FastOS_Application
+{
+ template <typename Traits>
+ void work_loop(ScanMethod scan_method);
+ int Main() override;
+};
+
+
+namespace {
+
+const char *scan_method_name(ScanMethod scan_method)
+{
+ switch (scan_method) {
+ case ScanMethod::ITERATOR:
+ return "iterator";
+ default:
+ return "functor";
+ }
+}
+
+class ScanOnce {
+public:
+ virtual ~ScanOnce() = default;
+ virtual void operator()(search::BitVector &bv) = 0;
+};
+
+template <typename Tree>
+class ScanTree : public ScanOnce {
+protected:
+ const Tree &_tree;
+ int _startval;
+ int _endval;
+public:
+ ScanTree(const Tree &tree, int startval, int endval)
+ : _tree(tree),
+ _startval(startval),
+ _endval(endval)
+ {
+ }
+ ~ScanTree() override { }
+};
+
+template <typename Tree>
+class ScanWithIterator : public ScanTree<Tree> {
+public:
+ ScanWithIterator(const Tree &tree, int startval, int endval)
+ : ScanTree<Tree>(tree, startval, endval)
+ {
+ }
+ ~ScanWithIterator() override = default;
+ void operator()(search::BitVector &bv) override;
+};
+
+template <typename Tree>
+void
+ScanWithIterator<Tree>::operator()(search::BitVector &bv)
+{
+ using ConstIterator = typename Tree::ConstIterator;
+ ConstIterator itr(BTreeNode::Ref(), this->_tree.getAllocator());
+ itr.lower_bound(this->_tree.getRoot(), this->_startval);
+ while (itr.valid() && itr.getKey() < this->_endval) {
+ bv.setBit(itr.getKey());
+ ++itr;
+ }
+}
+
+template <typename Tree>
+class ScanWithFunctor : public ScanTree<Tree> {
+
+public:
+ ScanWithFunctor(const Tree &tree, int startval, int endval)
+ : ScanTree<Tree>(tree, startval, endval)
+ {
+ }
+ ~ScanWithFunctor() override = default;
+ void operator()(search::BitVector &bv) override;
+};
+
+template <typename Tree>
+void
+ScanWithFunctor<Tree>::operator()(search::BitVector &bv)
+{
+ using ConstIterator = typename Tree::ConstIterator;
+ ConstIterator start(BTreeNode::Ref(), this->_tree.getAllocator());
+ ConstIterator end(BTreeNode::Ref(), this->_tree.getAllocator());
+ start.lower_bound(this->_tree.getRoot(), this->_startval);
+ end.lower_bound(this->_tree.getRoot(), this->_endval);
+ start.foreach_key_range(end, [&](int key) { bv.setBit(key); } );
+}
+
+}
+
+template <typename Traits>
+void
+ScanSpeed::work_loop(ScanMethod scan_method)
+{
+ vespalib::GenerationHandler g;
+ using Tree = BTree<int, int, vespalib::btree::NoAggregated, std::less<int>, Traits>;
+ using Builder = typename Tree::Builder;
+ Tree tree;
+ Builder builder(tree.getAllocator());
+ size_t numEntries = 1000000;
+ size_t numInnerLoops = 1000;
+ for (size_t i = 0; i < numEntries; ++i) {
+ builder.insert(i, 0);
+ }
+ tree.assign(builder);
+ assert(numEntries == tree.size());
+ assert(tree.isValid());
+ std::unique_ptr<ScanOnce> scan_once;
+ if (scan_method == ScanMethod::ITERATOR) {
+ scan_once = std::make_unique<ScanWithIterator<Tree>>(tree, 4, numEntries - 4);
+ } else {
+ scan_once = std::make_unique<ScanWithFunctor<Tree>>(tree, 4, numEntries - 4);
+ }
+ auto bv = search::BitVector::create(numEntries);
+ vespalib::Timer timer;
+ for (size_t innerl = 0; innerl < numInnerLoops; ++innerl) {
+ (*scan_once)(*bv);
+ }
+ double used = vespalib::to_s(timer.elapsed());
+ printf("Elapsed time for scanning %ld entries is %8.5f, "
+ "scanmethod=%s, fanout=%u,%u\n",
+ numEntries * numInnerLoops,
+ used,
+ scan_method_name(scan_method),
+ static_cast<int>(Traits::LEAF_SLOTS),
+ static_cast<int>(Traits::INTERNAL_SLOTS));
+ fflush(stdout);
+}
+
+
+int
+ScanSpeed::Main()
+{
+ using SmallTraits = BTreeTraits<4, 4, 31, false>;
+ using DefTraits = vespalib::btree::BTreeDefaultTraits;
+ using LargeTraits = BTreeTraits<32, 16, 10, true>;
+ using HugeTraits = BTreeTraits<64, 16, 10, true>;
+ work_loop<SmallTraits>(ScanMethod::ITERATOR);
+ work_loop<DefTraits>(ScanMethod::ITERATOR);
+ work_loop<LargeTraits>(ScanMethod::ITERATOR);
+ work_loop<HugeTraits>(ScanMethod::ITERATOR);
+ work_loop<SmallTraits>(ScanMethod::FUNCTOR);
+ work_loop<DefTraits>(ScanMethod::FUNCTOR);
+ work_loop<LargeTraits>(ScanMethod::FUNCTOR);
+ work_loop<HugeTraits>(ScanMethod::FUNCTOR);
+ return 0;
+}
+
+FASTOS_MAIN(ScanSpeed);
diff --git a/vespalib/src/tests/btree/btree_test.cpp b/vespalib/src/tests/btree/btree_test.cpp
index 848c8a37125..63afd8b770f 100644
--- a/vespalib/src/tests/btree/btree_test.cpp
+++ b/vespalib/src/tests/btree/btree_test.cpp
@@ -36,6 +36,54 @@ toStr(const T & v)
return ss.str();
}
+class SequenceValidator
+{
+ int _wanted_count;
+ int _prev_key;
+ int _count;
+ bool _failed;
+
+public:
+ SequenceValidator(int start, int wanted_count)
+ : _wanted_count(wanted_count),
+ _prev_key(start - 1),
+ _count(0),
+ _failed(false)
+ {
+ }
+
+ bool failed() const {
+ return _failed || _wanted_count != _count;
+ }
+
+ void operator()(int key) {
+ if (key != _prev_key + 1) {
+ _failed = true;
+ }
+ _prev_key = key;
+ ++_count;
+ }
+};
+
+class ForeachKeyValidator
+{
+ SequenceValidator & _validator;
+public:
+ ForeachKeyValidator(SequenceValidator &validator)
+ : _validator(validator)
+ {
+ }
+ void operator()(int key) {
+ _validator(key);
+ }
+};
+
+template <typename Iterator>
+void validate_subrange(Iterator &start, Iterator &end, SequenceValidator &validator) {
+ start.foreach_key_range(end, ForeachKeyValidator(validator));
+ EXPECT_FALSE(validator.failed());
+}
+
}
typedef BTreeTraits<4, 4, 31, false> MyTraits;
@@ -210,6 +258,8 @@ private:
void
requireThatIteratorDistanceWorks();
+
+ void requireThatForeachKeyWorks();
public:
int Main() override;
};
@@ -1489,6 +1539,32 @@ Test::requireThatIteratorDistanceWorks()
requireThatIteratorDistanceWorks(400);
}
+void
+Test::requireThatForeachKeyWorks()
+{
+ using Tree = BTree<int, int, btree::NoAggregated, MyComp, MyTraits>;
+ using Iterator = typename Tree::ConstIterator;
+ Tree t;
+ populateTree(t, 256, 1);
+
+ {
+ // Whole range
+ SequenceValidator validator(1, 256);
+ t.foreach_key(ForeachKeyValidator(validator));
+ EXPECT_FALSE(validator.failed());
+ }
+ {
+ // Subranges
+ for (int startval = 1; startval < 259; ++startval) {
+ for (int endval = 1; endval < 259; ++endval) {
+ SequenceValidator validator(startval, std::max(0, std::min(endval,257) - std::min(startval, 257)));
+ Iterator start = t.lowerBound(startval);
+ Iterator end = t.lowerBound(endval);
+ validate_subrange(start, end, validator);
+ }
+ }
+ }
+};
int
Test::Main()
@@ -1515,6 +1591,7 @@ Test::Main()
requireThatSmallNodesWorks();
requireThatApplyWorks();
requireThatIteratorDistanceWorks();
+ requireThatForeachKeyWorks();
TEST_DONE();
}
diff --git a/vespalib/src/tests/traits/traits_test.cpp b/vespalib/src/tests/traits/traits_test.cpp
index 0a29721df1d..7751327df75 100644
--- a/vespalib/src/tests/traits/traits_test.cpp
+++ b/vespalib/src/tests/traits/traits_test.cpp
@@ -42,4 +42,14 @@ TEST("require that can_skip_destruction works") {
EXPECT_EQUAL(can_skip_destruction<Child2>::value, true);
}
+struct NoType {};
+struct TypeType { using type = NoType; };
+struct NoTypeType { static constexpr int type = 3; };
+
+TEST("require that type type member can be detected") {
+ EXPECT_FALSE(has_type_type_v<NoType>);
+ EXPECT_TRUE(has_type_type_v<TypeType>);
+ EXPECT_FALSE(has_type_type_v<NoTypeType>);
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/btree/btreeiterator.h b/vespalib/src/vespa/vespalib/btree/btreeiterator.h
index 55ab37759ad..6933fc1c2d0 100644
--- a/vespalib/src/vespa/vespalib/btree/btreeiterator.h
+++ b/vespalib/src/vespa/vespalib/btree/btreeiterator.h
@@ -303,6 +303,47 @@ protected:
* @param pathSize New tree height (number of levels of internal nodes)
*/
VESPA_DLL_LOCAL void clearPath(uint32_t pathSize);
+
+ /**
+ * Call func with leaf entry key value as argument for all leaf entries in subtree
+ * from this iterator position to end of subtree.
+ */
+ template <typename FunctionType>
+ void
+ foreach_key_range_start(uint32_t level, FunctionType func) const
+ {
+ if (level > 0u) {
+ --level;
+ foreach_key_range_start(level, func);
+ auto &store = _allocator->getNodeStore();
+ auto node = _path[level].getNode();
+ uint32_t idx = _path[level].getIdx();
+ node->foreach_key_range(store, idx + 1, node->validSlots(), func);
+ } else {
+ _leaf.getNode()->foreach_key_range(_leaf.getIdx(), _leaf.getNode()->validSlots(), func);
+ }
+ }
+
+ /**
+ * Call func with leaf entry key value as argument for all leaf entries in subtree
+ * from start of subtree until this iterator position is reached (i.e. entries in
+ * subtree before this iterator position).
+ */
+ template <typename FunctionType>
+ void
+ foreach_key_range_end(uint32_t level, FunctionType func) const
+ {
+ if (level > 0u) {
+ --level;
+ auto &store = _allocator->getNodeStore();
+ auto node = _path[level].getNode();
+ uint32_t eidx = _path[level].getIdx();
+ node->foreach_key_range(store, 0, eidx, func);
+ foreach_key_range_end(level, func);
+ } else {
+ _leaf.getNode()->foreach_key_range(0, _leaf.getIdx(), func);
+ }
+ }
public:
bool
@@ -451,6 +492,68 @@ public:
_leafRoot->foreach_key(func);
}
}
+
+ /**
+ * Call func with leaf entry key value as argument for all leaf entries in tree from
+ * this iterator position until end_itr position is reached (i.e. entries in
+ * range [this iterator, end_itr)).
+ */
+ template <typename FunctionType>
+ void
+ foreach_key_range(const BTreeIteratorBase &end_itr, FunctionType func) const
+ {
+ if (!valid()) {
+ return;
+ }
+ if (!end_itr.valid()) {
+ foreach_key_range_start(_pathSize, func);
+ return;
+ }
+ assert(_pathSize == end_itr._pathSize);
+ assert(_allocator == end_itr._allocator);
+ uint32_t level = _pathSize;
+ if (level > 0u) {
+ /**
+ * Tree has intermediate nodes. Detect lowest shared tree node for this
+ * iterator and end_itr.
+ */
+ uint32_t idx;
+ uint32_t eidx;
+ do {
+ --level;
+ assert(_path[level].getNode() == end_itr._path[level].getNode());
+ idx = _path[level].getIdx();
+ eidx = end_itr._path[level].getIdx();
+ if (idx > eidx) {
+ return;
+ }
+ if (idx != eidx) {
+ ++level;
+ break;
+ }
+ } while (level != 0);
+ if (level > 0u) {
+ // Lowest shared node is an intermediate node.
+ // Left subtree for child [idx], from this iterator position to end of subtree.
+ foreach_key_range_start(level - 1, func);
+ auto &store = _allocator->getNodeStore();
+ auto node = _path[level - 1].getNode();
+ // Any intermediate subtrees for children [idx + 1, eidx).
+ node->foreach_key_range(store, idx + 1, eidx, func);
+ // Right subtree for child [eidx], from start of subtree to end_itr position.
+ end_itr.foreach_key_range_end(level - 1, func);
+ return;
+ } else {
+ // Lowest shared node is a leaf node.
+ assert(_leaf.getNode() == end_itr._leaf.getNode());
+ }
+ }
+ uint32_t idx = _leaf.getIdx();
+ uint32_t eidx = end_itr._leaf.getIdx();
+ if (idx < eidx) {
+ _leaf.getNode()->foreach_key_range(idx, eidx, func);
+ }
+ }
};
diff --git a/vespalib/src/vespa/vespalib/btree/btreenode.h b/vespalib/src/vespa/vespalib/btree/btreenode.h
index b34be33ccf5..0c70e70bc6a 100644
--- a/vespalib/src/vespa/vespalib/btree/btreenode.h
+++ b/vespalib/src/vespa/vespalib/btree/btreenode.h
@@ -370,6 +370,26 @@ public:
}
}
+ /**
+ * Call func with leaf entry key value as argument for all leaf entries in subtrees
+ * for children [start_idx, end_idx).
+ */
+ template <typename NodeStoreType, typename FunctionType>
+ void foreach_key_range(NodeStoreType &store, uint32_t start_idx, uint32_t end_idx, FunctionType func) const {
+ const BTreeNode::Ref *it = this->_data;
+ const BTreeNode::Ref *ite = it + end_idx;
+ it += start_idx;
+ if (this->getLevel() > 1u) {
+ for (; it != ite; ++it) {
+ store.mapInternalRef(*it)->foreach_key(store, func);
+ }
+ } else {
+ for (; it != ite; ++it) {
+ store.mapLeafRef(*it)->foreach_key(func);
+ }
+ }
+ }
+
template <typename NodeStoreType, typename FunctionType>
void foreach(NodeStoreType &store, FunctionType func) const {
const BTreeNode::Ref *it = this->_data;
@@ -459,6 +479,19 @@ public:
}
}
+ /**
+ * Call func with leaf entry key value as argument for leaf entries [start_idx, end_idx).
+ */
+ template <typename FunctionType>
+ void foreach_key_range(uint32_t start_idx, uint32_t end_idx, FunctionType func) const {
+ const KeyT *it = _keys;
+ const KeyT *ite = it + end_idx;
+ it += start_idx;
+ for (; it != ite; ++it) {
+ func(*it);
+ }
+ }
+
template <typename FunctionType>
void foreach(FunctionType func) const {
const KeyT *it = _keys;
diff --git a/vespalib/src/vespa/vespalib/util/traits.h b/vespalib/src/vespa/vespalib/util/traits.h
index eb0385abc72..7f8945954a8 100644
--- a/vespalib/src/vespa/vespalib/util/traits.h
+++ b/vespalib/src/vespa/vespalib/util/traits.h
@@ -39,4 +39,10 @@ struct can_skip_destruction : std::is_trivially_destructible<T> {};
//-----------------------------------------------------------------------------
+template <typename, typename = std::void_t<>> struct has_type_type : std::false_type {};
+template <typename T> struct has_type_type<T, std::void_t<typename T::type>> : std::true_type {};
+template <typename T> constexpr bool has_type_type_v = has_type_type<T>::value;
+
+//-----------------------------------------------------------------------------
+
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/util/typify.h b/vespalib/src/vespa/vespalib/util/typify.h
index a2a24baca41..0d84d1756a5 100644
--- a/vespalib/src/vespa/vespalib/util/typify.h
+++ b/vespalib/src/vespa/vespalib/util/typify.h
@@ -2,6 +2,7 @@
#pragma once
+#include "traits.h"
#include <stddef.h>
#include <utility>
@@ -10,27 +11,40 @@ namespace vespalib {
//-----------------------------------------------------------------------------
/**
- * Typification result for values resolving into actual types.
+ * Typification result for values resolving into actual types. Using
+ * this exact template is not required, but the result type must have
+ * the name 'type' for the auto-unwrapping performed by typify_invoke
+ * to work.
**/
template <typename T> struct TypifyResultType {
- static constexpr bool is_type = true;
using type = T;
};
/**
- * Typification result for values resolving into compile-time values
- * which are also types as long as they are kept inside their result
- * wrappers.
+ * Typification result for values resolving into non-types. Using this
+ * exact template is not required, but is supplied for
+ * convenience. The resolved compile-time value should be called
+ * 'value' for consistency across typifiers.
**/
template <typename T, T VALUE> struct TypifyResultValue {
- static constexpr bool is_type = false;
static constexpr T value = VALUE;
};
/**
+ * Typification result for values resolving into simple templates
+ * (templated on one type). Using this exact template is not required,
+ * but is supplied for convenience and as example. The resolved
+ * template should be called 'templ' for consistency across typifiers.
+ **/
+template <template<typename> typename TT> struct TypifyResultSimpleTemplate {
+ template <typename T> using templ = TT<T>;
+};
+
+/**
* A Typifier is able to take a run-time value and resolve it into a
- * type. The resolve result is passed to the specified function in the
- * form of a thin result wrapper.
+ * type, non-type constant value or a template. The resolve result is
+ * passed to the specified function in the form of a thin result
+ * wrapper.
**/
struct TypifyBool {
template <bool VALUE> using Result = TypifyResultValue<bool, VALUE>;
@@ -63,14 +77,14 @@ template <size_t N, typename Typifier, typename Target, typename ...Rs> struct T
return Target::template invoke<Rs...>(std::forward<T>(value), std::forward<Args>(args)...);
} else {
return Typifier::resolve(value, [&](auto t)->decltype(auto)
- {
- using X = decltype(t);
- if constexpr (X::is_type) {
- return TypifyInvokeImpl<N, Typifier, Target, Rs..., typename X::type>::select(std::forward<Args>(args)...);
- } else {
- return TypifyInvokeImpl<N, Typifier, Target, Rs..., X>::select(std::forward<Args>(args)...);
- }
- });
+ {
+ using X = decltype(t);
+ if constexpr (has_type_type_v<X>) {
+ return TypifyInvokeImpl<N, Typifier, Target, Rs..., typename X::type>::select(std::forward<Args>(args)...);
+ } else {
+ return TypifyInvokeImpl<N, Typifier, Target, Rs..., X>::select(std::forward<Args>(args)...);
+ }
+ });
}
}
};
@@ -82,9 +96,9 @@ template <size_t N, typename Typifier, typename Target, typename ...Rs> struct T
* the typification results from the N first parameters as template
* parameters. Note that typification results that are types are
* unwrapped before being used as template parameters while
- * typification results that are compile-time values are kept in their
- * wrappers when passed as template parameters. Please refer to the
- * unit test for examples.
+ * typification results that are non-types or templates are kept in
+ * their wrappers when passed as template parameters. Please refer to
+ * the unit test for examples.
**/
template <size_t N, typename Typifier, typename Target, typename ...Args> decltype(auto) typify_invoke(Args && ...args) {
static_assert(N > 0);