From 7b6ad41ea67e10ce9849b5a7ae4411b272c3d43c Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Fri, 14 Aug 2015 15:23:30 +0200 Subject: ucoo/utils: add Function --- ucoo/utils/function.hh | 139 +++++++++++++++++++++++++++++++++++++++ ucoo/utils/test/Makefile | 3 +- ucoo/utils/test/test_function.cc | 123 ++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 ucoo/utils/function.hh create mode 100644 ucoo/utils/test/test_function.cc (limited to 'ucoo') diff --git a/ucoo/utils/function.hh b/ucoo/utils/function.hh new file mode 100644 index 0000000..d8bbefd --- /dev/null +++ b/ucoo/utils/function.hh @@ -0,0 +1,139 @@ +#ifndef ucoo_utils_function_hh +#define ucoo_utils_function_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2015 Nicolas Schodet +// +// 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. +// +// }}} +#include "ucoo/common.hh" + +namespace ucoo { + +/// Type used to compute maximum size of a FunctionStore. +union FunctionStoreTypes +{ + /// A FunctionStore can store a function pointer... + template + using Function = Res (*) (Args...); + Function function; + /// ...or a bound member function (object pointer + member function + /// pointer). + template + struct BoundMember + { + T *object; + Res (T::*member_pointer) (Args...); + }; + class SomeClass; + BoundMember bound_member; +}; + +/// Store what is needed to call a function. +union FunctionStore +{ + void *access () { return data_; } + const void *access () const { return data_; } + template + T &access () + { + static_assert (sizeof (T) <= sizeof (data_), + "function pointer too big"); + return *reinterpret_cast (access ()); + } + template + const T &access () const + { + static_assert (sizeof (T) <= sizeof (data_), + "function pointer too big"); + return *reinterpret_cast (access ()); + } + private: + FunctionStoreTypes unused_; + char data_[sizeof (FunctionStoreTypes)]; +}; + +template +class Function; + +/// Store a reference to a callable element in an object which type only +/// depends on the callable signature. +/// +/// This is a lightweight and limited version of std::function. +template +class Function +{ + public: + /// Construct an empty function, can not be called. + Function () : call_ (nullptr) { } + /// Construct from a function pointer. + Function (Res (*function_pointer) (Args...)) + { + assert (function_pointer); + using F = FunctionStoreTypes::Function; + F &f = store_.access (); + f = function_pointer; + call_ = &Function::call_function; + } + /// Construct with a bound member function. + template + Function (T *object, Res (T::*member_pointer) (Args...)) + { + assert (object && member_pointer); + using F = FunctionStoreTypes::BoundMember; + F &f = store_.access (); + f.object = object; + f.member_pointer = member_pointer; + call_ = &Function::call_bound_member; + } + /// Reset to empty function. + void reset () { call_ = nullptr; } + /// Call the stored function. + Res operator() (Args... args) const + { + assert (call_); + return call_ (args..., store_); + } + /// Test whether there is a stored function. + operator bool () const + { + return call_; + } + private: + template + static Res call_function (Args... args, const FunctionStore &store) + { + const F &f = store.access (); + return f (args...); + } + template + static Res call_bound_member (Args... args, const FunctionStore &store) + { + const F &f = store.access (); + return (f.object->*f.member_pointer) (args...); + } + private: + Res (*call_) (Args..., const FunctionStore &store); + FunctionStore store_; +}; + +} // namespace ucoo + +#endif // ucoo_utils_function_hh diff --git a/ucoo/utils/test/Makefile b/ucoo/utils/test/Makefile index e490582..e1eb3b2 100644 --- a/ucoo/utils/test/Makefile +++ b/ucoo/utils/test/Makefile @@ -1,11 +1,12 @@ BASE = ../../.. TARGETS = host stm32f4 -PROGS = test_fifo test_crc +PROGS = test_fifo test_crc test_function stm32f4_PROGS = test_delay test_fifo_SOURCES = test_fifo.cc test_delay_SOURCES = test_delay.cc test_crc_SOURCES = test_crc.cc +test_function_SOURCES = test_function.cc MODULES = ucoo/utils ucoo/base/test ucoo/hal/usb diff --git a/ucoo/utils/test/test_function.cc b/ucoo/utils/test/test_function.cc new file mode 100644 index 0000000..91f0246 --- /dev/null +++ b/ucoo/utils/test/test_function.cc @@ -0,0 +1,123 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2015 Nicolas Schodet +// +// 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. +// +// }}} +#include "ucoo/utils/function.hh" +#include "ucoo/arch/arch.hh" +#include "ucoo/base/test/test.hh" + +int +sum (int a, int b) +{ + return a + b; +} + +struct Accum +{ + int cur; + Accum (int base) : cur (base) { } + void acc (int val) { cur += val; } +}; + +struct AbstractReduce +{ + virtual void acc (int val) = 0; +}; + +struct ConcreteAccum : public AbstractReduce +{ + int cur; + ConcreteAccum (int base) : cur (base) { } + void acc (int val) override { cur += val; } +}; + +bool __attribute__ ((noinline)) +test_function (const ucoo::Function &f) +{ + return f (1, 2) == 3; +} + +bool __attribute__ ((noinline)) +test_function (const ucoo::Function &f, int &cur) +{ + f (2); + return cur == 3; +} + +int +main (int argc, const char **argv) +{ + ucoo::arch_init (argc, argv); + ucoo::TestSuite tsuite ("function"); + { + ucoo::Test test (tsuite, "function pointer"); + ucoo::Function f (sum); + if (!test_function (f)) + test.fail (); + } + { + ucoo::Test test (tsuite, "bound member"); + Accum a (1); + ucoo::Function f (&a, &Accum::acc); + if (!test_function (f, a.cur)) + test.fail (); + } + { + ucoo::Test test (tsuite, "bound virtual member"); + ConcreteAccum a (1); + AbstractReduce *aa = &a; + ucoo::Function f (aa, &AbstractReduce::acc); + if (!test_function (f, a.cur)) + test.fail (); + } + { + ucoo::Test test (tsuite, "copy"); + ucoo::Function f (sum); + ucoo::Function f2 (f); + if (!test_function (f2)) + test.fail (); + } + { + ucoo::Test test (tsuite, "assignment"); + ucoo::Function f (sum); + ucoo::Function f2; + f2 = f; + if (!test_function (f2)) + test.fail (); + } + { + ucoo::Test test (tsuite, "function assignment"); + ucoo::Function f; + f = sum; + if (!f || !test_function (f)) + test.fail (); + } + { + ucoo::Test test (tsuite, "nullptr assignment"); + ucoo::Function f (sum); + f.reset (); + if (f) + test.fail (); + } + return tsuite.report () ? 0 : 1; +} + -- cgit v1.2.3