// MIT License // // Copyright (c) 2020, The Regents of the University of California, // through Lawrence Berkeley National Laboratory (subject to receipt of any // required approvals from the U.S. Dept. of Energy). All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #pragma once // For std::decay #include namespace tim { namespace utility { template struct transient_function; // intentionally not defined /// \struct tim::utility::transient_function /// \brief A light-weight alternative to std::function. Pass any callback - including /// capturing lambdas - cheaply and quickly as a function argument /// /// - No instantiation of called function at each call site /// - Simple to use - use transient_function<...> as the function argument /// - Low cost, cheap setup, one indirect function call to invoke /// - No risk of dynamic allocation (unlike std::function) /// - Not persistent: synchronous calls only /// template struct transient_function { using dispatch_type = RetT (*)(void*, Args...); using target_function_reference = RetT(Args...); // dispatch_type() is instantiated by the transient_function constructor, // which will store a pointer to the function in m_dispatch. template static RetT dispatcher(void* target, Args&&... args) { return (*static_cast(target))(std::forward(args)...); } transient_function() = default; ~transient_function() = default; transient_function(transient_function&&) noexcept = default; transient_function& operator=(transient_function&&) noexcept = default; template transient_function(Tp&& target) : m_dispatch(&dispatcher>) , m_target(&target) {} // Specialize for reference-to-function, to ensure that a valid pointer is stored. transient_function(target_function_reference target) : m_dispatch(dispatcher) { static_assert( sizeof(void*) == sizeof(target), "It will not be possible to pass functions by reference on this platform. " "Please use explicit function pointers i.e. foo(target) -> foo(&target)"); m_target = static_cast(target); } // Specialize for reference-to-function, to ensure that a valid pointer is stored. transient_function& operator=(target_function_reference target) { if(this == &target) return *this; static_assert( sizeof(void*) == sizeof(target), "It will not be possible to pass functions by reference on this platform. " "Please use explicit function pointers i.e. foo(target) -> foo(&target)"); m_dispatch = dispatcher; m_target = static_cast(target); return *this; } RetT operator()(Args&&... args) const { return m_dispatch(m_target, std::forward(args)...); } private: // A pointer to the static function that will call the wrapped invokable object dispatch_type m_dispatch = nullptr; void* m_target = nullptr; // pointer to the invokable object }; // } // namespace utility // namespace scope { // //--------------------------------------------------------------------------------------// // /// \struct tim::scope::destructor /// \brief provides an object which can be returned from functions that will execute /// the lambda provided during construction when it is destroyed /// struct transient_destructor { template transient_destructor(FuncT&& _func) : m_functor(std::forward(_func)) {} // delete copy operations transient_destructor(const transient_destructor&) = delete; transient_destructor& operator=(const transient_destructor&) = delete; // allow move operations transient_destructor(transient_destructor&& rhs) noexcept = default; transient_destructor& operator=(transient_destructor&& rhs) noexcept = default; ~transient_destructor() { m_functor(); } private: utility::transient_function m_functor = []() {}; }; } // namespace scope // } // namespace tim