/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __FN_MULTI_FUNCTION_BUILDER_HH__ #define __FN_MULTI_FUNCTION_BUILDER_HH__ /** \file * \ingroup fn * * This file contains several utilities to create multi-functions with less redundant code. */ #include #include "FN_multi_function.hh" namespace blender::fn { /** * Generates a multi-function with the following parameters: * 1. single input (SI) of type In1 * 2. single output (SO) of type Out1 * * This example creates a function that adds 10 to the incoming values: * CustomMF_SI_SO fn("add 10", [](int value) { return value + 10; }); */ template class CustomMF_SI_SO : public MultiFunction { private: using FunctionT = std::function, MutableSpan)>; FunctionT function_; public: CustomMF_SI_SO(StringRef name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature = this->get_builder(name); signature.single_input("In1"); signature.single_output("Out1"); } template CustomMF_SI_SO(StringRef name, ElementFuncT element_fn) : CustomMF_SI_SO(name, CustomMF_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, VSpan in1, MutableSpan out1) { mask.foreach_index([&](uint i) { new ((void *)&out1[i]) Out1(element_fn(in1[i])); }); }; } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { VSpan in1 = params.readonly_single_input(0); MutableSpan out1 = params.uninitialized_single_output(1); function_(mask, in1, out1); } }; /** * Generates a multi-function with the following parameters: * 1. single input (SI) of type In1 * 2. single input (SI) of type In2 * 3. single output (SO) of type Out1 */ template class CustomMF_SI_SI_SO : public MultiFunction { private: using FunctionT = std::function, VSpan, MutableSpan)>; FunctionT function_; public: CustomMF_SI_SI_SO(StringRef name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature = this->get_builder(name); signature.single_input("In1"); signature.single_input("In2"); signature.single_output("Out1"); } template CustomMF_SI_SI_SO(StringRef name, ElementFuncT element_fn) : CustomMF_SI_SI_SO(name, CustomMF_SI_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, VSpan in1, VSpan in2, MutableSpan out1) { mask.foreach_index([&](uint i) { new ((void *)&out1[i]) Out1(element_fn(in1[i], in2[i])); }); }; } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { VSpan in1 = params.readonly_single_input(0); VSpan in2 = params.readonly_single_input(1); MutableSpan out1 = params.uninitialized_single_output(2); function_(mask, in1, in2, out1); } }; /** * Generates a multi-function with the following parameters: * 1. single input (SI) of type In1 * 2. single input (SI) of type In2 * 3. single input (SI) of type In3 * 4. single output (SO) of type Out1 */ template class CustomMF_SI_SI_SI_SO : public MultiFunction { private: using FunctionT = std::function, VSpan, VSpan, MutableSpan)>; FunctionT function_; public: CustomMF_SI_SI_SI_SO(StringRef name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature = this->get_builder(name); signature.single_input("In1"); signature.single_input("In2"); signature.single_input("In3"); signature.single_output("Out1"); } template CustomMF_SI_SI_SI_SO(StringRef name, ElementFuncT element_fn) : CustomMF_SI_SI_SI_SO(name, CustomMF_SI_SI_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, VSpan in1, VSpan in2, VSpan in3, MutableSpan out1) { mask.foreach_index( [&](uint i) { new ((void *)&out1[i]) Out1(element_fn(in1[i], in2[i], in3[i])); }); }; } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { VSpan in1 = params.readonly_single_input(0); VSpan in2 = params.readonly_single_input(1); VSpan in3 = params.readonly_single_input(2); MutableSpan out1 = params.uninitialized_single_output(3); function_(mask, in1, in2, in3, out1); } }; /** * Generates a multi-function with the following parameters: * 1. single mutable (SM) of type Mut1 */ template class CustomMF_SM : public MultiFunction { private: using FunctionT = std::function)>; FunctionT function_; public: CustomMF_SM(StringRef name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature = this->get_builder(name); signature.single_mutable("Mut1"); } template CustomMF_SM(StringRef name, ElementFuncT element_fn) : CustomMF_SM(name, CustomMF_SM::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, MutableSpan mut1) { mask.foreach_index([&](uint i) { element_fn(mut1[i]); }); }; } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { MutableSpan mut1 = params.single_mutable(0); function_(mask, mut1); } }; /** * Generates a multi-function that converts between two types. */ template class CustomMF_Convert : public MultiFunction { public: CustomMF_Convert() { std::string name = CPPType::get().name() + " to " + CPPType::get().name(); MFSignatureBuilder signature = this->get_builder(std::move(name)); signature.single_input("Input"); signature.single_output("Output"); } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { VSpan inputs = params.readonly_single_input(0); MutableSpan outputs = params.uninitialized_single_output(1); for (uint i : mask) { new ((void *)&outputs[i]) To(inputs[i]); } } }; /** * A multi-function that outputs the same value every time. The value is not owned by an instance * of this function. The caller is responsible for destructing and freeing the value. */ class CustomMF_GenericConstant : public MultiFunction { private: const CPPType &type_; const void *value_; template friend class CustomMF_Constant; public: CustomMF_GenericConstant(const CPPType &type, const void *value); void call(IndexMask mask, MFParams params, MFContext context) const override; uint32_t hash() const override; bool equals(const MultiFunction &other) const override; }; /** * A multi-function that outputs the same array every time. The array is not owned by in instance * of this function. The caller is responsible for destructing and freeing the values. */ class CustomMF_GenericConstantArray : public MultiFunction { private: GSpan array_; public: CustomMF_GenericConstantArray(GSpan array); void call(IndexMask mask, MFParams params, MFContext context) const override; }; /** * Generates a multi-function that outputs a constant value. */ template class CustomMF_Constant : public MultiFunction { private: T value_; public: template CustomMF_Constant(U &&value) : value_(std::forward(value)) { MFSignatureBuilder signature = this->get_builder("Constant"); std::stringstream ss; ss << value_; signature.single_output(ss.str()); } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { MutableSpan output = params.uninitialized_single_output(0); mask.foreach_index([&](uint i) { new (&output[i]) T(value_); }); } uint32_t hash() const override { return DefaultHash{}(value_); } bool equals(const MultiFunction &other) const override { const CustomMF_Constant *other1 = dynamic_cast(&other); if (other1 != nullptr) { return value_ == other1->value_; } const CustomMF_GenericConstant *other2 = dynamic_cast( &other); if (other2 != nullptr) { const CPPType &type = CPPType::get(); if (type == other2->type_) { return type.is_equal((const void *)&value_, other2->value_); } } return false; } }; } // namespace blender::fn #endif /* __FN_MULTI_FUNCTION_BUILDER_HH__ */