Skip to content

Instantly share code, notes, and snippets.

@rolandschulz
Last active July 25, 2018 04:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rolandschulz/629387fa63a430b74b9ba5edf768df5a to your computer and use it in GitHub Desktop.
Save rolandschulz/629387fa63a430b74b9ba5edf768df5a to your computer and use it in GitHub Desktop.
#include <unordered_map>
#include <vector>
#include <iostream>
#include <type_traits>
#include <memory>
//iteator pair - very crude range
template <typename I1, typename I2=I1>
struct iter_pair : std::pair<I1, I2>
{
using std::pair<I1, I2>::pair;
I1 begin() { return this->first; }
I2 end() { return this->second; }
};
//from pair of iterators
template<typename T1, typename T2>
iter_pair<T1,T2> make_iter_pair(T1&& a, T2&& b) {
return {std::forward<T1>(a), std::forward<T2>(b)};
}
//from container
template<typename T>
auto make_iter_pair(T& a) -> iter_pair<typename T::iterator> {
return {a.begin(), a.end()};
}
//from const constainer
template<typename T>
auto make_iter_pair(const T& a) -> iter_pair<typename T::const_iterator> {
return {a.begin(), a.end()};
}
//transform iterator. operator* returns callable fn applied to iterator's reference
template<typename It, typename Fn>
class transform_iter : public It {
public:
using reference = typename std::result_of<Fn(typename It::reference)>::type;
transform_iter(const It &it, Fn fn) : It(it), fn_(fn) {}
reference operator*() const { return fn_(It::operator*()); }
reference operator[](typename It::difference_type n) const { return fn_(It::operator[](n)); }
private:
//Optimization opportunity: Inheriting from functor (with wrapper for function pointer), would allow
//empty base class optimization and avoid wasting space for 0-size lambda.
/*[[no_unique_address]]*/ Fn fn_;
};
template<typename Fn, typename ...A>
#if __cplusplus >= 201703L
//slightly nicer error in clang
using enable_if_is_invocable_t = typename std::enable_if_t<std::is_invocable_v<Fn, A...>>;
#else
using enable_if_is_invocable_t = typename std::result_of<Fn(A...)>::type;
#endif
template<typename T, typename Fn, typename=enable_if_is_invocable_t<Fn, typename T::const_reference>>
auto make_transform_iter_pair(const T& a, Fn fn) -> iter_pair<transform_iter<typename T::const_iterator, Fn>> {
return {{a.begin(), fn}, {a.end(), fn}};
}
template<typename T, typename Fn, typename=enable_if_is_invocable_t<Fn, typename T::reference>,
typename=typename std::enable_if<!std::is_const<T>::value>::type>
auto make_transform_iter_pair(T& a, Fn fn) -> iter_pair<transform_iter<typename T::iterator, Fn>> {
return {{a.begin(), fn}, {a.end(), fn}};
}
//Testing
template<typename T>
void printRange(T r)
{
for(const auto &x: r) {
std::cout<<x<<"\n";
}
}
static const std::string& getKey(const std::pair<const std::string, std::string>& p) {
return p.first;
}
[[maybe_unused]]static std::string& getValue(std::pair<const std::string, std::string>& p) {
return p.second;
}
int main() {
//-- Test data begin
std::unordered_map<std::string, std::string> u = {
{"RED","#FF0000"},
{"GREEN","#00FF00"},
{"BLUE","#0000FF"}
};
const auto cu = u; //const copy
std::vector<std::string> v = {"a", "b", "c"};
const auto cv = v; //const copy
//-- Test data end
printRange(make_transform_iter_pair(u, [](const std::pair<const std::string, std::string>& x){return x.first;}));
//Using C++14
//printRange(make_transform_iter_pair(u, [](const auto& x){return x.first;})); //C++14
//Function rather than lambda:
//printRange(make_transform_iter_pair(u, &getKey));
printRange(make_transform_iter_pair(cu, &getKey));
//Test with const container:
//printRange(make_transform_iter_pair(cu, [](std::pair<std::string, std::string> x){return x.first;}));
//Testing with incorrect lambda:
//printRange(make_transform_iter_pair(u, [](std::pair<std::string, int> x){return x.first;}));
//Testing with vector:
printRange(v);
//Same as:
//printRange(make_iter_pair(v));
//Testing modification through iterator:
//for (auto &s: make_transform_iter_pair(u, &getValue)) s = "BLA";
//Using lambda:
//for (auto &s: make_transform_iter_pair(u, [](auto& x) -> auto& {return x.second;})) s = "BLA";
//Tesing [] operator
//std::cout << make_transform_iter_pair(v, [](const auto& x){return x.size();}).begin()[1];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment