Skip to content

Instantly share code, notes, and snippets.

@Nekrolm
Last active August 6, 2019 15:04

Revisions

  1. Nekrolm revised this gist Aug 6, 2019. No changes.
  2. Nekrolm renamed this gist Aug 6, 2019. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. Nekrolm created this gist Aug 6, 2019.
    84 changes: 84 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    #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;
    }