/**************************************************************************************************************************** Function.h This library provides function templates to better support C++ functional programming across platforms. Based on Vlpp library (https://github.com/vczh-libraries/Vlpp) and Marcus Rugger functional-vlpp library (https://github.com/marcusrugger/functional-vlpp) Built by Khoi Hoang (https://github.com/khoih-prog/functional-vlpp) Licensed under MIT license Original author Vczh Library++ 3.0 Developer: Zihan Chen(vczh) Framework::Basic Classes: Func :Function object Functions: Curry :: (A->B) -> A -> B :Currying Combine :: (A->B) -> (A->C) -> (B->C->D) -> (A->D) :Combine multiple functors using an operator Version: 1.0.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/02/2019 Initial coding, testing and supporting AVR architecture 1.0.1 K Hoang 01/03/2020 Add support for STM32 and all other architectures. 1.0.2 K Hoang 21/02/2021 Clear compiler warnings *****************************************************************************************************************************/ #pragma once #ifndef VCZH_FUNCTION #define VCZH_FUNCTION #include #include "Basic.h" #include "Pointer.h" namespace vl { /*********************************************************************** vl::Func ***********************************************************************/ template class Func { }; namespace internal_invokers { template class Invoker : public Object { public: virtual R Invoke(TArgs&& ...args) = 0; }; //------------------------------------------------------ template class StaticInvoker : public Invoker { protected: R(*function)(TArgs ...args); public: StaticInvoker(R(*_function)(TArgs...)) : function(_function) { } R Invoke(TArgs&& ...args)override { return function(ForwardValue(args)...); } }; //------------------------------------------------------ template class MemberInvoker : public Invoker { protected: C* sender; R(C::*function)(TArgs ...args); public: MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args)) : sender(_sender) , function(_function) { } R Invoke(TArgs&& ...args)override { return (sender->*function)(ForwardValue(args)...); } }; //------------------------------------------------------ template class ObjectInvoker : public Invoker { protected: C function; public: ObjectInvoker(const C& _function) : function(_function) { } R Invoke(TArgs&& ...args)override { return function(ForwardValue(args)...); } }; //------------------------------------------------------ template class ObjectInvoker : public Invoker { protected: C function; public: ObjectInvoker(const C& _function) : function(_function) { } void Invoke(TArgs&& ...args)override { function(ForwardValue(args)...); } }; } /// A type representing a function reference. /// The return type. /// Types of parameters. template class Func : public Object { protected: Ptr> invoker; public: typedef R FunctionType(TArgs...); typedef R ResultType; /// Create a null function reference. Func() { } /// Copy a function reference. /// The function reference to copy. Func(const Func& function) : invoker(function.invoker) { } /// Create a reference using a function pointer. /// The function pointer. Func(R(*function)(TArgs...)) { invoker = new internal_invokers::StaticInvoker(function); } /// Create a reference using a method. /// Type of the class that has the method. /// The object that has the method. /// The function pointer. template Func(C* sender, R(C::*function)(TArgs...)) { invoker = new internal_invokers::MemberInvoker(sender, function); } /// Create a reference using a function object. /// Type of the function object. /// The function object. It could be a lambda expression. template Func(const C& function) { invoker = new internal_invokers::ObjectInvoker(function); } /// Invoke the function. /// Returns the function result. /// Arguments to invoke the function. R operator()(TArgs ...args)const { return invoker->Invoke(ForwardValue(args)...); } Func& operator=(const Func& function) { invoker = function.invoker; return *this; } Func& operator=(const Func&& function) { invoker = MoveValue(function.invoker); return *this; } bool operator==(const Func& function)const { return invoker == function.invoker; } bool operator!=(const Func& function)const { return invoker != function.invoker; } /// Test is the reference a null reference. /// Returns true if it is not a null reference. operator bool()const { return invoker; } }; /*********************************************************************** vl::function_lambda::LambdaRetriveType ***********************************************************************/ namespace function_lambda { template struct LambdaRetriveType { typedef vint Type; typedef vint FunctionType; typedef vint ResultType; }; template struct FunctionObjectRetriveType { typedef typename LambdaRetriveType::Type Type; typedef typename LambdaRetriveType::FunctionType FunctionType; typedef typename LambdaRetriveType::ResultType ResultType; typedef typename LambdaRetriveType::ParameterTypes ParameterTypes; }; template struct LambdaRetriveType { typedef Func Type; typedef R(FunctionType)(TArgs...); typedef R ResultType; }; template struct LambdaRetriveType { typedef Func Type; typedef R(FunctionType)(TArgs...); typedef R ResultType; }; template struct FunctionObjectRetriveType { typedef Func Type; typedef R(FunctionType)(TArgs...); typedef R ResultType; }; /// Create a function reference to a function object or a lambda expression, with all type information /// automatically inferred. You can use the macro called "LAMBDA" to refer to this function. /// Type of the function object or the lambda expression. /// The function reference. /// The function object or the lambda expression. template typename LambdaRetriveType::Type Lambda(T functionObject) { return functionObject; } /// Create a function reference to a function pointer, with all type information /// automatically inferred. You can use the macro called "FUNCTION" to refer to this function. /// Type of the function pointer. /// The function reference. /// The function pointer. template typename FunctionObjectRetriveType::Type ConvertToFunction(T functionObject) { return functionObject; } #define LAMBDA vl::function_lambda::Lambda #define FUNCTION vl::function_lambda::ConvertToFunction #define FUNCTION_TYPE(T) typename vl::function_lambda::FunctionObjectRetriveType::Type #define FUNCTION_RESULT_TYPE(T) typename vl::function_lambda::FunctionObjectRetriveType::ResultType } // namespace function_lambda /*********************************************************************** vl::function_binding::Binding ***********************************************************************/ namespace function_binding { template struct Binding { }; template struct CR { typedef const T& Type; }; template struct CR { typedef T& Type; }; template struct CR { typedef const T& Type; }; template struct CR { typedef const T& Type; }; template struct Binding { typedef R FunctionType(T0, TArgs...); typedef R CurriedType(TArgs...); typedef T0 FirstParameterType; class Binder : public Object { protected: Func target; T0 firstArgument; public: Binder(const Func& _target, T0 _firstArgument) : target(_target), firstArgument(ForwardValue(_firstArgument)) { } R operator()(TArgs ...args)const { return target(firstArgument, args...); } }; class Currier : public Object { protected: Func target; public: Currier(const Func& _target) : target(_target) { } Func operator()(T0 firstArgument)const { return Binder(target, firstArgument); } }; }; } // namespace function_binding /// Currize a function. Currizing means to create a new function whose argument is the first argument /// of the original function. Calling this function will return another function reference whose arguments is all /// remain arguments of the original function. Calling the returned function will call the original function. /// Type of the function. /// The currized function. /// The function pointer to currize. template Func::CurriedType>(typename function_binding::Binding::FirstParameterType)> Curry(T* function) { return typename function_binding::Binding::Currier(function); } /// Currize a function. Currizing means to create a new function whose argument is the first argument /// of the original function. Calling this function will return another function reference whose arguments is all /// remain arguments of the original function. Calling the returned function will call the original function. /// Type of the function. /// The currized function. /// The function reference to currize. template Func::CurriedType>(typename function_binding::Binding::FirstParameterType)> Curry(const Func& function) { return typename function_binding::Binding::Currier(function); } /*********************************************************************** vl::function_combining::Combining ***********************************************************************/ namespace function_combining { template class Combining { }; template class Combining : public Object { protected: Func function1; Func function2; Func converter; public: typedef R1 FirstFunctionType(TArgs...); typedef R2 SecondFunctionType(TArgs...); typedef R ConverterFunctionType(R1, R2); typedef R FinalFunctionType(TArgs...); Combining(const Func& _function1, const Func& _function2, const Func& _converter) : function1(_function1), function2(_function2), converter(_converter) { } R operator()(TArgs&& ...args)const { return converter(function1(ForwardValue(args)...), function2(ForwardValue(args)...)); } }; } // namespace function_combining /// Combine two functions with a converter function. The two functions to combine should have the same argument types. /// The converter function will use the return values of the two function to calculate the final value. /// Type of the first function. /// Type of the second function. /// Type of the converter function. /// A new function whose argument list are the same of the two functions to provide. Calling this function /// will call function1, function2 and converter in order to calculate the final value. /// The converter function. /// The first function. /// The second function. template Func::FinalFunctionType> Combine(Func converter, Func function1, Func function2) { return function_combining::Combining(function1, function2, converter); } /// Use the converter function to create a combiner, who will receive two function and use to create /// a combined function. This function assumes the result types of the two provided function in the future are the same, /// and the converter function will not change the result type. /// Type of the two functions to combine. /// The combiner. /// The converter function. template Func(Func, Func)> Combiner(const Func::ResultType(typename Func::ResultType, typename Func::ResultType)>& converter) { typedef typename Func::ResultType R; return Curry(Func, Func, Func)>(Combine)(converter); } } // namespace vl #endif // VCZH_FUNCTION