Last active
August 24, 2023 08:42
-
-
Save foonathan/023ff0fe923c6b0312dfc15e17ebb595 to your computer and use it in GitHub Desktop.
Revisions
-
foonathan revised this gist
May 18, 2017 . No changes.There are no files selected for viewing
-
foonathan created this gist
May 18, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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)); }; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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); }