Remove unneccessary files from utils
Reviewers: buda Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D276
This commit is contained in:
parent
59a9aaea16
commit
bba20cf89c
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "utils/underlying_cast.hpp"
|
|
||||||
|
|
||||||
namespace communication::bolt {
|
namespace communication::bolt {
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "logging/logger.hpp"
|
#include "logging/logger.hpp"
|
||||||
#include "query/typed_value.hpp"
|
#include "query/typed_value.hpp"
|
||||||
#include "utils/bswap.hpp"
|
#include "utils/bswap.hpp"
|
||||||
|
#include "utils/underlying_cast.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -56,9 +57,7 @@ class BaseEncoder : public Loggable {
|
|||||||
WriteRAW(reinterpret_cast<const uint8_t *>(&value), sizeof(value));
|
WriteRAW(reinterpret_cast<const uint8_t *>(&value), sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteNull() {
|
void WriteNull() { WriteRAW(underlying_cast(Marker::Null)); }
|
||||||
WriteRAW(underlying_cast(Marker::Null));
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteBool(const bool &value) {
|
void WriteBool(const bool &value) {
|
||||||
if (value)
|
if (value)
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include "storage/unique_object_store.hpp"
|
#include "storage/unique_object_store.hpp"
|
||||||
#include "storage/vertex.hpp"
|
#include "storage/vertex.hpp"
|
||||||
#include "transactions/engine.hpp"
|
#include "transactions/engine.hpp"
|
||||||
#include "utils/pass_key.hpp"
|
|
||||||
#include "utils/scheduler.hpp"
|
#include "utils/scheduler.hpp"
|
||||||
|
|
||||||
// TODO: Maybe split this in another layer between Db and Dbms. Where the new
|
// TODO: Maybe split this in another layer between Db and Dbms. Where the new
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "io/network/addrinfo.hpp"
|
#include "io/network/addrinfo.hpp"
|
||||||
|
|
||||||
#include "io/network/network_error.hpp"
|
#include "io/network/network_error.hpp"
|
||||||
#include "utils/underlying_cast.hpp"
|
|
||||||
|
|
||||||
namespace io::network {
|
namespace io::network {
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
#include "traversal/path.hpp"
|
#include "traversal/path.hpp"
|
||||||
#include "utils/exceptions/basic_exception.hpp"
|
#include "utils/exceptions/basic_exception.hpp"
|
||||||
#include "utils/total_ordering.hpp"
|
#include "utils/total_ordering.hpp"
|
||||||
#include "utils/underlying_cast.hpp"
|
|
||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "storage/edge.hpp"
|
#include "storage/edge.hpp"
|
||||||
#include "storage/record_accessor.hpp"
|
#include "storage/record_accessor.hpp"
|
||||||
#include "utils/assert.hpp"
|
#include "utils/assert.hpp"
|
||||||
#include "utils/reference_wrapper.hpp"
|
|
||||||
|
|
||||||
// forward declaring the VertexAccessor because it's returned
|
// forward declaring the VertexAccessor because it's returned
|
||||||
// by some functions
|
// by some functions
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||||
#include "utils/total_ordering.hpp"
|
#include "utils/total_ordering.hpp"
|
||||||
#include "utils/underlying_cast.hpp"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulation of a value and it's type encapsulated in a class that has no
|
* Encapsulation of a value and it's type encapsulated in a class that has no
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "storage/record_accessor.hpp"
|
|
||||||
#include "database/graph_db_accessor.hpp"
|
#include "database/graph_db_accessor.hpp"
|
||||||
#include "storage/edge.hpp"
|
#include "storage/edge.hpp"
|
||||||
|
#include "storage/record_accessor.hpp"
|
||||||
#include "storage/vertex.hpp"
|
#include "storage/vertex.hpp"
|
||||||
#include "utils/assert.hpp"
|
#include "utils/assert.hpp"
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ GraphDbAccessor &RecordAccessor<TRecord>::db_accessor() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
const uint64_t RecordAccessor<TRecord>::temporary_id() const {
|
uint64_t RecordAccessor<TRecord>::temporary_id() const {
|
||||||
return (uint64_t)vlist_;
|
return (uint64_t)vlist_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
//#include "database/graph_db_accessor.hpp"
|
//#include "database/graph_db_accessor.hpp"
|
||||||
#include "mvcc/version_list.hpp"
|
#include "mvcc/version_list.hpp"
|
||||||
#include "storage/property_value.hpp"
|
#include "storage/property_value.hpp"
|
||||||
#include "utils/pass_key.hpp"
|
|
||||||
|
|
||||||
#include "storage/property_value_store.hpp"
|
#include "storage/property_value_store.hpp"
|
||||||
|
|
||||||
@ -134,7 +133,7 @@ class RecordAccessor {
|
|||||||
*
|
*
|
||||||
* @return See above.
|
* @return See above.
|
||||||
*/
|
*/
|
||||||
const uint64_t temporary_id() const;
|
uint64_t temporary_id() const;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switches this record accessor to use the latest
|
* Switches this record accessor to use the latest
|
||||||
|
@ -4,9 +4,19 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include <linux/futex.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "lock_timeout_error.hpp"
|
#include "lock_timeout_error.hpp"
|
||||||
#include "utils/cpu_relax.hpp"
|
#include "utils/cpu_relax.hpp"
|
||||||
#include "utils/sys.hpp"
|
|
||||||
|
namespace sys {
|
||||||
|
inline int futex(void *addr1, int op, int val1, const struct timespec *timeout,
|
||||||
|
void *addr2, int val3) {
|
||||||
|
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class Futex {
|
class Futex {
|
||||||
using futex_t = uint32_t;
|
using futex_t = uint32_t;
|
||||||
@ -61,7 +71,7 @@ class Futex {
|
|||||||
UNLOCKED;
|
UNLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock(const struct timespec* timeout = nullptr) {
|
void lock(const struct timespec *timeout = nullptr) {
|
||||||
// try to fast lock a few times before going to sleep
|
// try to fast lock a few times before going to sleep
|
||||||
for (size_t i = 0; i < LOCK_RETRIES; ++i) {
|
for (size_t i = 0; i < LOCK_RETRIES; ++i) {
|
||||||
// try to lock and exit if we succeed
|
// try to lock and exit if we succeed
|
||||||
@ -125,7 +135,7 @@ class Futex {
|
|||||||
private:
|
private:
|
||||||
mutex_t mutex;
|
mutex_t mutex;
|
||||||
|
|
||||||
int futex_wait(int value, const struct timespec* timeout = nullptr) {
|
int futex_wait(int value, const struct timespec *timeout = nullptr) {
|
||||||
return sys::futex(&mutex.all, FUTEX_WAIT_PRIVATE, value, timeout, nullptr,
|
return sys::futex(&mutex.all, FUTEX_WAIT_PRIVATE, value, timeout, nullptr,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "utils/underlying_cast.hpp"
|
|
||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
static std::atomic<unsigned> thread_counter;
|
static std::atomic<unsigned> thread_counter;
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// FROM: A Malloc Tutorial, Marwan Burelle, 2009
|
|
||||||
|
|
||||||
// align address x to 4 bytes
|
|
||||||
#define align_4(x) (((((x)-1) >> 2) << 2) + 4)
|
|
||||||
|
|
||||||
// align address x to 8 bytes
|
|
||||||
#define align_8(x) (((((x)-1) >> 3) << 3) + 8)
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace bash_color {
|
|
||||||
auto blue = "\033[94m";
|
|
||||||
auto green = "\033[92m";
|
|
||||||
auto yellow = "\033[93m";
|
|
||||||
auto red = "\033[91m";
|
|
||||||
auto end = "\033[0m";
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "utils/numerics/ceil.hpp"
|
|
||||||
|
|
||||||
class Buffer {
|
|
||||||
public:
|
|
||||||
Buffer(size_t capacity, size_t chunk_size)
|
|
||||||
: capacity(capacity), chunk_size(chunk_size) {}
|
|
||||||
|
|
||||||
Buffer& append(const std::string& string) {
|
|
||||||
return this->append(string.c_str(), string.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer& append(const char* string, size_t n) {
|
|
||||||
auto new_size = size() + n;
|
|
||||||
|
|
||||||
if (capacity < new_size) {
|
|
||||||
capacity = new_size;
|
|
||||||
data = static_cast<char*>(realloc(data, new_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
size = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer& operator<<(const std::string& string) {}
|
|
||||||
|
|
||||||
size_t size() const { return str.size(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t size_, capacity, chunk_size;
|
|
||||||
char* data;
|
|
||||||
};
|
|
@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include "utils/total_ordering.hpp"
|
|
||||||
|
|
||||||
class CharStr : public TotalOrdering<CharStr> {
|
|
||||||
public:
|
|
||||||
CharStr(const char *str) : str(str) {}
|
|
||||||
|
|
||||||
std::string to_string() const { return std::string(str); }
|
|
||||||
|
|
||||||
friend bool operator==(const CharStr &lhs, const CharStr &rhs) {
|
|
||||||
return strcmp(lhs.str, rhs.str) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator<(const CharStr &lhs, const CharStr &rhs) {
|
|
||||||
return strcmp(lhs.str, rhs.str) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const char *str;
|
|
||||||
};
|
|
@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class EqWrapper {
|
|
||||||
public:
|
|
||||||
EqWrapper(T t) : t(t) {}
|
|
||||||
|
|
||||||
friend bool operator==(const EqWrapper &a, const EqWrapper &b) {
|
|
||||||
return a.t == b.t;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator!=(const EqWrapper &a, const EqWrapper &b) {
|
|
||||||
return !(a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
T t;
|
|
||||||
};
|
|
@ -8,9 +8,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -23,11 +25,26 @@ namespace fs = std::experimental::filesystem;
|
|||||||
#include "logging/loggable.hpp"
|
#include "logging/loggable.hpp"
|
||||||
#include "utils/algorithm.hpp"
|
#include "utils/algorithm.hpp"
|
||||||
#include "utils/assert.hpp"
|
#include "utils/assert.hpp"
|
||||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
#include "utils/exceptions/basic_exception.hpp"
|
||||||
#include "utils/linux.hpp"
|
#include "utils/likely.hpp"
|
||||||
#include "utils/underlying_cast.hpp"
|
#include "utils/underlying_cast.hpp"
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
namespace linux_os {
|
||||||
|
void set_non_blocking(int fd) {
|
||||||
|
auto flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
|
||||||
|
if (UNLIKELY(flags == -1))
|
||||||
|
throw BasicException("Cannot read flags from file descriptor.");
|
||||||
|
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
|
||||||
|
auto status = fcntl(fd, F_SETFL, flags);
|
||||||
|
|
||||||
|
if (UNLIKELY(status == -1))
|
||||||
|
throw BasicException("Can't set NON_BLOCK flag to file descriptor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using ms = std::chrono::milliseconds;
|
using ms = std::chrono::milliseconds;
|
||||||
|
|
||||||
@ -112,7 +129,7 @@ struct WatchDescriptor : public FSEventBase {
|
|||||||
WatchDescriptor(const fs::path &directory, const FSEventType type)
|
WatchDescriptor(const fs::path &directory, const FSEventType type)
|
||||||
: FSEventBase(directory, type) {
|
: FSEventBase(directory, type) {
|
||||||
debug_assert(fs::is_directory(path),
|
debug_assert(fs::is_directory(path),
|
||||||
"The path parameter should be directory");
|
"The path parameter should be directory");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Source object is going to be traversed by Writer and Writer will
|
|
||||||
* write that data into the Destination object.
|
|
||||||
*
|
|
||||||
* Writer object defines write format.
|
|
||||||
*/
|
|
||||||
template <typename Destination, typename Writer, typename Source>
|
|
||||||
Destination handle_write(const Source& source) {
|
|
||||||
Destination destination;
|
|
||||||
Writer writter(destination);
|
|
||||||
source.handle(writter);
|
|
||||||
return destination;
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/range_iterator.hpp"
|
|
||||||
#include "utils/option.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
// Class which turns ranged iterator with next() into accessor.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
template <class T, class I>
|
|
||||||
class OneTimeAccessor {
|
|
||||||
public:
|
|
||||||
OneTimeAccessor() : it(Option<RangeIterator<T, I>>()) {}
|
|
||||||
OneTimeAccessor(I &&it) : it(RangeIterator<T, I>(std::move(it))) {}
|
|
||||||
|
|
||||||
RangeIterator<T, I> begin() { return it.take(); }
|
|
||||||
|
|
||||||
RangeIterator<T, I> end() { return RangeIterator<T, I>(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Option<RangeIterator<T, I>> it;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I>
|
|
||||||
auto make_one_time_accessor(I &&iter) {
|
|
||||||
// Because function isn't receving or in any way using type T from
|
|
||||||
// OneTimeAccessor compiler can't deduce it thats way there is decltype in
|
|
||||||
// construction of OneTimeAccessor. Resoulting type of iter.next().take() is
|
|
||||||
// T.
|
|
||||||
return OneTimeAccessor<decltype(iter.next().take()), I>(std::move(iter));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which Combined two iterators IT1 and IT2.
|
|
||||||
// Both return values T
|
|
||||||
// T - type of return value
|
|
||||||
// IT1 - first iterator type
|
|
||||||
// IT2 - second iterator type
|
|
||||||
template <class T, class IT1, class IT2>
|
|
||||||
class Combined : public IteratorBase<T>,
|
|
||||||
public Composable<T, Combined<T, IT1, IT2>> {
|
|
||||||
public:
|
|
||||||
Combined() = delete;
|
|
||||||
|
|
||||||
// Combined operation is designed to be used in chained calls which operate
|
|
||||||
// on a iterator. Combined will in that usecase receive other iterator by
|
|
||||||
// value and std::move is a optimization for it.
|
|
||||||
Combined(IT1 &&iter1, IT2 &&iter2)
|
|
||||||
: iter1(Option<IT1>(std::move(iter1))),
|
|
||||||
iter2(Option<IT2>(std::move(iter2))) {}
|
|
||||||
|
|
||||||
// Return values first from first iterator then from second.
|
|
||||||
Option<T> next() final {
|
|
||||||
if (iter1.is_present()) {
|
|
||||||
auto ret = iter1.get().next();
|
|
||||||
if (ret.is_present()) {
|
|
||||||
return std::move(ret);
|
|
||||||
} else {
|
|
||||||
iter1.take();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iter2.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final {
|
|
||||||
return iter1.map_or([](auto &it) { return it.count(); }, 0) + iter2.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Option<IT1> iter1;
|
|
||||||
IT2 iter2;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IT1, class IT2>
|
|
||||||
auto make_combined(IT1 &&iter1, IT2 &&iter2) {
|
|
||||||
// Compiler cant deduce type T. decltype is here to help with it.
|
|
||||||
return Combined<decltype(iter1.next().take()), IT1, IT2>(std::move(iter1),
|
|
||||||
std::move(iter2));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/crtp.hpp"
|
|
||||||
#include "utils/iterator/count.hpp"
|
|
||||||
#include "utils/option.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_map(I &&iter, OP &&op);
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_filter(I &&iter, OP &&op);
|
|
||||||
|
|
||||||
template <class I, class C>
|
|
||||||
void for_all(I &&iter, C &&consumer);
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_flat_map(I &&iter, OP &&op);
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_inspect(I &&iter, OP &&op);
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_limited_map(I &&iter, OP &&op);
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_virtual(I &&iter);
|
|
||||||
|
|
||||||
template <class IT1, class IT2>
|
|
||||||
auto make_combined(IT1 &&iter1, IT2 &&iter2);
|
|
||||||
|
|
||||||
// Class for creating easy composable iterators fo querying.
|
|
||||||
//
|
|
||||||
// Derived - type of derived class
|
|
||||||
// T - return type
|
|
||||||
template <class T, class Derived>
|
|
||||||
class Composable : public Crtp<Derived> {
|
|
||||||
// Moves self
|
|
||||||
Derived &&move() { return std::move(this->derived()); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
auto virtualize() { return iter::make_virtual(move()); }
|
|
||||||
|
|
||||||
template <class IT>
|
|
||||||
auto combine(IT &&it) {
|
|
||||||
return iter::make_combined<Derived, IT>(move(), std::move(it));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class OP>
|
|
||||||
auto map(OP &&op) {
|
|
||||||
return iter::make_map<Derived, OP>(move(), std::move(op));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class OP>
|
|
||||||
auto filter(OP &&op) {
|
|
||||||
return iter::make_filter<Derived, OP>(move(), std::move(op));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replaces every item with item taken from n if it exists.
|
|
||||||
template <class R>
|
|
||||||
auto replace(Option<R> &n) {
|
|
||||||
return iter::make_limited_map<Derived>(
|
|
||||||
move(), [&](auto v) mutable { return std::move(n); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// For all items calls OP.
|
|
||||||
template <class OP>
|
|
||||||
void for_all(OP &&op) {
|
|
||||||
iter::for_all(move(), std::move(op));
|
|
||||||
}
|
|
||||||
|
|
||||||
// All items must satisfy given predicate for this function to return true.
|
|
||||||
// Otherwise stops calling predicate on firts false and returns fasle.
|
|
||||||
template <class OP>
|
|
||||||
bool all(OP &&op) {
|
|
||||||
auto iter = move();
|
|
||||||
auto e = iter.next();
|
|
||||||
while (e.is_present()) {
|
|
||||||
if (!op(e.take())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
e = iter.next();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// !! MEMGRAPH specific composable filters
|
|
||||||
// TODO: isolate, but it is not trivial because this class
|
|
||||||
// is a base for other generic classes
|
|
||||||
|
|
||||||
// Maps with call to method to() and filters with call to fill.
|
|
||||||
auto to() {
|
|
||||||
return map([](auto er) { return er.to(); }).fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maps with call to method from() and filters with call to fill.
|
|
||||||
auto from() {
|
|
||||||
return map([](auto er) { return er.from(); }).fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combines out iterators into one iterator.
|
|
||||||
auto out() {
|
|
||||||
return iter::make_flat_map<Derived>(
|
|
||||||
move(), [](auto vr) { return vr.out().fill(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
auto in() {
|
|
||||||
return iter::make_flat_map<Derived>(move(),
|
|
||||||
[](auto vr) { return vr.in().fill(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters with label on from vertex.
|
|
||||||
template <class LABEL>
|
|
||||||
auto from_label(LABEL const &label) {
|
|
||||||
return filter([&](auto &ra) {
|
|
||||||
auto va = ra.from();
|
|
||||||
return va.fill() && va.has_label(label);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls update on values and returns result.
|
|
||||||
auto update() {
|
|
||||||
return map([](auto ar) { return ar.update(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters with property under given key
|
|
||||||
template <class KEY>
|
|
||||||
auto has_property(KEY &key) {
|
|
||||||
return filter([&](auto &va) { return !va.at(key).is_empty(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters with property under given key
|
|
||||||
template <class KEY, class PROP>
|
|
||||||
auto has_property(KEY &key, PROP const &prop) {
|
|
||||||
return filter([&](auto &va) { return va.at(key) == prop; });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy-s pasing value to t before they are returned.
|
|
||||||
auto clone_to(Option<const T> &t) {
|
|
||||||
return iter::make_inspect<Derived>(
|
|
||||||
move(), [&](auto &v) mutable { t = Option<const T>(v); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// auto clone_to(Option<T> &t)
|
|
||||||
// {
|
|
||||||
// return iter::make_inspect<Derived>(
|
|
||||||
// move(), [&](auto &e) mutable { t = Option<T>(e); });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Filters with call to method fill()
|
|
||||||
auto fill() {
|
|
||||||
return filter([](auto &ra) { return ra.fill(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters with type
|
|
||||||
template <class TYPE>
|
|
||||||
auto type(TYPE const &type) {
|
|
||||||
return filter([&](auto &ra) { return ra.edge_type() == type; });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters with label.
|
|
||||||
template <class LABEL>
|
|
||||||
auto label(LABEL const &label) {
|
|
||||||
return filter([&](auto &va) { return va.has_label(label); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters out vertices which are connected.
|
|
||||||
auto isolated() {
|
|
||||||
return filter([&](auto &element) { return element.isolated(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter elements based on properties (all properties have to match)
|
|
||||||
// TRANSACTION -> transaction
|
|
||||||
// PROPERTIES -> [(name, property)]
|
|
||||||
template <class TRANSACTION, class PROPERTIES>
|
|
||||||
auto properties_filter(TRANSACTION &t, PROPERTIES &properties) {
|
|
||||||
return filter([&](auto &element) {
|
|
||||||
for (auto &name_value : properties) {
|
|
||||||
auto property_key = t.vertex_property_key(
|
|
||||||
name_value.first, name_value.second.key.flags());
|
|
||||||
if (!element.properties().contains(property_key)) return false;
|
|
||||||
auto vertex_property_value = element.at(property_key);
|
|
||||||
if (!(vertex_property_value == name_value.second)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/numerics/saturate.hpp"
|
|
||||||
#include "utils/total_ordering.hpp"
|
|
||||||
|
|
||||||
// Represents number of to be returned elements from iterator. Where acutal
|
|
||||||
// number is probably somwhere in [min,max].
|
|
||||||
// NOTE: Experimental
|
|
||||||
class Count : public TotalOrdering<Count> {
|
|
||||||
public:
|
|
||||||
Count(size_t exact) : min(exact), max(exact) {}
|
|
||||||
|
|
||||||
Count(size_t min, size_t max) : min(min), max(max) {}
|
|
||||||
|
|
||||||
Count min_zero() const { return Count(0, max); }
|
|
||||||
|
|
||||||
size_t avg() const { return ((max - min) >> 1) + min; }
|
|
||||||
|
|
||||||
friend constexpr bool operator<(const Count &lhs, const Count &rhs) {
|
|
||||||
return lhs.avg() < rhs.avg();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator==(const Count &lhs, const Count &rhs) {
|
|
||||||
return lhs.avg() == rhs.avg();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend Count operator+(const Count &lhs, const Count &rhs) {
|
|
||||||
return Count(num::saturating_add(lhs.min, rhs.min),
|
|
||||||
num::saturating_add(lhs.max, rhs.max));
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t min;
|
|
||||||
size_t max;
|
|
||||||
};
|
|
@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which filters values T returned by I iterator if OP returns true for
|
|
||||||
// them.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
// OP - type of filter function. OP: T& -> bool
|
|
||||||
template <class T, class I, class OP>
|
|
||||||
class Filter : public IteratorBase<T>, public Composable<T, Filter<T, I, OP>> {
|
|
||||||
public:
|
|
||||||
Filter() = delete;
|
|
||||||
|
|
||||||
// Filter operation is designed to be used in chained calls which operate on
|
|
||||||
// a
|
|
||||||
// iterator. Filter will in that usecase receive other iterator by value and
|
|
||||||
// std::move is a optimization for it.
|
|
||||||
Filter(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {}
|
|
||||||
|
|
||||||
// Return values for which filter return true.
|
|
||||||
Option<T> next() final {
|
|
||||||
auto item = Option<T>();
|
|
||||||
do {
|
|
||||||
item = iter.next();
|
|
||||||
if (!item.is_present()) {
|
|
||||||
return Option<T>();
|
|
||||||
}
|
|
||||||
} while (!op(item.get()));
|
|
||||||
|
|
||||||
return std::move(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final { return iter.count().min_zero(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
I iter;
|
|
||||||
OP op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_filter(I &&iter, OP &&op) {
|
|
||||||
// Compiler cant deduce type T. decltype is here to help with it.
|
|
||||||
return Filter<decltype(iter.next().take()), I, OP>(std::move(iter),
|
|
||||||
std::move(op));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which maps values returned by I iterator into iterators of type J
|
|
||||||
// ,which
|
|
||||||
// return value of type T, with function OP.
|
|
||||||
// function.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
// J - iterator type returned from OP
|
|
||||||
// OP - type of mapper function
|
|
||||||
// TODO: Split into flat operation and map operation.
|
|
||||||
template <class T, class I, class J, class OP>
|
|
||||||
class FlatMap : public IteratorBase<T>,
|
|
||||||
public Composable<T, FlatMap<T, I, J, OP>> {
|
|
||||||
public:
|
|
||||||
FlatMap() = delete;
|
|
||||||
|
|
||||||
// FlatMap operation is designed to be used in chained calls which operate
|
|
||||||
// on a
|
|
||||||
// iterator. FlatMap will in that usecase receive other iterator by value
|
|
||||||
// and
|
|
||||||
// std::move is a optimization for it.
|
|
||||||
FlatMap(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {}
|
|
||||||
|
|
||||||
FlatMap(FlatMap &&m)
|
|
||||||
: iter(std::move(m.iter)),
|
|
||||||
op(std::move(m.op)),
|
|
||||||
sub_iter(std::move(m.sub_iter)) {}
|
|
||||||
|
|
||||||
~FlatMap() final {}
|
|
||||||
|
|
||||||
Option<T> next() final {
|
|
||||||
do {
|
|
||||||
if (!sub_iter.is_present()) {
|
|
||||||
auto item = iter.next();
|
|
||||||
if (item.is_present()) {
|
|
||||||
sub_iter = Option<J>(op(item.take()));
|
|
||||||
} else {
|
|
||||||
return Option<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto item = sub_iter.get().next();
|
|
||||||
if (item.is_present()) {
|
|
||||||
return std::move(item);
|
|
||||||
} else {
|
|
||||||
sub_iter.take();
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final {
|
|
||||||
// TODO: Correct count, are at least correcter
|
|
||||||
return iter.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
I iter;
|
|
||||||
Option<J> sub_iter;
|
|
||||||
OP op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_flat_map(I &&iter, OP &&op) {
|
|
||||||
// Compiler cant deduce type T and J. decltype is here to help with it.
|
|
||||||
return FlatMap<decltype(op(iter.next().take()).next().take()), I,
|
|
||||||
decltype(op(iter.next().take())), OP>(std::move(iter),
|
|
||||||
std::move(op));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include "utils/option.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
template <class I, class C>
|
|
||||||
void for_all(I &&iter, C &&consumer) {
|
|
||||||
auto e = iter.next();
|
|
||||||
while (e.is_present()) {
|
|
||||||
consumer(e.take());
|
|
||||||
e = iter.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class I, class C>
|
|
||||||
void for_all(std::unique_ptr<I> &&iter, C &&consumer) {
|
|
||||||
auto e = iter->next();
|
|
||||||
while (e.is_present()) {
|
|
||||||
consumer(e.take());
|
|
||||||
e = iter->next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class I, class C>
|
|
||||||
void find(I iter, C &&consumer) {
|
|
||||||
auto e = iter.next();
|
|
||||||
while (e.is_present()) {
|
|
||||||
if (consumer(e.take())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e = iter.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which inspects values returned by I iterator
|
|
||||||
// before passing them as a result.
|
|
||||||
// function.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
// OP - type of inspector function. OP: T&->void
|
|
||||||
template <class T, class I, class OP>
|
|
||||||
class Inspect : public IteratorBase<T>,
|
|
||||||
public Composable<T, Inspect<T, I, OP>> {
|
|
||||||
public:
|
|
||||||
Inspect() = delete;
|
|
||||||
|
|
||||||
// Inspect operation is designed to be used
|
|
||||||
// in chained calls which operate on an
|
|
||||||
// iterator. Inspect will in that usecase
|
|
||||||
// receive other iterator by value and
|
|
||||||
// std::move is a optimization for it.
|
|
||||||
Inspect(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {}
|
|
||||||
|
|
||||||
Inspect(Inspect &&m) : iter(std::move(m.iter)), op(std::move(m.op)) {}
|
|
||||||
|
|
||||||
~Inspect() final {}
|
|
||||||
|
|
||||||
Option<T> next() final {
|
|
||||||
auto item = iter.next();
|
|
||||||
if (item.is_present()) {
|
|
||||||
op(item.get());
|
|
||||||
return std::move(item);
|
|
||||||
} else {
|
|
||||||
return Option<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final { return iter.count(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
I iter;
|
|
||||||
OP op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_inspect(I &&iter, OP &&op) {
|
|
||||||
// Compiler cant deduce type T. decltype is here to help with it.
|
|
||||||
return Inspect<decltype(iter.next().take()), I, OP>(std::move(iter),
|
|
||||||
std::move(op));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/accessor.hpp"
|
|
||||||
#include "utils/iterator/combined.hpp"
|
|
||||||
#include "utils/iterator/count.hpp"
|
|
||||||
#include "utils/iterator/filter.hpp"
|
|
||||||
#include "utils/iterator/flat_map.hpp"
|
|
||||||
#include "utils/iterator/for_all.hpp"
|
|
||||||
#include "utils/iterator/inspect.hpp"
|
|
||||||
#include "utils/iterator/iterator_accessor.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
#include "utils/iterator/lambda_iterator.hpp"
|
|
||||||
#include "utils/iterator/limited_map.hpp"
|
|
||||||
#include "utils/iterator/map.hpp"
|
|
||||||
#include "utils/iterator/range_iterator.hpp"
|
|
||||||
#include "utils/iterator/virtual_iter.hpp"
|
|
@ -1,72 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which turns accessor int next() based iterator.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type gotten from accessor
|
|
||||||
// A - accessor type
|
|
||||||
template <class T, class I, class A>
|
|
||||||
class IteratorAccessor : public IteratorBase<T>,
|
|
||||||
public Composable<T, IteratorAccessor<T, I, A>> {
|
|
||||||
public:
|
|
||||||
IteratorAccessor() = delete;
|
|
||||||
|
|
||||||
IteratorAccessor(A &&acc)
|
|
||||||
: begin(std::move(acc.begin())), acc(std::forward<A>(acc)), returned(0) {}
|
|
||||||
|
|
||||||
IteratorAccessor(IteratorAccessor &&other)
|
|
||||||
: begin(std::move(other.begin)),
|
|
||||||
acc(std::forward<A>(other.acc)),
|
|
||||||
returned(other.returned) {}
|
|
||||||
|
|
||||||
~IteratorAccessor() final {}
|
|
||||||
|
|
||||||
// Iter(const Iter &other) = delete;
|
|
||||||
// Iter(Iter &&other) :
|
|
||||||
// begin(std::move(other.begin)),end(std::move(other.end)) {};
|
|
||||||
|
|
||||||
Option<T> next() final {
|
|
||||||
if (begin != acc.end()) {
|
|
||||||
auto ret = Option<T>(&(*(begin.operator->())));
|
|
||||||
begin++;
|
|
||||||
returned++;
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return Option<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final {
|
|
||||||
auto size = acc.size();
|
|
||||||
if (size > returned) {
|
|
||||||
return Count(0);
|
|
||||||
} else {
|
|
||||||
return Count(size - returned);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
I begin;
|
|
||||||
A acc;
|
|
||||||
size_t returned;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Join to make functions into one
|
|
||||||
template <class A>
|
|
||||||
auto make_iter(A &&acc) {
|
|
||||||
// Compiler cant deduce types T and I. decltype are here to help with it.
|
|
||||||
return IteratorAccessor<decltype(&(*(acc.begin().operator->()))),
|
|
||||||
decltype(acc.begin()), A>(std::move(acc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class A>
|
|
||||||
auto make_iter_ref(A &acc) {
|
|
||||||
// Compiler cant deduce types T and I. decltype are here to help with it.
|
|
||||||
return IteratorAccessor<decltype(&(*(acc.begin().operator->()))),
|
|
||||||
decltype(acc.begin()), A &>(acc);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/count.hpp"
|
|
||||||
#include "utils/option.hpp"
|
|
||||||
|
|
||||||
// Base iterator for next() kind iterator.
|
|
||||||
// T - type of return value
|
|
||||||
template <class T>
|
|
||||||
class IteratorBase {
|
|
||||||
public:
|
|
||||||
virtual ~IteratorBase(){};
|
|
||||||
|
|
||||||
virtual Option<T> next() = 0;
|
|
||||||
|
|
||||||
virtual Count count() = 0;
|
|
||||||
};
|
|
@ -1,48 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
// Wraps lambda into interator with next().
|
|
||||||
// T - type of return value
|
|
||||||
// F - type of wraped lambda
|
|
||||||
template <class T, class F>
|
|
||||||
class LambdaIterator : public IteratorBase<T>,
|
|
||||||
public Composable<T, LambdaIterator<T, F>> {
|
|
||||||
public:
|
|
||||||
LambdaIterator(F &&f, size_t count) : func(std::move(f)), _count(count) {}
|
|
||||||
|
|
||||||
LambdaIterator(LambdaIterator &&other)
|
|
||||||
: func(std::move(other.func)), _count(other._count) {}
|
|
||||||
|
|
||||||
~LambdaIterator() final {}
|
|
||||||
|
|
||||||
Option<T> next() final {
|
|
||||||
_count = _count > 0 ? _count - 1 : 0;
|
|
||||||
return func();
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final { return Count(_count); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
F func;
|
|
||||||
size_t _count;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wraps lambda which returns options as an iterator.
|
|
||||||
template <class F>
|
|
||||||
auto make_iterator(F &&f) {
|
|
||||||
return make_iterator<F>(std::move(f), ~((size_t)0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps lambda which returns options as an iterator.
|
|
||||||
template <class F>
|
|
||||||
auto make_iterator(F &&f, size_t count) {
|
|
||||||
// Because function isn't receving or in any way using type T from
|
|
||||||
// FunctionIterator compiler can't deduce it thats way there is decltype in
|
|
||||||
// construction of FunctionIterator. Resoulting type of iter.next().take()
|
|
||||||
// is T.
|
|
||||||
return LambdaIterator<decltype(f().take()), F>(std::move(f), count);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which maps values returned by I iterator into value of type T with OP
|
|
||||||
// function and ends when op returns empty optional.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
// OP - type of mapper function. OP: V -> Option<T>
|
|
||||||
template <class T, class I, class OP>
|
|
||||||
class LimitedMap : public IteratorBase<T>,
|
|
||||||
public Composable<T, LimitedMap<T, I, OP>> {
|
|
||||||
public:
|
|
||||||
LimitedMap() = delete;
|
|
||||||
|
|
||||||
// LimitedMap operation is designed to be used in chained calls which
|
|
||||||
// operate on a
|
|
||||||
// iterator. LimitedMap will in that usecase receive other iterator by value
|
|
||||||
// and
|
|
||||||
// std::move is a optimization for it.
|
|
||||||
LimitedMap(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {}
|
|
||||||
|
|
||||||
LimitedMap(LimitedMap &&m) : iter(std::move(m.iter)), op(std::move(m.op)) {}
|
|
||||||
|
|
||||||
~LimitedMap() final {}
|
|
||||||
|
|
||||||
Option<T> next() final {
|
|
||||||
auto item = iter.next();
|
|
||||||
if (item.is_present()) {
|
|
||||||
return op(item.take());
|
|
||||||
} else {
|
|
||||||
return Option<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final { return iter.count(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
I iter;
|
|
||||||
OP op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_limited_map(I &&iter, OP &&op) {
|
|
||||||
// Compiler cant deduce type T. decltype is here to help with it.
|
|
||||||
return LimitedMap<decltype(op(iter.next().take()).take()), I, OP>(
|
|
||||||
std::move(iter), std::move(op));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which maps values returned by I iterator into value of type T with OP
|
|
||||||
// function.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
// OP - type of mapper function
|
|
||||||
template <class T, class I, class OP>
|
|
||||||
class Map : public IteratorBase<T>, public Composable<T, Map<T, I, OP>> {
|
|
||||||
public:
|
|
||||||
Map() = delete;
|
|
||||||
|
|
||||||
// Map operation is designed to be used in chained calls which operate on a
|
|
||||||
// iterator. Map will in that usecase receive other iterator by value and
|
|
||||||
// std::move is a optimization for it.
|
|
||||||
Map(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {}
|
|
||||||
|
|
||||||
Map(Map &&m) : iter(std::move(m.iter)), op(std::move(m.op)) {}
|
|
||||||
|
|
||||||
~Map() final {}
|
|
||||||
|
|
||||||
Option<T> next() final {
|
|
||||||
auto item = iter.next();
|
|
||||||
if (item.is_present()) {
|
|
||||||
return Option<T>(op(item.take()));
|
|
||||||
} else {
|
|
||||||
return Option<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Count count() final { return iter.count(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
I iter;
|
|
||||||
OP op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I, class OP>
|
|
||||||
auto make_map(I &&iter, OP &&op) {
|
|
||||||
// Compiler cant deduce type T. decltype is here to help with it.
|
|
||||||
return Map<decltype(op(iter.next().take())), I, OP>(std::move(iter),
|
|
||||||
std::move(op));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "storage/vertex_accessor.hpp"
|
|
||||||
#include "utils/iterator/iterator.hpp"
|
|
||||||
#include "utils/option.hpp"
|
|
||||||
|
|
||||||
namespace query_help {
|
|
||||||
|
|
||||||
template <class A>
|
|
||||||
bool fill(A &a) {
|
|
||||||
return a.fill();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Base iterator for next() kind iterator.
|
|
||||||
// Vertex::Accessor - type of return value
|
|
||||||
template <>
|
|
||||||
class IteratorBase<Vertex::Accessor> {
|
|
||||||
public:
|
|
||||||
virtual Option<Vertex::Accessor> next() = 0;
|
|
||||||
|
|
||||||
auto fill() { return iter::make_filter(std::move(*this), query_help::fill); }
|
|
||||||
};
|
|
@ -1,63 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/assert.hpp"
|
|
||||||
#include "utils/option.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which wraps iterator with next() into C++ iterator.
|
|
||||||
// T - type of return value
|
|
||||||
// I - iterator type
|
|
||||||
template <class T, class I>
|
|
||||||
class RangeIterator {
|
|
||||||
public:
|
|
||||||
RangeIterator() : iter(Option<I>()), value(Option<T>()){};
|
|
||||||
|
|
||||||
RangeIterator(I &&iter)
|
|
||||||
: value(iter.next()), iter(Option<I>(std::move(iter))) {}
|
|
||||||
|
|
||||||
T &operator*() {
|
|
||||||
debug_assert(value.is_present(), "No value.");
|
|
||||||
return value.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
T *operator->() {
|
|
||||||
debug_assert(value.is_present(), "No value.");
|
|
||||||
return &value.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
operator T &() {
|
|
||||||
debug_assert(value.is_present(), "No value.");
|
|
||||||
return value.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
RangeIterator &operator++() {
|
|
||||||
debug_assert(iter.is_present(), "No value.");
|
|
||||||
value = iter.get().next();
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
RangeIterator &operator++(int) { return operator++(); }
|
|
||||||
|
|
||||||
friend bool operator==(const RangeIterator &a, const RangeIterator &b) {
|
|
||||||
return a.value.is_present() == b.value.is_present();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator!=(const RangeIterator &a, const RangeIterator &b) {
|
|
||||||
return !(a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Option<I> iter;
|
|
||||||
Option<T> value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I>
|
|
||||||
auto make_range_iterator(I &&iter) {
|
|
||||||
// Because this function isn't receving or in any way using type T from
|
|
||||||
// RangeIterator, compiler can't deduce it. That is the reason why
|
|
||||||
// there is decltype in construction of RangeIterator.
|
|
||||||
// declytype(iter.next().take()) is T.
|
|
||||||
return RangeIterator<decltype(iter.next().take()), I>(std::move(iter));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/iterator/composable.hpp"
|
|
||||||
#include "utils/iterator/iterator_base.hpp"
|
|
||||||
|
|
||||||
namespace iter {
|
|
||||||
|
|
||||||
// Class which wraps iterator and hides it's type. It actualy does this by
|
|
||||||
// dynamicly allocating iterator on heap.
|
|
||||||
// T - type of return value
|
|
||||||
template <class T>
|
|
||||||
class Virtual : public Composable<T, Virtual<T>> {
|
|
||||||
public:
|
|
||||||
Virtual() = delete;
|
|
||||||
|
|
||||||
// Virtual operation is designed to be used in chained calls which operate
|
|
||||||
// on a
|
|
||||||
// iterator. Virtual will in that usecase receive other iterator by value
|
|
||||||
// and
|
|
||||||
// std::move is a optimization for it.
|
|
||||||
|
|
||||||
template <class I>
|
|
||||||
Virtual(I &&iter) : it(std::make_unique<I>(std::move(iter))) {}
|
|
||||||
|
|
||||||
Virtual(Virtual &&m) : it(std::move(m.it)) {}
|
|
||||||
|
|
||||||
~Virtual() {}
|
|
||||||
|
|
||||||
Option<T> next() { return it.get()->next(); }
|
|
||||||
|
|
||||||
Count count() { return it.get()->count(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<IteratorBase<T>> it;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class I>
|
|
||||||
auto make_virtual(I &&iter) {
|
|
||||||
// Compiler can't deduce type T. decltype is here to help with it.
|
|
||||||
return Virtual<decltype(iter.next().take())>(std::move(iter));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ** Books **
|
|
||||||
// http://instructor.sdu.edu.kz/~konst/sysprog2015fall/readings/linux%20system%20programming/The%20Linux%20Programming%20Interface-Michael%20Kerrisk.pdf
|
|
||||||
|
|
||||||
// ** Documentation **
|
|
||||||
// http://man7.org/linux/man-pages/man2/read.2.html
|
|
||||||
// http://man7.org/linux/man-pages/man2/select.2.html
|
|
||||||
// http://man7.org/linux/man-pages/man2/fcntl.2.html
|
|
||||||
|
|
||||||
// ** Community **
|
|
||||||
// http://stackoverflow.com/questions/5616092/non-blocking-call-for-reading-descriptor
|
|
||||||
// http://stackoverflow.com/questions/2917881/how-to-implement-a-timeout-in-read-function-call
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "utils/exceptions/not_yet_implemented.hpp"
|
|
||||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
|
||||||
#include "utils/likely.hpp"
|
|
||||||
|
|
||||||
namespace linux_os {
|
|
||||||
class LinuxException : public StacktraceException {
|
|
||||||
using StacktraceException::StacktraceException;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets non blocking flag to a file descriptor.
|
|
||||||
*/
|
|
||||||
void set_non_blocking(int fd) {
|
|
||||||
auto flags = fcntl(fd, F_GETFL, 0);
|
|
||||||
|
|
||||||
if (UNLIKELY(flags == -1))
|
|
||||||
throw LinuxException("Cannot read flags from file descriptor.");
|
|
||||||
|
|
||||||
flags |= O_NONBLOCK;
|
|
||||||
|
|
||||||
auto status = fcntl(fd, F_SETFL, flags);
|
|
||||||
|
|
||||||
if (UNLIKELY(status == -1))
|
|
||||||
throw LinuxException("Can't set NON_BLOCK flag to file descriptor");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a file descriptor with timeout.
|
|
||||||
*/
|
|
||||||
void tread() { throw NotYetImplemented(); }
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct MarkRef {
|
|
||||||
MarkRef() = default;
|
|
||||||
MarkRef(MarkRef&) = default;
|
|
||||||
MarkRef(MarkRef&&) = default;
|
|
||||||
|
|
||||||
bool is_marked() const { return ptr & 0x1L; }
|
|
||||||
|
|
||||||
bool set_mark() { return ptr |= 0x1L; }
|
|
||||||
|
|
||||||
bool clear_mark() { return ptr &= ~0x1L; }
|
|
||||||
|
|
||||||
T* get() const { return reinterpret_cast<T*>(ptr & ~0x1L); }
|
|
||||||
|
|
||||||
T& operator*() { return *get(); }
|
|
||||||
T* operator->() { return get(); }
|
|
||||||
|
|
||||||
operator T*() { return get(); }
|
|
||||||
|
|
||||||
uintptr_t ptr;
|
|
||||||
};
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// Defines ordering of data
|
|
||||||
enum Order : uint8_t {
|
|
||||||
None = 0,
|
|
||||||
Ascending = 1,
|
|
||||||
Descending = 2,
|
|
||||||
};
|
|
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2017 Memgraph
|
|
||||||
// Created by Marko Budiselic
|
|
||||||
//
|
|
||||||
// SOURCES:
|
|
||||||
// http://arne-mertz.de/2016/10/passkey-idiom/
|
|
||||||
// http://stackoverflow.com/questions/3217390/clean-c-granular-friend-equivalent-answer-attorney-client-idiom
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class PassKey {
|
|
||||||
friend T;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// default constructor has to be manually defined otherwise = default
|
|
||||||
// would allow aggregate initialization to bypass default constructor
|
|
||||||
// both, default and copy constructors have to be user-defined
|
|
||||||
// otherwise are public by default
|
|
||||||
PassKey() {}
|
|
||||||
};
|
|
@ -1,39 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// ReferenceWrapper was created because std::reference_wrapper
|
|
||||||
// wasn't copyable
|
|
||||||
// Implementation has been taken from:
|
|
||||||
// http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
|
|
||||||
// TODO: once the c++ implementation will have proper implementation
|
|
||||||
// this class should be deleted and replaced with std::reference_wrapper
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class ReferenceWrapper {
|
|
||||||
public:
|
|
||||||
// types
|
|
||||||
typedef T type;
|
|
||||||
|
|
||||||
// construct/copy/destroy
|
|
||||||
ReferenceWrapper(T &ref) noexcept : _ptr(std::addressof(ref)) {}
|
|
||||||
ReferenceWrapper(T &&) = delete;
|
|
||||||
ReferenceWrapper(const ReferenceWrapper &) noexcept = default;
|
|
||||||
|
|
||||||
// assignment
|
|
||||||
ReferenceWrapper &operator=(const ReferenceWrapper &x) noexcept = default;
|
|
||||||
|
|
||||||
// access
|
|
||||||
operator T &() const noexcept { return *_ptr; }
|
|
||||||
T &get() const noexcept { return *_ptr; }
|
|
||||||
|
|
||||||
// template <class... ArgTypes>
|
|
||||||
// typename std::result_of<T &(ArgTypes &&...)>::type
|
|
||||||
// operator()(ArgTypes &&... args) const
|
|
||||||
// {
|
|
||||||
// return std::invoke(get(), std::forward<ArgTypes>(args)...);
|
|
||||||
// }
|
|
||||||
|
|
||||||
private:
|
|
||||||
T *_ptr;
|
|
||||||
};
|
|
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// Wraps stream with convinient methods which need only one method:
|
|
||||||
// write (const char* s, n);
|
|
||||||
template <class STREAM>
|
|
||||||
class StreamWrapper {
|
|
||||||
public:
|
|
||||||
StreamWrapper() = delete;
|
|
||||||
StreamWrapper(STREAM &s) : stream(s) {}
|
|
||||||
|
|
||||||
void write(const unsigned char value) {
|
|
||||||
stream.write(reinterpret_cast<const char *>(&value), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const unsigned char *value, size_t n) {
|
|
||||||
stream.write(reinterpret_cast<const char *>(value), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
STREAM &stream;
|
|
||||||
};
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace utils {
|
|
||||||
|
|
||||||
class StringBuffer {
|
|
||||||
public:
|
|
||||||
StringBuffer() = default;
|
|
||||||
~StringBuffer() = default;
|
|
||||||
|
|
||||||
StringBuffer(const StringBuffer &) = delete;
|
|
||||||
StringBuffer(StringBuffer &&) = default;
|
|
||||||
|
|
||||||
StringBuffer &operator=(const StringBuffer &) = delete;
|
|
||||||
StringBuffer &operator=(StringBuffer &&) = default;
|
|
||||||
|
|
||||||
StringBuffer(std::string::size_type count) { resize(count); }
|
|
||||||
|
|
||||||
void resize(std::string::size_type count) { data.resize(count); }
|
|
||||||
|
|
||||||
StringBuffer &operator<<(const std::string &str) {
|
|
||||||
data += str;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuffer &operator<<(const char *str) {
|
|
||||||
data += str;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuffer &operator<<(char c) {
|
|
||||||
data += c;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string &str() { return data; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string data;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <linux/futex.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
namespace sys {
|
|
||||||
// Code from stackoverflow:
|
|
||||||
// http://stackoverflow.com/questions/676787/how-to-do-fsync-on-an-ofstream
|
|
||||||
// Extracts FILE* from streams in std.
|
|
||||||
inline int GetFileDescriptor(std::filebuf &filebuf) {
|
|
||||||
class my_filebuf : public std::filebuf {
|
|
||||||
public:
|
|
||||||
int handle() { return _M_file.fd(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
return static_cast<my_filebuf &>(filebuf).handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int futex(void *addr1, int op, int val1, const struct timespec *timeout,
|
|
||||||
void *addr2, int val3) {
|
|
||||||
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensures that everything written to file will be writen on disk when the
|
|
||||||
// function call returns. !=0 if error occured
|
|
||||||
template <class STREAM>
|
|
||||||
inline size_t flush_file_to_disk(STREAM &file) {
|
|
||||||
file.flush();
|
|
||||||
if (fsync(GetFileDescriptor(*file.rdbuf())) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return errno;
|
|
||||||
};
|
|
||||||
|
|
||||||
// True if succesffull
|
|
||||||
inline bool ensure_directory_exists(std::string const &path) {
|
|
||||||
struct stat st = {0};
|
|
||||||
|
|
||||||
if (stat(path.c_str(), &st) == -1) {
|
|
||||||
return mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
@ -11,19 +11,54 @@
|
|||||||
*/
|
*/
|
||||||
template <typename TLhs, typename TRhs = TLhs, typename TReturn = bool>
|
template <typename TLhs, typename TRhs = TLhs, typename TReturn = bool>
|
||||||
struct TotalOrdering {
|
struct TotalOrdering {
|
||||||
friend constexpr TReturn operator!=(const TLhs &a, const TRhs &b) {
|
friend constexpr TReturn operator!=(const TLhs& a, const TRhs& b) {
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend constexpr TReturn operator<=(const TLhs &a, const TRhs &b) {
|
friend constexpr TReturn operator<=(const TLhs& a, const TRhs& b) {
|
||||||
return a < b || a == b;
|
return a < b || a == b;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend constexpr TReturn operator>(const TLhs &a, const TRhs &b) {
|
friend constexpr TReturn operator>(const TLhs& a, const TRhs& b) {
|
||||||
return !(a <= b);
|
return !(a <= b);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend constexpr TReturn operator>=(const TLhs &a, const TRhs &b) {
|
friend constexpr TReturn operator>=(const TLhs& a, const TRhs& b) {
|
||||||
|
return !(a < b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Derived, class T>
|
||||||
|
struct TotalOrderingWith {
|
||||||
|
friend constexpr bool operator!=(const Derived& a, const T& b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator<=(const Derived& a, const T& b) {
|
||||||
|
return a < b || a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator>(const Derived& a, const T& b) {
|
||||||
|
return !(a <= b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator>=(const Derived& a, const T& b) {
|
||||||
|
return !(a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator!=(const T& a, const Derived& b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator<=(const T& a, const Derived& b) {
|
||||||
|
return a < b || a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator>(const T& a, const Derived& b) {
|
||||||
|
return !(a <= b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator>=(const T& a, const Derived& b) {
|
||||||
return !(a < b);
|
return !(a < b);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
template <class Derived, class T>
|
|
||||||
struct TotalOrderingWith {
|
|
||||||
friend constexpr bool operator!=(const Derived& a, const T& b) {
|
|
||||||
return !(a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator<=(const Derived& a, const T& b) {
|
|
||||||
return a < b || a == b;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator>(const Derived& a, const T& b) {
|
|
||||||
return !(a <= b);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator>=(const Derived& a, const T& b) {
|
|
||||||
return !(a < b);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator!=(const T& a, const Derived& b) {
|
|
||||||
return !(a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator<=(const T& a, const Derived& b) {
|
|
||||||
return a < b || a == b;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator>(const T& a, const Derived& b) {
|
|
||||||
return !(a <= b);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator>=(const T& a, const Derived& b) {
|
|
||||||
return !(a < b);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/total_ordering.hpp"
|
|
||||||
|
|
||||||
// Type which represents nothing.
|
|
||||||
class Void : public TotalOrdering<Void> {
|
|
||||||
public:
|
|
||||||
friend bool operator<(const Void &lhs, const Void &rhs) { return false; }
|
|
||||||
|
|
||||||
friend bool operator==(const Void &lhs, const Void &rhs) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
static Void _void = {};
|
|
Loading…
Reference in New Issue
Block a user