Skip to content

Instantly share code, notes, and snippets.

@Nekrolm
Last active August 6, 2019 15:04
Show Gist options
  • Save Nekrolm/ec96ec2c6a85c269b6f1e5b3ce9d6b14 to your computer and use it in GitHub Desktop.
Save Nekrolm/ec96ec2c6a85c269b6f1e5b3ce9d6b14 to your computer and use it in GitHub Desktop.
FP Lenses simple implementation (C++17)
#include <functional>
#include <iostream>
template<class ObjT, class ValueT>
using setter_t = std::function<ObjT(ObjT, ValueT)>;
template<class ObjT, class ValueT>
using getter_t = std::function<ValueT(ObjT)>;
template <class ObjT, class ValueT>
struct Lens{
using setter_t = ::setter_t<ObjT, ValueT>;
using getter_t = ::getter_t<ObjT, ValueT>;
Lens(getter_t get, setter_t set) : set{set}, get{get} {}
template<class Getter, class Setter>
Lens(Getter&& g, Setter&& s) : Lens(getter_t(std::forward<Getter>(g)),
setter_t(std::forward<Setter>(s))) {
static_assert (std::is_invocable_r_v<ValueT, Getter, ObjT>, "Getter is not invocable with corresponding args");
static_assert (std::is_invocable_r_v<ObjT, Setter, ObjT, ValueT>, "Setter is not invocable with corresponding args");
}
setter_t set;
getter_t get;
};
template<class ObjOuter, class ObjInner, class ValueInner>
Lens<ObjOuter, ValueInner> compose(Lens<ObjOuter, ObjInner> outer, Lens<ObjInner, ValueInner> inner)
{
return { [outer, inner](ObjOuter obj){ return inner.get(outer.get(obj));},
[outer, inner](ObjOuter obj, ValueInner val){ return outer.set(obj, inner.set(outer.get(obj), val));}
};
}
template <class F>
struct func_traits;
template <class R, class... Args>
struct func_traits<std::function<R(Args...)>>
{
using return_type = R;
};
template <class Getter, class Setter>
auto make_lens(Getter&& g, Setter&& s)
{
//We don't want to deal with that messy zoo of pointer-to-function, functors, lambdas & etc.
//Use C++17 template deduction: cast Getter/Setter to corresponding std::funciton and handle only one case!
using ValueT = typename func_traits<decltype (std::function{std::declval<Getter>()})>::return_type;
using ObjT = typename func_traits<decltype (std::function{std::declval<Setter>()})>::return_type;
return Lens<ObjT, ValueT>{std::forward<Getter>(g), std::forward<Setter>(s)};
}
//-----------
struct X {
int v = 5;
};
int getter(const X& x) { return 5;}
int main(){
Lens L = make_lens([](X x){return x.v;},
[](X x, int v){ return X{v};});
Lens L2 = make_lens(getter,
[](X x, int v){ return X{v};});
auto f = std::function([](int x) -> int { return x;});
X x{7};
std::cout << L.get(L.set(x, 9));
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment