Commit f1681080 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Add string_view-aware convenience map type

parent e1c3146d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <algorithm>
#include <iterator>
#include <map>
#include <string>
#include "caf/string_view.hpp"
namespace caf {
/// Maps strings to values of type `V`, but unlike `std::map<std::string, V>`
/// accepts `string_view` for looking up keys efficiently.
template <class V>
class dictionary {
public:
// -- member types ----------------------------------------------------------
using map_type = std::map<std::string, V>;
using key_type = typename map_type::key_type;
using mapped_type = typename map_type::mapped_type;
using value_type = typename map_type::value_type;
using allocator_type = typename map_type::allocator_type;
using size_type = typename map_type::size_type;
using difference_type = typename map_type::difference_type;
using reference = typename map_type::reference;
using const_reference = typename map_type::const_reference;
using pointer = typename map_type::pointer;
using const_pointer = typename map_type::const_pointer;
using iterator = typename map_type::iterator;
using const_iterator = typename map_type::const_iterator;
using reverse_iterator = typename map_type::reverse_iterator;
using const_reverse_iterator = typename map_type::const_reverse_iterator;
using iterator_bool_pair = std::pair<iterator, bool>;
struct mapped_type_less {
inline bool operator()(const value_type& x, string_view y) const {
return x.first < y;
}
inline bool operator()(const value_type& x, const value_type& y) const {
return x.first < y.first;
}
inline bool operator()(string_view x, const value_type& y) const {
return x < y.first;
}
};
// -- constructors, destructors, and assignment operators -------------------
dictionary() = default;
dictionary(std::initializer_list<value_type> xs) : xs_(xs) {
// nop
}
template <class InputIterator>
dictionary(InputIterator first, InputIterator last) : xs_(first, last) {
// nop
}
// -- iterator access --------------------------------------------------------
iterator begin() noexcept {
return xs_.begin();
}
const_iterator begin() const noexcept {
return xs_.begin();
}
const_iterator cbegin() const noexcept {
return begin();
}
reverse_iterator rbegin() noexcept {
return xs_.rbegin();
}
const_reverse_iterator rbegin() const noexcept {
return xs_.rbegin();
}
const_reverse_iterator crbegin() const noexcept {
return rbegin();
}
iterator end() noexcept {
return xs_.end();
}
const_iterator end() const noexcept {
return xs_.end();
}
const_iterator cend() const noexcept {
return end();
}
reverse_iterator rend() noexcept {
return xs_.rend();
}
const_reverse_iterator rend() const noexcept {
return xs_.rend();
}
const_reverse_iterator crend() const noexcept {
return rend();
}
// -- size -------------------------------------------------------------------
bool empty() const noexcept {
return xs_.empty();
}
size_type size() const noexcept {
return xs_.size();
}
// -- access to members ------------------------------------------------------
/// Gives raw access to the underlying container.
map_type& container() noexcept {
return xs_;
}
/// Gives raw access to the underlying container.
const map_type& container() const noexcept {
return xs_;
}
// -- modifiers -------------------------------------------------------------
void clear() noexcept {
return xs_.clear();
}
void swap(dictionary& other) {
xs_.swap(other.xs_);
}
// -- insertion --------------------------------------------------------------
template <class T>
iterator_bool_pair insert(string_view key, T&& value) {
auto i = lower_bound(key);
if (i == end())
return xs_.emplace(copy(key), std::forward<T>(value));
if (i->first == key)
return {i, false};
return {xs_.emplace_hint(i, copy(key), std::forward<T>(value)), true};
}
template <class T>
iterator insert(iterator hint, string_view key, T&& value) {
if (hint == end() || hint->first > key)
return xs_.emplace(copy(key), std::forward<T>(value)).first;
if (hint->first == key)
return hint;
return xs_.emplace_hint(hint, copy(key), std::forward<T>(value));
}
template <class T>
iterator_bool_pair insert_or_assign(string_view key, T&& value) {
auto i = lower_bound(key);
if (i == end())
return xs_.emplace(copy(key), std::forward<T>(value));
if (i->first == key) {
i->second = std::forward<T>(value);
return {i, false};
}
return {xs_.emplace_hint(i, copy(key), std::forward<T>(value)), true};
}
template <class T>
iterator insert_or_assign(iterator hint, string_view key, T&& value) {
if (hint == end() || hint->first > key)
return insert_or_assign(key, std::forward<T>(value)).first;
hint = lower_bound(hint, key);
if (hint != end() && hint->first == key) {
hint->second = std::forward<T>(value);
return hint;
}
return xs_.emplace_hint(hint, copy(key), std::forward<T>(value));
}
// -- lookup -----------------------------------------------------------------
bool contains(string_view key) const noexcept {
auto i = lower_bound(key);
return !(i == end() || i->first != key);
}
size_t count(string_view key) const noexcept {
return contains(key) ? 1u : 0u;
}
iterator find(string_view key) noexcept {
auto i = lower_bound(key);
return i != end() && i->first == key ? i : end();
}
const_iterator find(string_view key) const noexcept {
auto i = lower_bound(key);
return i != end() && i->first == key ? i : end();
}
iterator lower_bound(string_view key) {
return lower_bound(begin(), key);
}
const_iterator lower_bound(string_view key) const {
return lower_bound(begin(), key);
}
iterator upper_bound(string_view key) {
mapped_type_less cmp;
return std::upper_bound(begin(), end(), key, cmp);
}
const_iterator upper_bound(string_view key) const {
mapped_type_less cmp;
return std::upper_bound(begin(), end(), key, cmp);
}
// -- element access ---------------------------------------------------------
mapped_type& operator[](string_view key) {
return insert(key, mapped_type{}).first->second;
}
private:
iterator lower_bound(iterator from, string_view key) {
mapped_type_less cmp;
return std::lower_bound(from, end(), key, cmp);
}
const_iterator lower_bound(const_iterator from, string_view key) const {
mapped_type_less cmp;
return std::lower_bound(from, end(), key, cmp);
}
// Copies the content of `str` into a new string.
static inline std::string copy(string_view str) {
return std::string{str.begin(), str.end()};
}
map_type xs_;
};
// @relates dictionary
template <class T>
bool operator==(const dictionary<T>& xs, const dictionary<int>& ys) {
return xs.container() == ys.container();
}
// @relates dictionary
template <class T>
bool operator!=(const dictionary<T>& xs, const dictionary<int>& ys) {
return xs.container() != ys.container();
}
// @relates dictionary
template <class T>
bool operator<(const dictionary<T>& xs, const dictionary<int>& ys) {
return xs.container() < ys.container();
}
// @relates dictionary
template <class T>
bool operator<=(const dictionary<T>& xs, const dictionary<int>& ys) {
return xs.container() <= ys.container();
}
// @relates dictionary
template <class T>
bool operator>(const dictionary<T>& xs, const dictionary<int>& ys) {
return xs.container() > ys.container();
}
// @relates dictionary
template <class T>
bool operator>=(const dictionary<T>& xs, const dictionary<int>& ys) {
return xs.container() >= ys.container();
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE dictionary
#include "caf/test/dsl.hpp"
#include "caf/dictionary.hpp"
using namespace caf;
namespace {
using int_dict = dictionary<int>;
struct fixture {
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(dictionary_tests, fixture)
CAF_TEST(construction and comparions) {
int_dict xs;
CAF_CHECK_EQUAL(xs.empty(), true);
CAF_CHECK_EQUAL(xs.size(), 0u);
int_dict ys{{"foo", 1}, {"bar", 2}};
CAF_CHECK_EQUAL(ys.empty(), false);
CAF_CHECK_EQUAL(ys.size(), 2u);
CAF_CHECK_NOT_EQUAL(xs, ys);
int_dict zs{ys.begin(), ys.end()};
CAF_CHECK_EQUAL(zs.empty(), false);
CAF_CHECK_EQUAL(zs.size(), 2u);
CAF_CHECK_EQUAL(ys, zs);
zs.clear();
CAF_CHECK_EQUAL(zs.empty(), true);
CAF_CHECK_EQUAL(zs.size(), 0u);
CAF_CHECK_EQUAL(xs, zs);
}
CAF_TEST(iterators) {
using std::equal;
using vector_type = std::vector<int_dict::value_type>;
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}};
vector_type ys{{"a", 1}, {"b", 2}, {"c", 3}};
CAF_CHECK(equal(xs.begin(), xs.end(), ys.begin()));
CAF_CHECK(equal(xs.cbegin(), xs.cend(), ys.cbegin()));
CAF_CHECK(equal(xs.rbegin(), xs.rend(), ys.rbegin()));
CAF_CHECK(equal(xs.crbegin(), xs.crend(), ys.crbegin()));
}
CAF_TEST(swapping) {
int_dict xs{{"foo", 1}, {"bar", 2}};
int_dict ys;
int_dict zs{{"foo", 1}, {"bar", 2}};
CAF_CHECK_NOT_EQUAL(xs, ys);
CAF_CHECK_NOT_EQUAL(ys, zs);
CAF_CHECK_EQUAL(xs, zs);
xs.swap(ys);
CAF_CHECK_NOT_EQUAL(xs, ys);
CAF_CHECK_EQUAL(ys, zs);
CAF_CHECK_NOT_EQUAL(xs, zs);
}
CAF_TEST(insertion) {
int_dict xs;
CAF_CHECK_EQUAL(xs.insert("a", 1).second, true);
CAF_CHECK_EQUAL(xs.insert("b", 2).second, true);
CAF_CHECK_EQUAL(xs.insert("c", 3).second, true);
CAF_CHECK_EQUAL(xs.insert("c", 4).second, false);
int_dict ys;
CAF_CHECK_EQUAL(ys.insert_or_assign("a", 1).second, true);
CAF_CHECK_EQUAL(ys.insert_or_assign("b", 2).second, true);
CAF_CHECK_EQUAL(ys.insert_or_assign("c", 0).second, true);
CAF_CHECK_EQUAL(ys.insert_or_assign("c", 3).second, false);
CAF_CHECK_EQUAL(xs, ys);
}
CAF_TEST(insertion with hint) {
int_dict xs;
auto xs_last = xs.end();
auto xs_insert = [&](string_view key, int val) {
xs_last = xs.insert(xs_last, key, val);
};
xs_insert("a", 1);
xs_insert("c", 3);
xs_insert("b", 2);
xs_insert("c", 4);
int_dict ys;
auto ys_last = ys.end();
auto ys_insert_or_assign = [&](string_view key, int val) {
ys_last = ys.insert_or_assign(ys_last, key, val);
};
ys_insert_or_assign("a", 1);
ys_insert_or_assign("c", 0);
ys_insert_or_assign("b", 2);
ys_insert_or_assign("c", 3);
CAF_CHECK_EQUAL(xs, ys);
}
CAF_TEST(bounds) {
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};
const int_dict& const_xs = xs;
CAF_CHECK_EQUAL(xs.lower_bound("c")->first, "c");
CAF_CHECK_EQUAL(xs.upper_bound("c")->first, "d");
CAF_CHECK_EQUAL(const_xs.lower_bound("c")->first, "c");
CAF_CHECK_EQUAL(const_xs.upper_bound("c")->first, "d");
}
CAF_TEST(find) {
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};
const int_dict& const_xs = xs;
CAF_CHECK_EQUAL(xs.find("e"), xs.end());
CAF_CHECK_EQUAL(xs.find("a")->second, 1);
CAF_CHECK_EQUAL(xs.find("c")->second, 3);
CAF_CHECK_EQUAL(const_xs.find("e"), xs.end());
CAF_CHECK_EQUAL(const_xs.find("a")->second, 1);
CAF_CHECK_EQUAL(const_xs.find("c")->second, 3);
}
CAF_TEST(element access) {
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};
CAF_CHECK_EQUAL(xs["a"], 1);
CAF_CHECK_EQUAL(xs["b"], 2);
CAF_CHECK_EQUAL(xs["e"], 0);
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment