Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ if(BUILD_TESTING AND ${KDALGORITHMS_BUILD_TEST})
add_executable(tst_kdalgorithms
src/kdalgorithms.h

src/bits/copy_or_move_iterators.h
src/bits/read_iterator_wrapper.h
src/bits/filter.h
src/bits/has_reserve_trait.h
src/bits/insert_wrapper.h
src/bits/is_const_method.h
src/bits/is_detected.h
src/bits/method_tests.h
src/bits/operators.h
src/bits/return_type_trait.h
src/bits/shared.h
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ Lots of Examples
In this document you will find lots of examples. More, however, may be found in the
unit tests.

Qt data types
-------------
A few of the functions (copy, filtered, and transform) need additional support for Qt
data types. Therefore, to us kdalgorithms together with Qt, you must include kdalgorithms_qt.h
rather than kdalgorithms.h

Algorithms
==========

Expand Down Expand Up @@ -183,7 +189,7 @@ auto toString = [] (int i) { return QString::number(i); }
QVector<QString> result = kdalgorithms::transformed<QVector>(ints, toString);
```

Finally ther is a version which does an inline transform:
Finally there is a version which does an inline transform:

```
std::vector<int> ints{1,2,3};
Expand All @@ -192,6 +198,29 @@ kdalgorithms::transform(ints, square);
// ints = {1,4,9}
```

transformed on maps
-------------------
The transform methods can unfortunately not automatically deduce the
Comment thread
jesperkdab marked this conversation as resolved.
type of the result container - even when it is just the same type as the input container,
therefore two additional methods are provided:

```
std::map<int, std::string> map{{1, "abc"}, {2, "def"}, {3, "hij"}, {4, "klm"}};
auto doubleKeys = [](const auto& item) {
return std::make_pair(item.first * 2, item.second);
};
auto result = kdalgorithms::transformed_to_same_container(map, doubleKeys);
// result = {2, "abc"}, {4, "def"}, {6, "hij"}, {8, "klm"}
Comment thread
jesperkdab marked this conversation as resolved.
```


```
std::map<int, std::string> map{{1, "abc"}, {2, "def"}, {3, "hij"}, {4, "klm"}};
auto result = kdalgorithms::transformed_with_new_return_type<std::map<std::string, int>>(
map, [](auto item) { return std::make_pair(item.second, item.first); });
// result = {{"abc", 1}, {"def", 2}, {"hij", 3}, {"klm", 4}};
```

See [std::transform](https://en.cppreference.com/w/cpp/algorithm/transform) for the algorithm from the standard.

<a name="reverse">reverse / reversed</a>
Expand Down
10 changes: 6 additions & 4 deletions src/bits/filter.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "has_reserve_trait.h"
#include "insert_wrapper.h"
#include "method_tests.h"
#include "read_iterator_wrapper.h"
#include "shared.h"
#include "to_function_object.h"
#include <algorithm>
Expand All @@ -14,12 +16,12 @@ namespace detail {
{
ResultContainer result;
#if __cplusplus >= 201703L
if constexpr (traits::has_reserve_method_v<ResultContainer>) {
if constexpr (detail::has_reserve_method_v<ResultContainer>) {
result.reserve(input.size());
}
#endif
auto range = copy_or_move_iterators(std::forward<InputContainer>(input));
std::copy_if(range.begin, range.end, std::back_inserter(result),
auto range = read_iterator_wrapper(std::forward<InputContainer>(input));
std::copy_if(range.begin, range.end, detail::insert_wrapper(result),
std::forward<UnaryPredicate>(predicate));
return result;
}
Expand Down
31 changes: 0 additions & 31 deletions src/bits/has_reserve_trait.h

This file was deleted.

88 changes: 88 additions & 0 deletions src/bits/insert_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once

#include "method_tests.h"
#include <iterator>
#include <utility>

namespace kdalgorithms {
namespace detail {
// similar to std::inserter, but calls insert with just one argument - required for QSet in Qt5
template <typename T>
class single_arg_inserter
{
public:
using value_type = typename T::value_type;
single_arg_inserter(T &set)
: m_set(set)
{
}

single_arg_inserter &operator++() { return *this; }
single_arg_inserter &operator*() { return *this; }
single_arg_inserter &operator=(const value_type &value)
{
m_set.insert(value);
return *this;
}

private:
T &m_set;
};

template <class Container, typename T>
using has_push_back = decltype(std::declval<Container &>().push_back(std::declval<T>()));

template <class Container, typename T>
using has_insert = decltype(std::declval<Container &>().insert(std::declval<T>()));

template <typename Container>
auto insert_wrapper(
Container &c,
std::enable_if_t<
detail::is_detected_v<has_push_back, Container, typename Container::value_type>, int> =
0)
{
return std::back_inserter(c);
}

template <typename Container>
auto insert_wrapper(
Container &c,
std::enable_if_t<
detail::is_detected_v<has_insert, Container, typename Container::value_type>, int> = 0)
{
return single_arg_inserter<Container>(c);
}

// similar to std::inserter, but for QMap/QHash which usually accept a value in their operator=
template <typename T>
class qmap_inserter
{
public:
qmap_inserter(T &map)
: m_map(map)
{
}

qmap_inserter &operator++() { return *this; }
qmap_inserter &operator*() { return *this; }
qmap_inserter &
operator=(const std::pair<typename T::key_type, typename T::mapped_type> &pair)
{
m_map.insert(pair.first, pair.second);
return *this;
}

private:
T &m_map;
};

template <typename Container>
auto insert_wrapper(Container &c,
std::enable_if_t<detail::has_keyValueBegin_v<Container>, int> = 0)
{
return qmap_inserter<Container>(c);
}

} // namespace detail
} // namespace kdalgorithms
26 changes: 26 additions & 0 deletions src/bits/is_detected.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <type_traits>

// Inspired from std::experimental::is_detected
// See https://en.cppreference.com/w/cpp/experimental/is_detected

namespace kdalgorithms {
namespace detail {
template <class AlwaysVoid, template <class...> class, typename... Args>
struct detector : std::false_type
{
};

template <template <class...> class Op, typename... Args>
struct detector<std::void_t<Op<Args...>>, Op, Args...> : std::true_type
{
};

template <template <class...> class Op, typename... Args>
using is_detected = detector<void, Op, Args...>;

template <template <class...> class Op, typename... Args>
static constexpr auto is_detected_v = detector<void, Op, Args...>::value;
}
}
28 changes: 28 additions & 0 deletions src/bits/method_tests.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "is_detected.h"
namespace kdalgorithms {

namespace detail {
namespace tests {
template <typename Container>
using has_reserve = decltype(std::declval<Container>().reserve(42));

template <typename Container>
using has_keyValueBegin = decltype(std::declval<Container &>().keyValueBegin());

template <typename Container>
using has_keyValueBegin = decltype(std::declval<Container &>().keyValueBegin());
}

template <typename Container>
constexpr bool has_reserve_method_v = detail::is_detected_v<tests::has_reserve, Container>;

template <typename Container>
using has_keyValueBegin = detail::is_detected<tests::has_keyValueBegin, Container>;

template <typename Container>
constexpr bool has_keyValueBegin_v = detail::is_detected_v<tests::has_keyValueBegin, Container>;

} // namespace detail
} // namespace kdalgorithms
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "method_tests.h"
#include <iterator>
#include <tuple>
#include <type_traits>
Expand All @@ -21,27 +22,35 @@ namespace detail {

// used for l-value containers
template <typename Container>
auto copy_or_move_iterators_helper(const Container &container, std::true_type)
auto read_iterator_wrapper_helper(const Container &container, std::true_type, std::false_type)
{
return make_iteratorPair(container.cbegin(), container.cend());
}

// used for r-value containers
template <typename Container>
auto copy_or_move_iterators_helper(Container &&container, std::false_type)
auto read_iterator_wrapper_helper(Container &&container, std::false_type, std::false_type)
{
return make_iteratorPair(std::make_move_iterator(container.begin()),
std::make_move_iterator(container.end()));
}

// Used for QHash and QMap containers
template <typename Container, typename T>
auto read_iterator_wrapper_helper(Container &&container, T, std::true_type)
{
return make_iteratorPair(container.constKeyValueBegin(), container.constKeyValueEnd());
}
}

// Depending on the value type of container - either l-value or r-value
// return iterators that either copy elements out or that move elements out.
template <typename Container>
auto copy_or_move_iterators(Container &&container)
auto read_iterator_wrapper(Container &&container)
{
return detail::copy_or_move_iterators_helper(
std::forward<Container>(container), typename std::is_lvalue_reference<Container>::type());
return detail::read_iterator_wrapper_helper(
std::forward<Container>(container), typename std::is_lvalue_reference<Container>::type(),
detail::has_keyValueBegin<Container>());
}

} // namespace kdalgorithms
17 changes: 16 additions & 1 deletion src/bits/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,23 @@ using remove_cvref_t = typename std::remove_const<typename std::remove_reference
#endif

// -------------------- general helpers --------------------
namespace detail {
template <typename T, typename = void>
struct ValueTypeHelper
{
using value_type = typename T::value_type;
};

// QMap doesn't have the value_type typedef, so we have to build that ourselves.
template <typename T>
struct ValueTypeHelper<T, std::void_t<typename T::mapped_type>>
{
using value_type = std::pair<const typename T::key_type, typename T::mapped_type>;
};
}

template <typename Container>
using ValueType = typename remove_cvref_t<Container>::value_type;
using ValueType = typename detail::ValueTypeHelper<remove_cvref_t<Container>>::value_type;

// -------------------- concepts --------------------
#if __cplusplus >= 202002L
Expand Down
2 changes: 1 addition & 1 deletion src/bits/to_function_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace detail {
}

template <typename F>
auto to_function_object(F &&f)
decltype(auto) to_function_object(F &&f)
{
return detail::to_function_object_helper(std::forward<F>(f),
typename std::is_member_pointer<F>::type{});
Expand Down
Loading