Skip to content

Commit

Permalink
Merge pull request #2 from andreiavrammsd/v0.2.0
Browse files Browse the repository at this point in the history
V0.2.0
  • Loading branch information
andreiavrammsd authored Oct 23, 2021
2 parents ae96e3b + 14b4f4c commit 42c27aa
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 151 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BasedOnStyle: Google
IndentWidth: 4
Language: Cpp
PointerAlignment: Right
PointerAlignment: Left
BreakBeforeBraces: Stroustrup
ColumnLimit: 120
4 changes: 2 additions & 2 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: build
name: test

on:
push:
Expand All @@ -10,7 +10,7 @@ env:
BUILD_TYPE: Release

jobs:
build:
test:
# The CMake configure and build commands are platform agnostic and should work equally
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
Expand Down
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
cmake_minimum_required(VERSION 3.17)
project(poly_map)
set(PROJECT_VERSION 0.1.0)
set(PROJECT_VERSION 0.2.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CXX_EXTENSIONS NO)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror --coverage -fsanitize=undefined")

include_directories(include)

Expand Down
205 changes: 59 additions & 146 deletions include/msd/poly_map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,142 +11,23 @@

namespace msd {

template <typename... Keys>
class poly_map {
static_assert(sizeof...(Keys) > 0, "no key type provided");

struct poly_map_element;

public:
/**
* Checked access by key.
*
* @tparam T Type of key.
*
* @param key Key to index map by.
*
* @return Map at given key.
*
* @throws std::out_of_range if key not found.
*/
template <typename T>
[[nodiscard]] poly_map_element& at(const T& key)
{
return elements_.at(key);
}

/**
* Checked access by list of keys.
*
* @tparam T Type of first key.
* @tparam Ts Types of other keys.
*
* @param key First key to index map by.
* @param keys Other keys.
*
* @return Map at given key.
*
* @throws std::out_of_range if key not found.
*/
template <typename T, typename... Ts>
[[nodiscard]] auto& at(const T& key, const Ts&... keys)
{
return elements_.at(key).at(keys...);
}

/**
* Checked access by key. Const overload.
*
* @tparam T Type of key.
*
* @param key Key to index map by.
*
* @return Map at given key.
*
* @throws std::out_of_range if key not found.
*/
template <typename T, typename... Ts>
[[nodiscard]] auto& at(const T& key, const Ts&... keys) const
{
return const_cast<poly_map*>(this)->at(key, keys...);
}

/**
* Map value
*/
struct poly_map_value {
/**
* Unchecked access by key.
* Set value in map.
*
* @tparam T Type of key.
* @param value Value to set.
*
* @param key Key to index map by.
*
* @return Map at given key.
*
* @throws std::out_of_range if key not found.
* @throws std::bad_alloc or the exception thrown by the assigned value's constructor.
*/
template <typename T>
auto& operator[](const T& key)
{
return elements_[key];
}

/**
* Iterate over map element by given visitor.
*
* @param visitor Visitor with overloads for keys types.
*/
template <typename V>
void for_each(V&& visitor)
{
elements_.for_each(std::forward<V>(visitor));
}

/**
* Iterate over map element by given visitor. Const overload.
*
* @param visitor Visitor with overloads for keys types.
*/
template <typename V>
void for_each(V&& visitor) const
void set(T&& value)
{
const_cast<poly_map*>(this)->for_each(std::forward<V>(visitor));
value_ = std::forward<T>(value);
}

/**
* Tests if map has no elements.
*/
[[nodiscard]] bool empty() const noexcept { return elements_.empty(); }

/**
* Returns number of elements.
*/
[[nodiscard]] std::size_t size() const noexcept { return elements_.size(); }

/**
* Removes elements from map.
*/
void clear() noexcept { elements_.elements_.clear(); }

/**
* Tests if given path of keys is in map.
*
* @param key Key to search for.
* @param keys List of keys to search for.
*/
template <typename T, typename... Ts>
[[nodiscard]] bool contains(const T& key, const Ts&... keys) const
{
return elements_.contains(key, keys...);
}

private:
poly_map_element elements_;
};

/**
* Map value
*/
struct poly_map_value {
std::any value_;

/**
* Get value from map.
*
Expand All @@ -171,18 +52,20 @@ struct poly_map_value {
* Tests if value has been set.
*/
[[nodiscard]] bool empty() const noexcept { return !value_.has_value(); }

private:
std::any value_;
};

/**
* Child map
* Polymorphic map
*
* @tparam Keys Types of keys.
* @tparam Key First type of key.
* @tparam Keys Other optional types of keys.
*/
template <typename... Keys>
struct poly_map<Keys...>::poly_map_element {
std::map<std::variant<Keys...>, poly_map_element> elements_;
poly_map_value value_;

template <typename Key, typename... Keys>
class poly_map {
public:
/**
* Assign value to map.
*
Expand All @@ -193,16 +76,14 @@ struct poly_map<Keys...>::poly_map_element {
template <typename T>
auto& operator=(T&& v)
{
value_.value_ = std::forward<T>(v);
value_.set(std::forward<T>(v));

return *this;
}

/**
* Checked access by key.
*
* @tparam T Type of key.
*
* @param key Key to index map by.
*
* @return Map at given key.
Expand All @@ -218,9 +99,6 @@ struct poly_map<Keys...>::poly_map_element {
/**
* Checked access by list of keys.
*
* @tparam T Type of first key.
* @tparam Ts Types of other keys.
*
* @param key First key to index map by.
* @param keys Other keys.
*
Expand All @@ -229,15 +107,28 @@ struct poly_map<Keys...>::poly_map_element {
* @throws std::out_of_range if key not found.
*/
template <typename T, typename... Ts>
[[nodiscard]] poly_map_element& at(const T& key, const Ts&... keys)
[[nodiscard]] poly_map& at(const T& key, const Ts&... keys)
{
return elements_.at(key).at(keys...);
}

/**
* Unchecked access by key.
* Checked access by key. Const overload.
*
* @tparam T Type of key.
* @param key Key to index map by.
*
* @return Map at given key.
*
* @throws std::out_of_range if key not found.
*/
template <typename T, typename... Ts>
[[nodiscard]] auto& at(const T& key, const Ts&... keys) const
{
return const_cast<poly_map*>(this)->at(key, keys...);
}

/**
* Unchecked access by key.
*
* @param key Key to index map by.
*
Expand Down Expand Up @@ -295,7 +186,7 @@ struct poly_map<Keys...>::poly_map_element {
template <typename V>
void for_each(V&& visitor) const
{
const_cast<poly_map_element*>(this)->for_each(std::forward<V>(visitor));
const_cast<poly_map*>(this)->for_each(std::forward<V>(visitor));
}

/**
Expand Down Expand Up @@ -330,7 +221,7 @@ struct poly_map<Keys...>::poly_map_element {
template <typename T>
[[nodiscard]] bool contains(const T& key) const
{
return elements_.find(key) != elements_.end();
return elements_.find(key) != elements_.cend();
}

/**
Expand All @@ -348,6 +239,28 @@ struct poly_map<Keys...>::poly_map_element {

return elements_.at(key).contains(keys...);
}

/**
* Access map element at next level
*
* @pre Map must not be empty: !empty().
*
* @return Reference to map.
*/
poly_map& map() noexcept { return elements_.begin()->second; }

/**
* Access map element at next level. Const overload.
*
* @pre Map must not be empty: !empty().
*
* @return Const reference to map.
*/
const poly_map& map() const noexcept { return elements_.cbegin()->second; }

private:
std::map<std::variant<Key, Keys...>, poly_map> elements_;
poly_map_value value_;
};

} // namespace msd
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror --coverage -fsanitize=undefined")

# Testing framework
include(FetchContent)
if (NOT googletest_POPULATED)
Expand Down
37 changes: 37 additions & 0 deletions tests/poly_map_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,40 @@ TEST_F(PolyMapTest, contains)
EXPECT_FALSE(const_map.at(1).at(2).contains(2));
EXPECT_TRUE(const_map.at(1).at(2).contains(3.1, 4.2));
}

template <typename T>
bool recursive_find(poly_map_type& m, const T& value)
{
if (m.contains(value)) {
return true;
}

if (m.empty()) {
return false;
}

return recursive_find(m.map(), value);
}

template <typename T>
bool recursive_find(const poly_map_type& m, const T& value)
{
if (m.contains(value)) {
return true;
}

if (m.empty()) {
return false;
}

return recursive_find(m.map(), value);
}

TEST_F(PolyMapTest, recursion)
{
EXPECT_TRUE(recursive_find(map, "f"));
EXPECT_FALSE(recursive_find(map, 1.1));

EXPECT_TRUE(recursive_find(const_map, "f"));
EXPECT_FALSE(recursive_find(const_map, 1.1));
}

0 comments on commit 42c27aa

Please sign in to comment.