Skip to content

Instantly share code, notes, and snippets.

@Happy-Ferret
Forked from foonathan/borrow.cpp
Created May 2, 2022 22:20
Show Gist options
  • Save Happy-Ferret/1a66e2670a6d13a92394e3ff021a1df9 to your computer and use it in GitHub Desktop.
Save Happy-Ferret/1a66e2670a6d13a92394e3ff021a1df9 to your computer and use it in GitHub Desktop.

Revisions

  1. @foonathan foonathan revised this gist May 18, 2017. No changes.
  2. @foonathan foonathan created this gist May 18, 2017.
    25 changes: 25 additions & 0 deletions borrow.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    #include <iostream>

    #include "borrow_checker.hpp"

    int main()
    {
    auto i = 42;

    // borrow `i` under name `ref`
    borrow_var(ref, i)
    {
    // ++i; - error
    // *ref = 0; - error
    std::cout << *ref << ' ' << i << '\n';
    };

    // mutable borrow `i` under name `ref`
    mut_borrow_var(ref, i)
    {
    // auto var = i; - error
    *ref = 42;
    };

    ++i;
    }
    180 changes: 180 additions & 0 deletions borrow_checker.hpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,180 @@
    #ifndef BORROW_CHECKER_HPP_INCLUDED
    #define BORROW_CHECKER_HPP_INCLUDED

    #include <initializer_list>
    #include <new>
    #include <type_traits>

    namespace detail
    {
    template <typename T>
    class ref
    {
    public:
    explicit ref(T& obj)
    : ptr_(&obj) {}

    T& operator*() const noexcept
    {
    return *ptr_;
    }

    T* operator->() const noexcept
    {
    return ptr_;
    }

    private:
    T* ptr_;
    };

    template <typename Object, typename Expression>
    struct borrow_lambda_invoker
    {
    Object& object;
    Expression& expression;

    template <typename Lambda>
    auto call_lambda(int, const Lambda& l)
    -> decltype(l(object, ref<Expression>(expression)))
    {
    return l(object, ref<Expression>(expression));
    }

    template <typename Lambda>
    auto call_lambda(short, const Lambda& l)
    -> decltype(l({}, ref<Expression>(expression)))
    {
    return l({}, ref<Expression>(expression));
    }

    template <typename Lambda>
    void operator=(const Lambda& l)
    {
    call_lambda(0, l);
    }
    };

    struct error_variable_borrowed_as_mutable
    {
    error_variable_borrowed_as_mutable(const error_variable_borrowed_as_mutable&) = delete;
    };

    template <typename T>
    struct destructive_move_holder
    {
    std::aligned_storage_t<sizeof(T), alignof(T)> storage;
    bool should_destroy;

    template <typename ... Args>
    destructive_move_holder(Args&&... args)
    : should_destroy(true)
    {
    ::new(&storage) T(std::forward<Args>(args)...);
    }

    template <typename U>
    destructive_move_holder(std::initializer_list<U> list)
    : should_destroy(true)
    {
    ::new(&storage) T(std::move(list));
    }

    destructive_move_holder(const destructive_move_holder&) = delete;
    destructive_move_holder& operator=(const destructive_move_holder&) = delete;

    ~destructive_move_holder()
    {
    if (should_destroy)
    get().~T();
    }

    T& get()
    {
    void* mem = &storage;
    return *static_cast<T*>(mem);
    }

    T&& move()
    {
    should_destroy = false;
    return std::move(get());
    }
    };

    template <typename T>
    struct destructive_move_lambda_invoker
    {
    destructive_move_holder<T> holder;

    template <typename ... Args>
    destructive_move_lambda_invoker(Args&&... args)
    : holder(std::forward<Args>(args)...) {}

    template <typename U>
    destructive_move_lambda_invoker(std::initializer_list<U> list)
    : holder(std::move(list)) {}

    template <typename Lambda>
    void operator=(const Lambda& lambda)
    {
    lambda(holder, holder.get());
    }
    };
    }

    template <typename T>
    using ref = detail::ref<const T>;

    template <typename T>
    using mut_ref = detail::ref<T>;

    template <typename T>
    ref<T> borrow(const T& obj)
    {
    return ref<T>(obj);
    }

    template <typename T>
    mut_ref<T> mut_borrow(T& obj)
    {
    return mut_ref<T>(obj);
    }

    #define _borrow_lambda_invoker(Obj, ...) \
    detail::borrow_lambda_invoker<std::remove_reference_t<decltype(Obj)>, \
    const std::remove_reference_t<decltype(__VA_ARGS__)>>{Obj, __VA_ARGS__}


    #define _mut_borrow_lambda_invoker(Obj, ...) \
    detail::borrow_lambda_invoker<std::remove_reference_t<decltype(Obj)>, \
    std::remove_reference_t<decltype(__VA_ARGS__)>>{Obj, __VA_ARGS__}

    #define _borrow_lambda(Name, Obj) \
    [&]([[gnu::unused]] const auto& Obj, const auto& Name)

    #define _mut_borrow_lambda(Name, Obj) \
    [&]([[gnu::unused]] const detail::error_variable_borrowed_as_mutable& Obj, const auto& Name)

    #define _borrow(Name, Obj, ...) \
    _borrow_lambda_invoker(Obj, __VA_ARGS__) = _borrow_lambda(Name, Obj)

    #define _mut_borrow(Name, Obj, ...) \
    _mut_borrow_lambda_invoker(Obj, __VA_ARGS__) = _mut_borrow_lambda(Name, Obj)

    #define borrow_var(Name, Obj) _borrow(Name, Obj, Obj)
    #define borrow_expr(Name, Obj, ...) _borrow(Name, Obj, __VA_ARGS__)
    #define borrow_elem(Name, Obj, I) _borrow(Name, Obj, Obj[I])

    #define mut_borrow_var(Name, Obj) _mut_borrow(Name, Obj, Obj)
    #define mut_borrow_expr(Name, Obj, ...) _mut_borrow(Name, Obj, __VA_ARGS__)
    #define mut_borrow_elem(Name, Obj, I) _mut_borrow(Name, Obj, Obj[I])

    #define destructive_moveable(Type, Name, ...) \
    detail::destructive_move_lambda_invoker<Type>{__VA_ARGS__} \
    = [&]([[gnu::unused]] auto& _holder_##Name, auto& Name)

    #define destructive_move(Name) \
    _holder_##Name.move()

    #endif // BORROW_CHECKER_HPP_INCLUDED
    36 changes: 36 additions & 0 deletions borrow_func.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    #include <iostream>
    #include <vector>

    #include "borrow_checker.hpp"

    void func_borrow(ref<std::vector<int>> vec)
    {
    for (auto& el : *vec)
    std::cout << el << ' ';
    std::cout << '\n';
    }

    void func_mut_borrow(mut_ref<std::vector<int>> vec)
    {
    vec->push_back(4);
    }

    void func_destructive_move(std::vector<int> vec)
    {
    std::cout << "Now it's mine!\n";
    }

    int main()
    {
    destructive_moveable(std::vector<int>, vec, {1, 2, 3})
    {
    vec.push_back(42);

    func_borrow(borrow(vec));
    func_mut_borrow(mut_borrow(vec));
    func_borrow(borrow(vec));

    func_destructive_move(destructive_move(vec));
    };
    }

    19 changes: 19 additions & 0 deletions borrow_vec.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    #include <iostream>
    #include <vector>

    #include "borrow_checker.hpp"

    int main()
    {
    std::vector<int> vec = {1, 2, 3};

    // borrow `vec[0]` as ref, locking `vec`
    borrow_expr(ref, vec, vec[0])
    {
    //*ref = 0; - error
    //vec.push_back(4); - error
    std::cout << vec.size() << ' ' << *ref << '\n';
    };

    vec.push_back(4);
    }