diff --git a/.gitlab/ci/_global.gitlab-ci.yml b/.gitlab/ci/_global.gitlab-ci.yml index aab5d745367d22052f82c6e3ef144680a822cd45..94e5658ff6adc8e07036d3d59ea39a68fbddc4bf 100644 --- a/.gitlab/ci/_global.gitlab-ci.yml +++ b/.gitlab/ci/_global.gitlab-ci.yml @@ -9,6 +9,14 @@ variables: GIT_SSL_NO_VERIFY: 1 DEBIAN_FRONTEND: noninteractive +# See https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS + when: never + - if: $CI_COMMIT_BRANCH + default: image: nvidia/cuda:12.2.0-devel-ubuntu22.04 before_script: diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index 6bfae0be1e31a89d27413677fa4cdc4612561333..a4579e2951ccbafc4335ae428c62eba94c0757e5 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -88,8 +88,7 @@ build:ubuntu_python: - virtualenv venv - source venv/bin/activate # Numpy dependancy for unit test - - python3 -m pip install numpy - - export AIDGE_INSTALL=`pwd`/install + - python3 -m pip install -r requirements.txt - python3 -m pip install . artifacts: expire_in: 1 week @@ -147,8 +146,7 @@ build:windows_python: - virtualenv venv - venv\Scripts\Activate.ps1 # Numpy dependancy for unit test - - python -m pip install numpy - - $env:AIDGE_INSTALL = "$pwd" + "install" + - python -m pip install -r requirements.txt - python -m pip install . artifacts: expire_in: 1 week diff --git a/CMakeLists.txt b/CMakeLists.txt index 40d8837f41bdc0d8dfd7eac1c5960064967f1efb..f8dbe375e217020a4c4570bd67c1b466e6593130 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ file(READ "${CMAKE_SOURCE_DIR}/project_name.txt" project) message(STATUS "Project name: ${project}") message(STATUS "Project version: ${version}") -# Note : project name is {project} and python module name is also {project} +# Note : project name is {project} and python module name is also {project} set(module_name _${project}) # target name @@ -57,7 +57,7 @@ if (PYBIND) # Handles Python + pybind11 headers dependencies target_link_libraries(${module_name} - PUBLIC + PUBLIC pybind11::pybind11 PRIVATE Python::Python @@ -101,8 +101,8 @@ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(EXPORT ${project}-targets FILE "${project}-targets.cmake" DESTINATION ${INSTALL_CONFIGDIR} -# COMPONENT ${module_name} -) +# COMPONENT ${module_name} +) #Create a ConfigVersion.cmake file include(CMakePackageConfigHelpers) @@ -136,4 +136,4 @@ export(EXPORT ${project}-targets if(TEST) enable_testing() add_subdirectory(unit_tests) -endif() \ No newline at end of file +endif() diff --git a/aidge_core/__init__.py b/aidge_core/__init__.py index ad18a8ef1b23625dcb52951f52c43adc4222c997..c65dcc6cfc4be8825d1213854014718fb7170854 100644 --- a/aidge_core/__init__.py +++ b/aidge_core/__init__.py @@ -8,3 +8,4 @@ http://www.eclipse.org/legal/epl-2.0. SPDX-License-Identifier: EPL-2.0 """ from aidge_core.aidge_core import * # import so generated by PyBind +from aidge_core.export import ExportNode diff --git a/aidge_core/export/__init__.py b/aidge_core/export/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..00b44121d68af06171525fdf953bf50e53328421 --- /dev/null +++ b/aidge_core/export/__init__.py @@ -0,0 +1 @@ +from .node_export import * diff --git a/aidge_core/export/node_export.py b/aidge_core/export/node_export.py new file mode 100644 index 0000000000000000000000000000000000000000..980cb05a5814b7476d64757353e393ad6130218b --- /dev/null +++ b/aidge_core/export/node_export.py @@ -0,0 +1,61 @@ +import aidge_core + +from abc import ABC, abstractmethod + + +class ExportNode(ABC): + """Abstract class to interface node with export generation. + """ + + @abstractmethod + def __init__(self, aidge_node: aidge_core.Node) -> None: + """Create ExportNode and retieve attirubtes from ``aidge_node``: + + - name: aidge Node name + - attributes: dictionnary of attributes of the aidge Operator linked to the node, attributes name follow aidge naming convention + - parameters: List of parameters node, order in the list is the same as the one defined by the aidge operator + + """ + super().__init__() + self.node = aidge_node + self.operator = aidge_node.get_operator() + self.name = self.node.name() + self.attributes = {} # Attributes are auto fetched from aidge operators + if isinstance(self.operator, aidge_core.Attributes): + for attr_name in self.operator.get_attrs_name(): + self.attributes[attr_name] = self.operator.get_attr(attr_name) + + # rename is_leaf ? + self.is_last = len(self.node.get_children()) == 0 + + + self.inputs = [] + self.outputs = [] + self.inputs_dims = [] + self.outputs_dims = [] + + for idx, parent_node in enumerate(self.node.get_parents()): + self.inputs.append(parent_node) + if parent_node is not None: + self.inputs_dims.append(self.operator.input(idx).dims()) + else: + self.inputs_dims.append(None) + + for idx, child_node in enumerate(self.node.get_children()): + self.outputs.append(child_node) + + # Dirty hot fix, change it quickly + self.outputs_dims.append(self.operator.output(0).dims()) + + @abstractmethod + def export(self, export_folder:str, list_configs:list): + """Define how to export the node definition. + """ + pass + + @abstractmethod + def forward(self, list_actions:list): + """Define how to generate code to perform a forward pass. + """ + pass + diff --git a/aidge_core/unit_tests/test_operator_binding.py b/aidge_core/unit_tests/test_operator_binding.py index fc60f52274162155f8f891bf86c22c9a13b241f4..7bd1e730a973810db89aa786b52fa05c53c43590 100644 --- a/aidge_core/unit_tests/test_operator_binding.py +++ b/aidge_core/unit_tests/test_operator_binding.py @@ -102,5 +102,30 @@ class test_operator_binding(unittest.TestCase): genOp.get_operator().compute_output_dims() self.assertListEqual(genOp.get_operator().output(0).dims(), in_dims) + def test_set_impl(self): + + class PythonCustomImpl(aidge_core.OperatorImpl): + """Dummy implementation to test that C++ call python code + """ + def __init__(self, op: aidge_core.Operator): + aidge_core.OperatorImpl.__init__(self, op) # Recquired to avoid type error ! + self.idx = 0 + + def forward(self): + """Increment idx attribute on forward. + """ + self.idx += 1 + + generic_node = aidge_core.GenericOperator("Relu", 1, 1, 1, name="myReLu") + generic_op = generic_node.get_operator() + customImpl = PythonCustomImpl(generic_op) + + generic_op.forward() # Do nothing, no implementation set + generic_op.set_impl(customImpl) + generic_op.forward() # Increment idx + self.assertEqual(customImpl.idx, 1) + + + if __name__ == '__main__': unittest.main() diff --git a/include/aidge/aidge.hpp b/include/aidge/aidge.hpp index c3f97f96e6b797afca7a28928f717691ae998185..e708c168421216fa249f26eee1f2b2eb80b588fd 100644 --- a/include/aidge/aidge.hpp +++ b/include/aidge/aidge.hpp @@ -38,6 +38,7 @@ #include "aidge/operator/MetaOperator.hpp" #include "aidge/operator/MetaOperatorDefs.hpp" #include "aidge/operator/Operator.hpp" +#include "aidge/operator/Pad.hpp" #include "aidge/operator/Producer.hpp" #include "aidge/operator/ReLU.hpp" #include "aidge/operator/Softmax.hpp" diff --git a/include/aidge/backend/OperatorImpl.hpp b/include/aidge/backend/OperatorImpl.hpp index 453e30a8636d86794c96723350bff615af090e3e..19f0837504016f38ae96dd852bc6fa41b5ab53ba 100644 --- a/include/aidge/backend/OperatorImpl.hpp +++ b/include/aidge/backend/OperatorImpl.hpp @@ -18,11 +18,13 @@ #include "aidge/utils/Types.h" namespace Aidge { +class Operator; + class OperatorImpl { public: - - virtual void forward(){}; - virtual void backward(){}; + OperatorImpl(const Operator& op); + virtual void forward(); + virtual void backward(); /** * @brief Minimum amount of data from a specific input required by the @@ -31,13 +33,13 @@ public: * @param inputIdx Index of the input analysed. * @return std::size_t */ - virtual NbElts_t getNbRequiredData(const IOIndex_t inputIdx) const = 0; + virtual NbElts_t getNbRequiredData(const IOIndex_t inputIdx) const; // Amount of input data that cannot be overwritten during the execution. - virtual NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const = 0; + virtual NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const; // Memory required at an output for a given input size. - virtual NbElts_t getRequiredMemory(const IOIndex_t outputIdx, const std::vector<DimSize_t> &inputsSize) const = 0; + virtual NbElts_t getRequiredMemory(const IOIndex_t outputIdx, const std::vector<DimSize_t> &inputsSize) const; /** * @brief Total amount of consumed data from a specific input. @@ -45,7 +47,7 @@ public: * @param inputIdx Index of the input analysed. * @return DimSize_t */ - virtual NbElts_t getNbConsumedData(const IOIndex_t inputIdx) const = 0; + virtual NbElts_t getNbConsumedData(const IOIndex_t inputIdx) const; /** * @brief Total amount of produced data ready to be used on a specific output. @@ -53,15 +55,20 @@ public: * @param outputIdx Index of the output analysed. * @return DimSize_t */ - virtual NbElts_t getNbProducedData(const IOIndex_t outputIdx) const = 0; + virtual NbElts_t getNbProducedData(const IOIndex_t outputIdx) const; /** * @brief Update the Consummer Producer system by simulating the consumption and production of i/o * */ - virtual void updateConsummerProducer() = 0; + virtual void updateConsummerProducer(); virtual ~OperatorImpl() = default; + +protected: + const Operator &mOp; + std::vector<NbElts_t> mNbConsumedData; + std::vector<NbElts_t> mNbProducedData; }; } // namespace Aidge diff --git a/include/aidge/data/Tensor.hpp b/include/aidge/data/Tensor.hpp index 7422a52eb171ee6dae0e14ad67c0562295fe5d8c..58c434bccc7c8dd39a93c46ecf74c38d7d834d1a 100644 --- a/include/aidge/data/Tensor.hpp +++ b/include/aidge/data/Tensor.hpp @@ -477,13 +477,14 @@ class Tensor : public Data, if (dims().empty()) { return "{}"; } std::string res; std::size_t dim = 0; - std::size_t *dimVals = new std::size_t[nbDims()]; - for (std::size_t i = 0; i < nbDims(); ++i) { - dimVals[i] = 0; - } std::size_t counter = 0; - res += "{\n"; - if (nbDims()>=2){ + if (nbDims()>=2) { + std::size_t *dimVals = new std::size_t[nbDims()]; + for (std::size_t i = 0; i < nbDims(); ++i) { + dimVals[i] = 0; + } + // std::vector<std::size_t> dimVals = std::vector<std::size_t>(nbDims(), 0); + res += "{\n"; while (counter < mSize) { std::string spaceString = std::string((dim+1)<<1,' '); if (dim < nbDims()-2) { @@ -532,31 +533,35 @@ class Tensor : public Data, } res += "\n"; } + if (dim == 0) { + break; + } dimVals[dim--] = 0; dimVals[dim]++; } } - for(int i = static_cast<int>(dim); i>=0; --i) { + delete[] dimVals; + + for(int i = static_cast<int>(dim); i > 0; --i) { res += std::string((dim+1)<<1,' ') + "}\n"; } - }else{ + } else { + res += "{"; for (DimSize_t j = 0; j < dims()[0]; ++j) { switch (mDataType) { case DataType::Int32: - res += " " + std::to_string(static_cast<int *>(mImpl->rawPtr())[j]) + ((j < dims()[0]-1) ? "," : "\n"); + res += " " + std::to_string(static_cast<int *>(mImpl->rawPtr())[j]) + ((j < dims()[0]-1) ? "," : ""); break; case DataType::Float64: - res += " " + std::to_string(static_cast<double *>(mImpl->rawPtr())[j]) + ((j < dims()[0]-1) ? "," : "\n"); + res += " " + std::to_string(static_cast<double *>(mImpl->rawPtr())[j]) + ((j < dims()[0]-1) ? "," : ""); break; default: - res += " " + std::to_string(static_cast<float *>(mImpl->rawPtr())[j]) + ((j < dims()[0]-1) ? "," : "\n"); + res += " " + std::to_string(static_cast<float *>(mImpl->rawPtr())[j]) + ((j < dims()[0]-1) ? "," : ""); break; } } } - - res += "}"; return res; } diff --git a/include/aidge/graphRegex/GraphRegex.hpp b/include/aidge/graphRegex/GraphRegex.hpp index 83940cc7fc270e78a5f80db147267d5136c23936..a357462c2ac0153714bd85398058bf7c3085148e 100644 --- a/include/aidge/graphRegex/GraphRegex.hpp +++ b/include/aidge/graphRegex/GraphRegex.hpp @@ -21,7 +21,7 @@ class GraphRegex{ std::vector<std::string> mQuery; std::vector<std::shared_ptr<ConditionalInterpreter>> mAllTest; - std::map<std::string, std::function<bool(NodePtr)>&> mAllLambda; + std::map<std::string, std::function<bool(NodePtr)>> mAllLambda; public: GraphRegex(){}; diff --git a/include/aidge/operator/Add.hpp b/include/aidge/operator/Add.hpp index 57403270d44d66e87675a3cadb227342c0cacd91..65c7e8ce0e47bd470e2a1499a682ed2f2c8c2dbc 100644 --- a/include/aidge/operator/Add.hpp +++ b/include/aidge/operator/Add.hpp @@ -162,6 +162,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return NUM; } inline IOIndex_t nbDataInputs() const noexcept override final { return NUM; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input_0", "data_input_n"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <std::size_t NUM> diff --git a/include/aidge/operator/AvgPooling.hpp b/include/aidge/operator/AvgPooling.hpp index be15ceb66ce32b98bfafab4af4213eee163dfbf9..dfcd0d5b3b4d892f201485e85710d42cd5b71dba 100644 --- a/include/aidge/operator/AvgPooling.hpp +++ b/include/aidge/operator/AvgPooling.hpp @@ -157,18 +157,23 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <std::array<DimSize_t, 1>::size_type DIM> inline std::shared_ptr<Node> AvgPooling(const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1)) { - // FIXME: properly handle default w&b initialization in every cases static_assert(DIM<=MaxDim,"Too many kernel dimensions required by AvgPooling, not supported"); - auto avgPool = std::make_shared<Node>(std::make_shared<AvgPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name); - return avgPool; + return std::make_shared<Node>(std::make_shared<AvgPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name); } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> AvgPooling( DimSize_t const (&kernel_dims)[DIM], diff --git a/include/aidge/operator/BatchNorm.hpp b/include/aidge/operator/BatchNorm.hpp index 75c901a1f00d26cc8b65192815c6fe93575723f0..da7360c8ba3816cdfe1d2d00f80b08808a80f961 100644 --- a/include/aidge/operator/BatchNorm.hpp +++ b/include/aidge/operator/BatchNorm.hpp @@ -160,6 +160,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 5; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input", "scale", "shift", "mean", "variance"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <DimSize_t DIM> diff --git a/include/aidge/operator/Conv.hpp b/include/aidge/operator/Conv.hpp index c8e229cbb3815ae7bd24064e862dc407b327febd..b1e3e34b0eff681632d90cb8314ebd8c96722eec 100644 --- a/include/aidge/operator/Conv.hpp +++ b/include/aidge/operator/Conv.hpp @@ -177,6 +177,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 3; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input", "weight", "bias"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <std::array<DimSize_t, 1>::size_type DIM> @@ -195,6 +201,7 @@ inline std::shared_ptr<Node> Conv(DimSize_t in_channels, return conv; } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> Conv( DimSize_t in_channels, diff --git a/include/aidge/operator/ConvDepthWise.hpp b/include/aidge/operator/ConvDepthWise.hpp index 55a48a978f4bd515f31cff4feae79c3ab262b0e0..4caec2032a3c61529d452ae855f00c1da411af10 100644 --- a/include/aidge/operator/ConvDepthWise.hpp +++ b/include/aidge/operator/ConvDepthWise.hpp @@ -176,6 +176,12 @@ class ConvDepthWise_Op : public Operator, inline IOIndex_t nbInputs() const noexcept override final { return 3; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input", "weight", "bias"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <std::array<DimSize_t, 1>::size_type DIM> @@ -191,6 +197,7 @@ inline std::shared_ptr<Node> ConvDepthWise(const std::array<DimSize_t, DIM> &ker return convDW; } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> ConvDepthWise( DimSize_t const (&kernel_dims)[DIM], diff --git a/include/aidge/operator/FC.hpp b/include/aidge/operator/FC.hpp index 8dea03ca0b7cd9ce7543fa35d082bc5164365b7b..b949527c51b9330077dd3bd8f8b4bf1f1b9d719c 100644 --- a/include/aidge/operator/FC.hpp +++ b/include/aidge/operator/FC.hpp @@ -158,6 +158,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 3; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input", "weight", "bias"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; inline std::shared_ptr<Node> FC(DimSize_t out_channels, bool noBias = false, const std::string& name = "") { @@ -175,4 +181,4 @@ const char *const EnumStrings<Aidge::FCAttr>::data[] = {"OutChannels", "NoBias"}; } -#endif /* AIDGE_CORE_OPERATOR_FC_H_ */ \ No newline at end of file +#endif /* AIDGE_CORE_OPERATOR_FC_H_ */ diff --git a/include/aidge/operator/GenericOperator.hpp b/include/aidge/operator/GenericOperator.hpp index 83b9a932633deb822ad86c24b96e6e928b5e2be2..55ccbf1516fa79663d57e1e44bc4017bc5c8b843 100644 --- a/include/aidge/operator/GenericOperator.hpp +++ b/include/aidge/operator/GenericOperator.hpp @@ -168,9 +168,20 @@ class GenericOperator_Op void setBackend(const std::string & /*name*/) override { printf("setBackend: not available yet.\n"); } void setDatatype(const DataType & /*datatype*/) override { printf("setDatatype: not available yet.\n"); } - void forward() override final { printf("forward: not available yet.\n"); } - void backward() override final { printf("backward: not available yet.\n"); } - + void forward() override final { + if(mImpl){ + mImpl->forward(); + }else{ + printf("forward: No implementation is linked.\n"); + } + } + void backward() override final { + if(mImpl){ + mImpl->backward(); + }else{ + printf("backward: No implementation is linked.\n"); + } + } inline IOIndex_t nbInputs() const noexcept override final { return mNbIn; }; inline IOIndex_t nbDataInputs() const noexcept override final { return mNbDataIn; }; inline IOIndex_t nbOutputs() const noexcept override final { return mNbOut; }; diff --git a/include/aidge/operator/LeakyReLU.hpp b/include/aidge/operator/LeakyReLU.hpp index 7a6fc4cbb8648b04aa42158c34d022b11775b84c..bcdcbc7cabd8eda46a7c0c4930f317e562fb46a0 100644 --- a/include/aidge/operator/LeakyReLU.hpp +++ b/include/aidge/operator/LeakyReLU.hpp @@ -137,10 +137,15 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; inline std::shared_ptr<Node> LeakyReLU(float negativeSlope = 0.0f, const std::string& name = "") { - // FIXME: properly handle default w&b initialization in every cases return std::make_shared<Node>(std::make_shared<LeakyReLU_Op>(negativeSlope), name); } } diff --git a/include/aidge/operator/MatMul.hpp b/include/aidge/operator/MatMul.hpp index eec7072ff2739c80bb327f0e987e7d3712ba217e..eed1ec04535aa5896aa3d01a27d8023d37a42183 100644 --- a/include/aidge/operator/MatMul.hpp +++ b/include/aidge/operator/MatMul.hpp @@ -148,6 +148,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 2; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input", "weight"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; inline std::shared_ptr<Node> MatMul(DimSize_t out_channels, const std::string& name = "") { diff --git a/include/aidge/operator/MaxPooling.hpp b/include/aidge/operator/MaxPooling.hpp index bf802238c2dba8d13a0bb230750f3b882b6c09f5..bcf47f13cc34132f668ea1ffcb2c91ed6f06f44d 100644 --- a/include/aidge/operator/MaxPooling.hpp +++ b/include/aidge/operator/MaxPooling.hpp @@ -26,14 +26,15 @@ #include "aidge/utils/Types.h" namespace Aidge { -enum class MaxPoolingAttr { StrideDims, KernelDims }; +enum class MaxPoolingAttr { StrideDims, KernelDims, CeilMode }; template <DimIdx_t DIM> class MaxPooling_Op : public Operator, public Registrable<MaxPooling_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const MaxPooling_Op<DIM> &)>, public StaticAttributes<MaxPoolingAttr, std::array<DimSize_t, DIM>, - std::array<DimSize_t, DIM>> { + std::array<DimSize_t, DIM>, + bool> { private: // FIXME: change accessibility std::shared_ptr<Tensor> mInput = std::make_shared<Tensor>(); @@ -46,15 +47,18 @@ public: using Attributes_ = StaticAttributes<MaxPoolingAttr, std::array<DimSize_t, DIM>, - std::array<DimSize_t, DIM>>; + std::array<DimSize_t, DIM>, + bool>; template <MaxPoolingAttr e> using attr = typename Attributes_::template attr<e>; constexpr MaxPooling_Op(const std::array<DimSize_t, DIM> &kernel_dims, - const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1)) + const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), + bool ceil_mode = false) : Operator(Type), Attributes_(attr<MaxPoolingAttr::StrideDims>(stride_dims), - attr<MaxPoolingAttr::KernelDims>(kernel_dims)), + attr<MaxPoolingAttr::KernelDims>(kernel_dims), + attr<MaxPoolingAttr::CeilMode>(ceil_mode)), mOutput(std::make_shared<Tensor>()) { setDatatype(DataType::Float32); } @@ -93,9 +97,16 @@ public: if (!mInput->empty()) { std::array<DimSize_t, DIM + 2> outputDims = {}; + std::function<float(float)> roundingFunction; + if (this->template getAttr<MaxPoolingAttr::CeilMode>()) { + roundingFunction = [](float x) { return std::ceil(x); }; + } else { + roundingFunction = [](float x) { return std::floor(x); }; + } + for (std::size_t dim = 0; dim < this->template getAttr<MaxPoolingAttr::KernelDims>().size() ; ++dim) { outputDims[dim+2] = 1 + static_cast<DimSize_t>( - std::floor(static_cast<float>(mInput->dims()[dim+2] - + roundingFunction(static_cast<float>(mInput->dims()[dim+2] - this->template getAttr<MaxPoolingAttr::KernelDims>()[dim]) / static_cast<float>(this->template getAttr<MaxPoolingAttr::StrideDims>()[dim]))); } @@ -158,31 +169,38 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <std::array<DimSize_t, 1>::size_type DIM> inline std::shared_ptr<Node> MaxPooling(const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", - const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1)) { - // FIXME: properly handle default w&b initialization in every cases + const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), + bool ceil_mode=false) { static_assert(DIM<=MaxDim,"Too many kernel dimensions required by MaxPooling, not supported"); - auto avgPool = std::make_shared<Node>(std::make_shared<MaxPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name); - return avgPool; + return std::make_shared<Node>(std::make_shared<MaxPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims, ceil_mode), name); } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> MaxPooling( DimSize_t const (&kernel_dims)[DIM], const std::string& name = "", - const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1)) { + const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), + bool ceil_mode = false) { static_assert(DIM<=MaxDim,"Too many kernel dimensions required by MaxPooling, not supported"); - return MaxPooling(to_array(kernel_dims), name, stride_dims); + return MaxPooling(to_array(kernel_dims), name, stride_dims, ceil_mode); } } // namespace Aidge namespace { template <> -const char *const EnumStrings<Aidge::MaxPoolingAttr>::data[] = {"StrideDims", "KernelDims"}; +const char *const EnumStrings<Aidge::MaxPoolingAttr>::data[] = {"StrideDims", "KernelDims", "CeilMode"}; } #endif /* AIDGE_CORE_OPERATOR_MAXPOOLING_H_ */ diff --git a/include/aidge/operator/MetaOperator.hpp b/include/aidge/operator/MetaOperator.hpp index bb34fd9c7756f103d4f31f17f815309c925306b7..72058dfcba6e811a01a22e261208741879638cad 100644 --- a/include/aidge/operator/MetaOperator.hpp +++ b/include/aidge/operator/MetaOperator.hpp @@ -156,9 +156,11 @@ public: inline std::shared_ptr<Node> MetaOperator(const char *type, const std::shared_ptr<GraphView>& graph, - const std::string& name = "") + const std::string& name = "", + std::vector<NodePtr> inputNodes = std::vector<NodePtr>(), + std::vector<NodePtr> outputNodes = std::vector<NodePtr>()) { - return std::make_shared<Node>(std::make_shared<MetaOperator_Op>(type, graph), name); + return std::make_shared<Node>(std::make_shared<MetaOperator_Op>(type, graph, inputNodes, outputNodes), name); } } // namespace Aidge diff --git a/include/aidge/operator/MetaOperatorDefs.hpp b/include/aidge/operator/MetaOperatorDefs.hpp index df66cec7e1accfee1518378ce2e9697cdc7f91fb..73feb134837787ae8d0d280dd723182c9d21438b 100644 --- a/include/aidge/operator/MetaOperatorDefs.hpp +++ b/include/aidge/operator/MetaOperatorDefs.hpp @@ -26,21 +26,22 @@ inline std::shared_ptr<Node> PaddedConv(DimSize_t in_channels, const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), - const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0}, + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0), const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1)) { // Construct micro-graph - auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(padding_dims, PadBorderType::Constant, 0.0), (!name.empty()) ? name + "_pad" : ""); + auto pad = Pad<DIM>(padding_dims, (!name.empty()) ? name + "_pad" : "", PadBorderType::Constant, 0.0); auto conv = std::make_shared<Node>(std::make_shared<Conv_Op<static_cast<DimIdx_t>(DIM)>>(in_channels, out_channels, kernel_dims, stride_dims, dilation_dims), (!name.empty()) ? name + "_conv" : ""); // Need to specify the ordered list of input operators const std::vector<NodePtr> orderedInputNodes = {pad, conv}; - auto metaOp = std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedConv", Sequential({pad, conv}), orderedInputNodes), name); + auto metaOp = MetaOperator("PaddedConv", Sequential({pad, conv}), name, orderedInputNodes); addProducer(metaOp, 1, append(out_channels, append(in_channels, kernel_dims)), "w"); addProducer(metaOp, 2, {out_channels}, "b"); return metaOp; } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> PaddedConv( DimSize_t in_channels, @@ -48,76 +49,93 @@ inline std::shared_ptr<Node> PaddedConv( DimSize_t const (&kernel_dims)[DIM], const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), - const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0}, + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0), const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1)) { - return PaddedConv<DIM>(in_channels, out_channels, to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims); + return PaddedConv(in_channels, out_channels, to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims); } template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> PaddedConvDepthWise(DimSize_t in_channels, - DimSize_t out_channels, - const std::array<DimSize_t, DIM> &kernel_dims, +inline std::shared_ptr<Node> PaddedConvDepthWise(const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), - const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0}, + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0), const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1)) { // Construct micro-graph - auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(padding_dims, PadBorderType::Constant, 0.0), (!name.empty()) ? name + "_pad" : ""); - auto conv = std::make_shared<Node>(std::make_shared<ConvDepthWise_Op<static_cast<DimIdx_t>(DIM)>>(in_channels, out_channels, kernel_dims, stride_dims, dilation_dims), (!name.empty()) ? name + "_conv" : ""); + auto pad = Pad<DIM>(padding_dims, (!name.empty()) ? name + "_pad" : "", PadBorderType::Constant, 0.0); + auto conv = std::make_shared<Node>(std::make_shared<ConvDepthWise_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims, dilation_dims), (!name.empty()) ? name + "_conv" : ""); // Need to specify the ordered list of input operators const std::vector<NodePtr> orderedInputNodes = {pad, conv}; - auto metaOp = std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedConvDepthWise", Sequential({pad, conv}), orderedInputNodes), name); - addProducer(metaOp, 1, append(out_channels, append(in_channels, kernel_dims)), "w"); - addProducer(metaOp, 2, {out_channels}, "b"); + auto metaOp = MetaOperator("PaddedConvDepthWise", Sequential({pad, conv}), name, orderedInputNodes); + addProducer(metaOp, 1, std::array<DimSize_t,0>({}), "w"); + addProducer(metaOp, 2, std::array<DimSize_t,0>({}), "b"); return metaOp; } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> PaddedConvDepthWise( - DimSize_t in_channels, - DimSize_t out_channels, DimSize_t const (&kernel_dims)[DIM], const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), - const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0}, + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0), const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1)) { - return PaddedConvDepthWise<DIM>(in_channels, out_channels, to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims); + return PaddedConvDepthWise(to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims); } template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> PaddedAvgPooling(DimSize_t in_channels, - DimSize_t out_channels, - const std::array<DimSize_t, DIM> &kernel_dims, +inline std::shared_ptr<Node> PaddedAvgPooling(const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), - const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0}) + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0)) { auto graph = Sequential({ Pad<DIM>(padding_dims, (!name.empty()) ? name + "_pad" : ""), - AvgPooling_Op<DIM>(kernel_dims, (!name.empty()) ? name + "_avgpooling" : "", stride_dims) + AvgPooling(kernel_dims, (!name.empty()) ? name + "_avgpooling" : "", stride_dims) }); - return std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedAvgPooling", graph), name); + return MetaOperator("PaddedAvgPooling", graph, name); +} + +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction +template <DimSize_t DIM> +inline std::shared_ptr<Node> PaddedAvgPooling( + DimSize_t const (&kernel_dims)[DIM], + const std::string& name = "", + const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0)) +{ + return PaddedAvgPooling(to_array(kernel_dims), name, stride_dims, padding_dims); } template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> PaddedMaxPooling(DimSize_t in_channels, - DimSize_t out_channels, - const std::array<DimSize_t, DIM> &kernel_dims, +inline std::shared_ptr<Node> PaddedMaxPooling(const std::array<DimSize_t, DIM> &kernel_dims, const std::string& name = "", const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), - const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0}) + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0), + bool ceil_mode = false) { auto graph = Sequential({ Pad<DIM>(padding_dims, (!name.empty()) ? name + "_pad" : ""), - MaxPooling_Op<DIM>(kernel_dims, (!name.empty()) ? name + "_maxpooling" : "", stride_dims) + MaxPooling(kernel_dims, (!name.empty()) ? name + "_maxpooling" : "", stride_dims, ceil_mode) }); - return std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedMaxPooling", graph), name); + return MetaOperator("PaddedMaxPooling", graph, name); +} + +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction +template <DimSize_t DIM> +inline std::shared_ptr<Node> PaddedMaxPooling( + DimSize_t const (&kernel_dims)[DIM], + const std::string& name = "", + const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1), + const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0), + bool ceil_mode= false) +{ + return PaddedMaxPooling(to_array(kernel_dims), name, stride_dims, padding_dims, ceil_mode); } } // namespace Aidge diff --git a/include/aidge/operator/Operator.hpp b/include/aidge/operator/Operator.hpp index e3544171de9b97a2795f1d936adfeff341bd32dc..903b6362adf3db0c867dc419086e0cb6ddaa65c7 100644 --- a/include/aidge/operator/Operator.hpp +++ b/include/aidge/operator/Operator.hpp @@ -26,7 +26,7 @@ namespace Aidge { class Operator : public std::enable_shared_from_this<Operator> { protected: - std::unique_ptr<OperatorImpl> mImpl; // implementation of the operator + std::shared_ptr<OperatorImpl> mImpl; // implementation of the operator std::map<std::string, std::shared_ptr<Hook>> mHooks; private: @@ -76,6 +76,14 @@ public: virtual void setBackend(const std::string& name) = 0; virtual void setDatatype(const DataType& datatype) = 0; + /** + * @brief Set the a new OperatorImpl to the Operator + * + */ + void setImpl(std::shared_ptr<OperatorImpl> impl){ + mImpl = impl; + } + /** * @brief Minimum amount of data from a specific input for one computation pass. * @param inputIdx Index of the input analysed. @@ -116,6 +124,12 @@ public: virtual IOIndex_t nbInputs() const noexcept = 0; virtual IOIndex_t nbDataInputs() const noexcept = 0; virtual IOIndex_t nbOutputs() const noexcept = 0; + static const std::vector<std::string> getInputsName(){ + return {}; + } + static const std::vector<std::string> getOutputsName(){ + return {}; + } }; } // namespace Aidge diff --git a/include/aidge/operator/Pad.hpp b/include/aidge/operator/Pad.hpp index deae0e6b8c5a91e5c10e7655549a4e46ac90eb0b..cbebb16e1e24501b0ea371fb45211047f6e2b5e7 100644 --- a/include/aidge/operator/Pad.hpp +++ b/include/aidge/operator/Pad.hpp @@ -27,13 +27,13 @@ namespace Aidge { enum class PadAttr { BeginEndBorders, BorderType, BorderValue }; -enum class PadBorderType { Constant, Replicate, Reflect, Wrap }; +enum class PadBorderType { Constant, Edge, Reflect, Wrap }; template <DimIdx_t DIM> class Pad_Op : public Operator, public Registrable<Pad_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const Pad_Op<DIM> &)>, public StaticAttributes<PadAttr, - std::array<std::array<DimSize_t, 2>, DIM>, + std::array<DimSize_t, 2*DIM>, PadBorderType, double> { private: @@ -47,13 +47,13 @@ public: Pad_Op() = delete; using Attributes_ = StaticAttributes<PadAttr, - std::array<std::array<DimSize_t, 2>, DIM>, + std::array<DimSize_t, 2*DIM>, PadBorderType, double>; template <PadAttr e> using attr = typename Attributes_::template attr<e>; - constexpr Pad_Op(const std::array<std::array<DimSize_t, 2>, DIM> &beginEndTuples, + constexpr Pad_Op(const std::array<DimSize_t, 2*DIM> &beginEndTuples, const PadBorderType &borderType = PadBorderType::Constant, double borderValue = 0.0) : Operator(Type), @@ -97,9 +97,9 @@ public: std::array<DimSize_t, DIM + 2> outputDims = {}; for (std::size_t dim = 0; dim < DIM; ++dim) { - outputDims[dim+2] = this->template getAttr<PadAttr::BeginEndBorders>()[dim][0] + outputDims[dim+2] = this->template getAttr<PadAttr::BeginEndBorders>()[2*dim] + mInput->dims()[dim+2] - + this->template getAttr<PadAttr::BeginEndBorders>()[dim][1]; + + this->template getAttr<PadAttr::BeginEndBorders>()[2*dim+1]; } outputDims[1] = mInput->dims()[1]; outputDims[0] = mInput->dims()[0]; @@ -160,65 +160,33 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> Pad(const std::array<std::array<DimSize_t, 2>, DIM> &beginEndTuples, - const std::string& name = "", - const PadBorderType &borderType = PadBorderType::Constant, - double borderValue = 0.0) -{ - static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported"); - auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, borderType, borderValue), name); - return pad; -} - -template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> Pad(const std::array<DimSize_t, DIM> &dimBeginEnd, +inline std::shared_ptr<Node> Pad(const std::array<DimSize_t, 2*DIM> &beginEndTuples, const std::string& name = "", const PadBorderType &borderType = PadBorderType::Constant, double borderValue = 0.0) { static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported"); - std::array<std::array<DimSize_t, 2>, DIM> beginEndTuples; - for (size_t i = 0; i < DIM; ++i) { - beginEndTuples[i] = {dimBeginEnd[i], dimBeginEnd[i]}; - } - auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, borderType, borderValue), name); - return pad; -} - -template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> ZeroPad(const std::array<std::array<DimSize_t, 2>, DIM> &beginEndTuples, - const std::string& name = "") -{ - static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported"); - auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, PadBorderType::Constant, 0.0), name); - return pad; -} - -template <std::array<DimSize_t, 1>::size_type DIM> -inline std::shared_ptr<Node> ZeroPad(const std::array<DimSize_t, DIM> &dimBeginEnd, - const std::string& name = "") -{ - static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported"); - std::array<std::array<DimSize_t, 2>, DIM> beginEndTuples; - for (size_t i = 0; i < DIM; ++i) { - beginEndTuples[i] = {dimBeginEnd[i], dimBeginEnd[i]}; - } - auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, PadBorderType::Constant, 0.0), name); - return pad; + return std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, borderType, borderValue), name); } +// helper with C-style array instead of std::array for beginEndTuples to allow automatic template DIM deduction template <DimSize_t DIM> inline std::shared_ptr<Node> Pad( - std::array<DimSize_t, 2> const (&beginEndTuples)[DIM], + DimSize_t const (&beginEndTuples)[2*DIM], const std::string& name = "", const PadBorderType &borderType = PadBorderType::Constant, double borderValue = 0.0) { - static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported"); - return Pad(to_array(beginEndTuples), name, borderType, borderValue); + return Pad<DIM>(to_array(beginEndTuples), name, borderType, borderValue); } } // namespace Aidge @@ -227,7 +195,7 @@ template <> const char *const EnumStrings<Aidge::PadAttr>::data[] = {"BeginEndBorders", "BorderType", "BorderValue"}; template <> -const char *const EnumStrings<Aidge::PadBorderType>::data[] = {"Constant", "Replicate", "Reflect", "Wrap"}; +const char *const EnumStrings<Aidge::PadBorderType>::data[] = {"Constant", "Edge", "Reflect", "Wrap"}; } #endif /* AIDGE_CORE_OPERATOR_PAD_H_ */ diff --git a/include/aidge/operator/Producer.hpp b/include/aidge/operator/Producer.hpp index 07d932bd0501832c78df2c3530f657b57251183f..d747b340618cc7e321f2cfc2ed9169798e5d77e9 100644 --- a/include/aidge/operator/Producer.hpp +++ b/include/aidge/operator/Producer.hpp @@ -79,7 +79,7 @@ public: * @brief Set the Output Tensor of the Producer operator. * This method will create a copy of the Tensor. * - * @param newOutput Tensor containing the values to copy + * @param newOutput Tensor containing the values to copy */ void setOutputTensor(const Tensor& newOutput) { *mOutput = newOutput; @@ -132,6 +132,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 0; }; inline IOIndex_t nbDataInputs() const noexcept override final { return 0; }; inline IOIndex_t nbOutputs() const noexcept override final { return 1; }; + static const std::vector<std::string> getInputsName(){ + return {}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } public: void forward() override final { @@ -148,6 +154,7 @@ inline std::shared_ptr<Node> Producer(const std::array<DimSize_t, DIM> &dims, co return std::make_shared<Node>(std::make_shared<Producer_Op>(dims), name); } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <std::size_t DIM> inline std::shared_ptr<Node> Producer(DimSize_t const (&dims)[DIM], const std::string& name = "") { return Producer(to_array(dims), name); @@ -167,6 +174,7 @@ void addProducer(std::shared_ptr<Node>& otherNode, const IOIndex_t inputIdx, con otherNode->getOperator()->associateInput(inputIdx, prod->getOperator()->getRawOutput(0)); } +// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction template <std::size_t DIM> void addProducer(std::shared_ptr<Node>& otherNode, const IOIndex_t inputIdx, DimSize_t const (&dims)[DIM], const std::string& extension) { addProducer(otherNode, inputIdx, to_array(dims), extension); diff --git a/include/aidge/operator/ReLU.hpp b/include/aidge/operator/ReLU.hpp index 0a7ec3b4fd9b51dbdb7cc95cd111337dad8553c4..52f13f1c5ce1d0b7a0d4ccaa4d7fe9927bcc3e53 100644 --- a/include/aidge/operator/ReLU.hpp +++ b/include/aidge/operator/ReLU.hpp @@ -125,10 +125,15 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; inline std::shared_ptr<Node> ReLU(const std::string& name = "") { - // FIXME: properly handle default w&b initialization in every cases return std::make_shared<Node>(std::make_shared<ReLU_Op>(), name); } } diff --git a/include/aidge/operator/Scaling.hpp b/include/aidge/operator/Scaling.hpp index f18abaf320620bbffec646d1bbb752b834487dd4..353666fb3950d034a7dbe8ec1d3ebdb312679f95 100644 --- a/include/aidge/operator/Scaling.hpp +++ b/include/aidge/operator/Scaling.hpp @@ -146,6 +146,12 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; inline std::shared_ptr<Node> Scaling(float scalingFactor = 1.0f, const std::string& name = "") { diff --git a/include/aidge/operator/Softmax.hpp b/include/aidge/operator/Softmax.hpp index 095ea0aadb9b9684a472b8a437ace6f5151bc4cf..ba6132a5ee00325d0f7de57db117a169d42352e9 100644 --- a/include/aidge/operator/Softmax.hpp +++ b/include/aidge/operator/Softmax.hpp @@ -125,10 +125,15 @@ public: inline IOIndex_t nbInputs() const noexcept override final { return 1; } inline IOIndex_t nbDataInputs() const noexcept override final { return 1; } inline IOIndex_t nbOutputs() const noexcept override final { return 1; } + static const std::vector<std::string> getInputsName(){ + return {"data_input"}; + } + static const std::vector<std::string> getOutputsName(){ + return {"data_output"}; + } }; inline std::shared_ptr<Node> Softmax(const std::string& name = "") { - // FIXME: properly handle default w&b initialization in every cases return std::make_shared<Node>(std::make_shared<Softmax_Op>(), name); } } diff --git a/include/aidge/utils/DynamicAttributes.hpp b/include/aidge/utils/DynamicAttributes.hpp index af03ee2861e81d81171ccc2ea14289f2ce3aa9e3..2af8f47e9420f266cc6eca21f167944c761db7ea 100644 --- a/include/aidge/utils/DynamicAttributes.hpp +++ b/include/aidge/utils/DynamicAttributes.hpp @@ -19,7 +19,7 @@ #include <cassert> #include <string> -#include "aidge/utils/Any.hpp" +#include "aidge/utils/future_std/any.hpp" #include "aidge/utils/Attributes.hpp" #ifdef PYBIND @@ -54,12 +54,12 @@ public: auto itPy = mAttrsPy.find(name); if (itPy != mAttrsPy.end()) { // Insert the attribute back in C++ - mAttrs.emplace(std::make_pair(name, libany::any(itPy->second.cast<T>()))); + mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>()))); } } #endif - return libany::any_cast<T&>(mAttrs.at(name)); + return future_std::any_cast<T&>(mAttrs.at(name)); } template<class T> const T& getAttr(const std::string& name) const @@ -71,12 +71,12 @@ public: auto itPy = mAttrsPy.find(name); if (itPy != mAttrsPy.end()) { // Insert the attribute back in C++ - mAttrs.emplace(std::make_pair(name, libany::any(itPy->second.cast<T>()))); + mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>()))); } } #endif - return libany::any_cast<const T&>(mAttrs.at(name)); + return future_std::any_cast<const T&>(mAttrs.at(name)); } ///\brief Add a new Attribute, identified by its name. If it already exists, asserts. @@ -85,7 +85,7 @@ public: ///\param value Attribute value template<class T> void addAttr(const std::string& name, const T& value) { - const auto& res = mAttrs.emplace(std::make_pair(name, libany::any(value))); + const auto& res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); assert(res.second && "attribute already exists"); #ifdef PYBIND @@ -103,9 +103,9 @@ public: ///\param value Attribute value template<class T> void setAttr(const std::string& name, const T& value) { - auto res = mAttrs.emplace(std::make_pair(name, libany::any(value))); + auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); if (!res.second) - res.first->second = libany::any(value); + res.first->second = future_std::any(value); #ifdef PYBIND // We cannot handle Python object if the Python interpreter is not running @@ -210,9 +210,9 @@ private: std::map<std::string, py::object> mAttrsPy; // Stores C++ attributes only // mutable because it may be updated in getAttr() from Python - mutable std::map<std::string, libany::any> mAttrs; + mutable std::map<std::string, future_std::any> mAttrs; #else - std::map<std::string, libany::any> mAttrs; + std::map<std::string, future_std::any> mAttrs; #endif }; diff --git a/include/aidge/utils/Registrar.hpp b/include/aidge/utils/Registrar.hpp index 3b29c472b3a540c9ef3b8ed46520e3e718e8cbfb..ece74509d466800c870d73d1e0bbe1d639f8bf54 100644 --- a/include/aidge/utils/Registrar.hpp +++ b/include/aidge/utils/Registrar.hpp @@ -35,7 +35,7 @@ public: { #ifdef PYBIND #define _CRT_SECURE_NO_WARNINGS - if (std::getenv("AIDGE_CORE_WITH_PYBIND")){ + if (Py_IsInitialized()){ std::string name = std::string("registrar_")+typeid(Registrable<DerivedClass, Key, Func>).name(); static auto shared_data = reinterpret_cast<std::map<Key, std::function<Func>> *>(py::get_shared_data(name)); if (!shared_data) @@ -78,4 +78,4 @@ struct Registrar { }; } -#endif //AIDGE_CORE_UTILS_REGISTRAR_H_ \ No newline at end of file +#endif //AIDGE_CORE_UTILS_REGISTRAR_H_ diff --git a/include/aidge/utils/Any.hpp b/include/aidge/utils/future_std/any.hpp similarity index 98% rename from include/aidge/utils/Any.hpp rename to include/aidge/utils/future_std/any.hpp index 0e65710596d31920de60a35d600e7ae612ea2bc4..8d9bfe28d0497dc12c59aaed68a23d3a9563815e 100644 --- a/include/aidge/utils/Any.hpp +++ b/include/aidge/utils/future_std/any.hpp @@ -14,11 +14,11 @@ * Copyright (c) 2018 Claudio Fantacci * * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt) + * (See copy at http://www.boost.org/LICENSE_1_0.txt) */ -#ifndef AIDGE_CORE_UTILS_ANY_H_ -#define AIDGE_CORE_UTILS_ANY_H_ +#ifndef AIDGE_CORE_UTILS_FUTURE_STD_ANY_H_ +#define AIDGE_CORE_UTILS_FUTURE_STD_ANY_H_ #include <stdexcept> #include <typeinfo> @@ -26,7 +26,7 @@ #include <utility> -namespace libany +namespace future_std { class bad_any_cast : public std::bad_cast @@ -549,4 +549,4 @@ inline void swap(any& lhs, any& rhs) noexcept } -#endif /* AIDGE_CORE_UTILS_ANY_H_ */ +#endif /* AIDGE_CORE_UTILS_FUTURE_STD_ANY_H_ */ diff --git a/include/aidge/utils/future_std/expected.hpp b/include/aidge/utils/future_std/expected.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c271d0e8d8066c0bcd0358f28f8bcd711a8b6ba0 --- /dev/null +++ b/include/aidge/utils/future_std/expected.hpp @@ -0,0 +1,3487 @@ +// Origin: https://github.com/martinmoene/expected-lite +// +// This version targets C++11 and later. +// +// Copyright (C) 2016-2020 Martin Moene. +// +// Distributed under the Boost Software License, Version 1.0. +// (See copy at http://www.boost.org/LICENSE_1_0.txt) +// +// expected lite is based on: +// A proposal to add a utility class to represent expected monad +// by Vicente J. Botet Escriba and Pierre Talbot. http:://wg21.link/p0323 + +#ifndef AIDGE_CORE_UTILS_FUTURE_STD_EXPECTED_H_ +#define AIDGE_CORE_UTILS_FUTURE_STD_EXPECTED_H_ + +#define expected_lite_MAJOR 0 +#define expected_lite_MINOR 6 +#define expected_lite_PATCH 3 + +#define expected_lite_VERSION expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH) + +#define expected_STRINGIFY( x ) expected_STRINGIFY_( x ) +#define expected_STRINGIFY_( x ) #x + +// expected-lite configuration: + +#define nsel_EXPECTED_DEFAULT 0 +#define nsel_EXPECTED_FUTURE_STD 1 +#define nsel_EXPECTED_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include(<future_std/expected.tweak.hpp>) +# include <future_std/expected.tweak.hpp> +# endif +#define expected_HAVE_TWEAK_HEADER 1 +#else +#define expected_HAVE_TWEAK_HEADER 0 +//# pragma message("expected.hpp: Note: Tweak header not supported.") +#endif + +// expected selection and configuration: + +#if !defined( nsel_CONFIG_SELECT_EXPECTED ) +# define nsel_CONFIG_SELECT_EXPECTED ( nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_FUTURE_STD ) +#endif + +// Proposal revisions: +// +// DXXXXR0: -- +// N4015 : -2 (2014-05-26) +// N4109 : -1 (2014-06-29) +// P0323R0: 0 (2016-05-28) +// P0323R1: 1 (2016-10-12) +// -------: +// P0323R2: 2 (2017-06-15) +// P0323R3: 3 (2017-10-15) +// P0323R4: 4 (2017-11-26) +// P0323R5: 5 (2018-02-08) +// P0323R6: 6 (2018-04-02) +// P0323R7: 7 (2018-06-22) * +// +// expected-lite uses 2 and higher + +#ifndef nsel_P0323R +# define nsel_P0323R 7 +#endif + +// Monadic operations proposal revisions: +// +// P2505R0: 0 (2021-12-12) +// P2505R1: 1 (2022-02-10) +// P2505R2: 2 (2022-04-15) +// P2505R3: 3 (2022-06-05) +// P2505R4: 4 (2022-06-15) +// P2505R5: 5 (2022-09-20) * +// +// expected-lite uses 5 + +#ifndef nsel_P2505R +# define nsel_P2505R 5 +#endif + +// Control presence of C++ exception handling (try and auto discover): + +#ifndef nsel_CONFIG_NO_EXCEPTIONS +# if defined(_MSC_VER) +# include <cstddef> // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define nsel_CONFIG_NO_EXCEPTIONS 0 +# else +# define nsel_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// at default use SEH with MSVC for no C++ exceptions + +#ifndef nsel_CONFIG_NO_EXCEPTIONS_SEH +# define nsel_CONFIG_NO_EXCEPTIONS_SEH ( nsel_CONFIG_NO_EXCEPTIONS && _MSC_VER ) +#endif + +// C++ language version detection (C++23 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef nsel_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define nsel_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define nsel_CPLUSPLUS __cplusplus +# endif +#endif + +#define nsel_CPP98_OR_GREATER ( nsel_CPLUSPLUS >= 199711L ) +#define nsel_CPP11_OR_GREATER ( nsel_CPLUSPLUS >= 201103L ) +#define nsel_CPP14_OR_GREATER ( nsel_CPLUSPLUS >= 201402L ) +#define nsel_CPP17_OR_GREATER ( nsel_CPLUSPLUS >= 201703L ) +#define nsel_CPP20_OR_GREATER ( nsel_CPLUSPLUS >= 202002L ) +#define nsel_CPP23_OR_GREATER ( nsel_CPLUSPLUS >= 202300L ) + +// Use C++23 std::expected if available and requested: + +#if nsel_CPP23_OR_GREATER && defined(__has_include ) +# if __has_include( <expected> ) +# define nsel_HAVE_STD_EXPECTED 1 +# else +# define nsel_HAVE_STD_EXPECTED 0 +# endif +#else +# define nsel_HAVE_STD_EXPECTED 0 +#endif + +#define nsel_USES_STD_EXPECTED ( (nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) || ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && nsel_HAVE_STD_EXPECTED) ) + +// +// in_place: code duplicated in any-lite, expected-lite, expected-lite, value-ptr-lite, variant-lite: +// + +#ifndef future_std_lite_HAVE_IN_PLACE_TYPES +#define future_std_lite_HAVE_IN_PLACE_TYPES 1 + +// C++17 std::in_place in <utility>: + +#if nsel_CPP17_OR_GREATER + +#include <utility> + +namespace future_std { + +using std::in_place; +using std::in_place_type; +using std::in_place_index; +using std::in_place_t; +using std::in_place_type_t; +using std::in_place_index_t; + +#define future_std_lite_in_place_t( T) std::in_place_t +#define future_std_lite_in_place_type_t( T) std::in_place_type_t<T> +#define future_std_lite_in_place_index_t(K) std::in_place_index_t<K> + +#define future_std_lite_in_place( T) std::in_place_t{} +#define future_std_lite_in_place_type( T) std::in_place_type_t<T>{} +#define future_std_lite_in_place_index(K) std::in_place_index_t<K>{} + +} // namespace future_std + +#else // nsel_CPP17_OR_GREATER + +#include <cstddef> + +namespace future_std { +namespace detail { + +template< class T > +struct in_place_type_tag {}; + +template< std::size_t K > +struct in_place_index_tag {}; + +} // namespace detail + +struct in_place_t {}; + +template< class T > +inline in_place_t in_place( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() ) +{ + return in_place_t(); +} + +template< std::size_t K > +inline in_place_t in_place( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() ) +{ + return in_place_t(); +} + +template< class T > +inline in_place_t in_place_type( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() ) +{ + return in_place_t(); +} + +template< std::size_t K > +inline in_place_t in_place_index( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() ) +{ + return in_place_t(); +} + +// mimic templated typedef: + +#define future_std_lite_in_place_t( T) future_std::in_place_t(&)( future_std::detail::in_place_type_tag<T> ) +#define future_std_lite_in_place_type_t( T) future_std::in_place_t(&)( future_std::detail::in_place_type_tag<T> ) +#define future_std_lite_in_place_index_t(K) future_std::in_place_t(&)( future_std::detail::in_place_index_tag<K> ) + +#define future_std_lite_in_place( T) future_std::in_place_type<T> +#define future_std_lite_in_place_type( T) future_std::in_place_type<T> +#define future_std_lite_in_place_index(K) future_std::in_place_index<K> + +} // namespace future_std + +#endif // nsel_CPP17_OR_GREATER +#endif // future_std_lite_HAVE_IN_PLACE_TYPES + +// +// Using std::expected: +// + +#if nsel_USES_STD_EXPECTED + +#include <expected> + +namespace future_std { + + using std::expected; +// ... +} + +#else // nsel_USES_STD_EXPECTED + +#include <cassert> +#include <exception> +#include <functional> +#include <initializer_list> +#include <memory> +#include <new> +#include <system_error> +#include <type_traits> +#include <utility> + +// additional includes: + +#if nsel_CONFIG_NO_EXCEPTIONS +# if nsel_CONFIG_NO_EXCEPTIONS_SEH +# include <windows.h> // for ExceptionCodes +# else +// already included: <cassert> +# endif +#else +# include <stdexcept> +#endif + +// C++ feature usage: + +#if nsel_CPP11_OR_GREATER +# define nsel_constexpr constexpr +#else +# define nsel_constexpr /*constexpr*/ +#endif + +#if nsel_CPP14_OR_GREATER +# define nsel_constexpr14 constexpr +#else +# define nsel_constexpr14 /*constexpr*/ +#endif + +#if nsel_CPP17_OR_GREATER +# define nsel_inline17 inline +#else +# define nsel_inline17 /*inline*/ +#endif + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 nsel_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 nsel_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 nsel_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 nsel_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 nsel_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 nsel_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 nsel_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 nsel_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 nsel_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 nsel_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 nsel_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER) && !defined(__clang__) +# define nsel_COMPILER_MSVC_VER (_MSC_VER ) +# define nsel_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900)) ) +#else +# define nsel_COMPILER_MSVC_VER 0 +# define nsel_COMPILER_MSVC_VERSION 0 +#endif + +#define nsel_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined(__clang__) +# define nsel_COMPILER_CLANG_VERSION nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define nsel_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define nsel_COMPILER_GNUC_VERSION nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define nsel_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +//#define nsel_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Method enabling + +#define nsel_REQUIRES_0(...) \ + template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 > + +#define nsel_REQUIRES_T(...) \ + , typename std::enable_if< (__VA_ARGS__), int >::type = 0 + +#define nsel_REQUIRES_R(R, ...) \ + typename std::enable_if< (__VA_ARGS__), R>::type + +#define nsel_REQUIRES_A(...) \ + , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define nsel_HAS_CPP0X _HAS_CPP0X +#else +# define nsel_HAS_CPP0X 0 +#endif + +//#define nsel_CPP11_140 (nsel_CPP11_OR_GREATER || nsel_COMPILER_MSVC_VER >= 1900) + +// Clang, GNUC, MSVC warning suppression macros: + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined __GNUC__ +# pragma GCC diagnostic push +#endif // __clang__ + +#if nsel_COMPILER_MSVC_VERSION >= 140 +# pragma warning( push ) +# define nsel_DISABLE_MSVC_WARNINGS(codes) __pragma( warning(disable: codes) ) +#else +# define nsel_DISABLE_MSVC_WARNINGS(codes) +#endif + +#ifdef __clang__ +# define nsel_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined __GNUC__ +# define nsel_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nsel_COMPILER_MSVC_VERSION >= 140 +# define nsel_RESTORE_WARNINGS() __pragma( warning( pop ) ) +#else +# define nsel_RESTORE_WARNINGS() +#endif + +// Suppress the following MSVC (GSL) warnings: +// - C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11) + +nsel_DISABLE_MSVC_WARNINGS( 26409 ) + +// +// expected: +// + +namespace future_std { namespace expected_lite { + +// type traits C++17: + +namespace std17 { + +#if nsel_CPP17_OR_GREATER + +using std::conjunction; +using std::is_swappable; +using std::is_nothrow_swappable; + +#else // nsel_CPP17_OR_GREATER + +namespace detail { + +using std::swap; + +struct is_swappable +{ + template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) > + static std::true_type test( int /* unused */); + + template< typename > + static std::false_type test(...); +}; + +struct is_nothrow_swappable +{ + // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015): + + template< typename T > + static constexpr bool satisfies() + { + return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) ); + } + + template< typename T > + static auto test( int ) -> std::integral_constant<bool, satisfies<T>()>{} + + template< typename > + static auto test(...) -> std::false_type; +}; +} // namespace detail + +// is [nothrow] swappable: + +template< typename T > +struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){}; + +template< typename T > +struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){}; + +// conjunction: + +template< typename... > struct conjunction : std::true_type{}; +template< typename B1 > struct conjunction<B1> : B1{}; + +template< typename B1, typename... Bn > +struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type{}; + +#endif // nsel_CPP17_OR_GREATER + +} // namespace std17 + +// type traits C++20: + +namespace std20 { + +#if defined(__cpp_lib_remove_cvref) + +using std::remove_cvref; + +#else + +template< typename T > +struct remove_cvref +{ + typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type; +}; + +#endif + +} // namespace std20 + +// forward declaration: + +template< typename T, typename E > +class expected; + +namespace detail { + +#if nsel_P2505R >= 3 +template< typename T > +struct is_expected : std::false_type {}; + +template< typename T, typename E > +struct is_expected< expected< T, E > > : std::true_type {}; +#endif // nsel_P2505R >= 3 + +/// discriminated union to hold value or 'error'. + +template< typename T, typename E > +class storage_t_noncopy_nonmove_impl +{ + template< typename, typename > friend class future_std::expected_lite::expected; + +public: + using value_type = T; + using error_type = E; + + // no-op construction + storage_t_noncopy_nonmove_impl() {} + ~storage_t_noncopy_nonmove_impl() {} + + explicit storage_t_noncopy_nonmove_impl( bool has_value ) + : m_has_value( has_value ) + {} + + void construct_value() + { + new( &m_value ) value_type(); + } + + // void construct_value( value_type const & e ) + // { + // new( &m_value ) value_type( e ); + // } + + // void construct_value( value_type && e ) + // { + // new( &m_value ) value_type( std::move( e ) ); + // } + + template< class... Args > + void emplace_value( Args&&... args ) + { + new( &m_value ) value_type( std::forward<Args>(args)...); + } + + template< class U, class... Args > + void emplace_value( std::initializer_list<U> il, Args&&... args ) + { + new( &m_value ) value_type( il, std::forward<Args>(args)... ); + } + + void destruct_value() + { + m_value.~value_type(); + } + + // void construct_error( error_type const & e ) + // { + // // new( &m_error ) error_type( e ); + // } + + // void construct_error( error_type && e ) + // { + // // new( &m_error ) error_type( std::move( e ) ); + // } + + template< class... Args > + void emplace_error( Args&&... args ) + { + new( &m_error ) error_type( std::forward<Args>(args)...); + } + + template< class U, class... Args > + void emplace_error( std::initializer_list<U> il, Args&&... args ) + { + new( &m_error ) error_type( il, std::forward<Args>(args)... ); + } + + void destruct_error() + { + m_error.~error_type(); + } + + constexpr value_type const & value() const & + { + return m_value; + } + + value_type & value() & + { + return m_value; + } + + constexpr value_type const && value() const && + { + return std::move( m_value ); + } + + nsel_constexpr14 value_type && value() && + { + return std::move( m_value ); + } + + value_type const * value_ptr() const + { + return &m_value; + } + + value_type * value_ptr() + { + return &m_value; + } + + error_type const & error() const & + { + return m_error; + } + + error_type & error() & + { + return m_error; + } + + constexpr error_type const && error() const && + { + return std::move( m_error ); + } + + nsel_constexpr14 error_type && error() && + { + return std::move( m_error ); + } + + bool has_value() const + { + return m_has_value; + } + + void set_has_value( bool v ) + { + m_has_value = v; + } + +private: + union + { + value_type m_value; + error_type m_error; + }; + + bool m_has_value = false; +}; + +template< typename T, typename E > +class storage_t_impl +{ + template< typename, typename > friend class future_std::expected_lite::expected; + +public: + using value_type = T; + using error_type = E; + + // no-op construction + storage_t_impl() {} + ~storage_t_impl() {} + + explicit storage_t_impl( bool has_value ) + : m_has_value( has_value ) + {} + + void construct_value() + { + new( &m_value ) value_type(); + } + + void construct_value( value_type const & e ) + { + new( &m_value ) value_type( e ); + } + + void construct_value( value_type && e ) + { + new( &m_value ) value_type( std::move( e ) ); + } + + template< class... Args > + void emplace_value( Args&&... args ) + { + new( &m_value ) value_type( std::forward<Args>(args)...); + } + + template< class U, class... Args > + void emplace_value( std::initializer_list<U> il, Args&&... args ) + { + new( &m_value ) value_type( il, std::forward<Args>(args)... ); + } + + void destruct_value() + { + m_value.~value_type(); + } + + void construct_error( error_type const & e ) + { + new( &m_error ) error_type( e ); + } + + void construct_error( error_type && e ) + { + new( &m_error ) error_type( std::move( e ) ); + } + + template< class... Args > + void emplace_error( Args&&... args ) + { + new( &m_error ) error_type( std::forward<Args>(args)...); + } + + template< class U, class... Args > + void emplace_error( std::initializer_list<U> il, Args&&... args ) + { + new( &m_error ) error_type( il, std::forward<Args>(args)... ); + } + + void destruct_error() + { + m_error.~error_type(); + } + + constexpr value_type const & value() const & + { + return m_value; + } + + value_type & value() & + { + return m_value; + } + + constexpr value_type const && value() const && + { + return std::move( m_value ); + } + + nsel_constexpr14 value_type && value() && + { + return std::move( m_value ); + } + + value_type const * value_ptr() const + { + return &m_value; + } + + value_type * value_ptr() + { + return &m_value; + } + + error_type const & error() const & + { + return m_error; + } + + error_type & error() & + { + return m_error; + } + + constexpr error_type const && error() const && + { + return std::move( m_error ); + } + + nsel_constexpr14 error_type && error() && + { + return std::move( m_error ); + } + + bool has_value() const + { + return m_has_value; + } + + void set_has_value( bool v ) + { + m_has_value = v; + } + +private: + union + { + value_type m_value; + error_type m_error; + }; + + bool m_has_value = false; +}; + +/// discriminated union to hold only 'error'. + +template< typename E > +struct storage_t_impl<void, E> +{ + template< typename, typename > friend class future_std::expected_lite::expected; + +public: + using value_type = void; + using error_type = E; + + // no-op construction + storage_t_impl() {} + ~storage_t_impl() {} + + explicit storage_t_impl( bool has_value ) + : m_has_value( has_value ) + {} + + void construct_error( error_type const & e ) + { + new( &m_error ) error_type( e ); + } + + void construct_error( error_type && e ) + { + new( &m_error ) error_type( std::move( e ) ); + } + + template< class... Args > + void emplace_error( Args&&... args ) + { + new( &m_error ) error_type( std::forward<Args>(args)...); + } + + template< class U, class... Args > + void emplace_error( std::initializer_list<U> il, Args&&... args ) + { + new( &m_error ) error_type( il, std::forward<Args>(args)... ); + } + + void destruct_error() + { + m_error.~error_type(); + } + + error_type const & error() const & + { + return m_error; + } + + error_type & error() & + { + return m_error; + } + + constexpr error_type const && error() const && + { + return std::move( m_error ); + } + + nsel_constexpr14 error_type && error() && + { + return std::move( m_error ); + } + + bool has_value() const + { + return m_has_value; + } + + void set_has_value( bool v ) + { + m_has_value = v; + } + +private: + union + { + char m_dummy; + error_type m_error; + }; + + bool m_has_value = false; +}; + +template< typename T, typename E, bool isConstructable, bool isMoveable > +class storage_t +{ +public: +}; + +template< typename T, typename E > +class storage_t<T, E, false, false> : public storage_t_noncopy_nonmove_impl<T, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_noncopy_nonmove_impl<T, E>( has_value ) + {} + + storage_t( storage_t const & other ) = delete; + storage_t( storage_t && other ) = delete; + +}; + +template< typename T, typename E > +class storage_t<T, E, true, true> : public storage_t_impl<T, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_impl<T, E>( has_value ) + {} + + storage_t( storage_t const & other ) + : storage_t_impl<T, E>( other.has_value() ) + { + if ( this->has_value() ) this->construct_value( other.value() ); + else this->construct_error( other.error() ); + } + + storage_t(storage_t && other ) + : storage_t_impl<T, E>( other.has_value() ) + { + if ( this->has_value() ) this->construct_value( std::move( other.value() ) ); + else this->construct_error( std::move( other.error() ) ); + } +}; + +template< typename E > +class storage_t<void, E, true, true> : public storage_t_impl<void, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_impl<void, E>( has_value ) + {} + + storage_t( storage_t const & other ) + : storage_t_impl<void, E>( other.has_value() ) + { + if ( this->has_value() ) ; + else this->construct_error( other.error() ); + } + + storage_t(storage_t && other ) + : storage_t_impl<void, E>( other.has_value() ) + { + if ( this->has_value() ) ; + else this->construct_error( std::move( other.error() ) ); + } +}; + +template< typename T, typename E > +class storage_t<T, E, true, false> : public storage_t_impl<T, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_impl<T, E>( has_value ) + {} + + storage_t( storage_t const & other ) + : storage_t_impl<T, E>(other.has_value()) + { + if ( this->has_value() ) this->construct_value( other.value() ); + else this->construct_error( other.error() ); + } + + storage_t( storage_t && other ) = delete; +}; + +template< typename E > +class storage_t<void, E, true, false> : public storage_t_impl<void, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_impl<void, E>( has_value ) + {} + + storage_t( storage_t const & other ) + : storage_t_impl<void, E>(other.has_value()) + { + if ( this->has_value() ) ; + else this->construct_error( other.error() ); + } + + storage_t( storage_t && other ) = delete; +}; + +template< typename T, typename E > +class storage_t<T, E, false, true> : public storage_t_impl<T, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_impl<T, E>( has_value ) + {} + + storage_t( storage_t const & other ) = delete; + + storage_t( storage_t && other ) + : storage_t_impl<T, E>( other.has_value() ) + { + if ( this->has_value() ) this->construct_value( std::move( other.value() ) ); + else this->construct_error( std::move( other.error() ) ); + } +}; + +template< typename E > +class storage_t<void, E, false, true> : public storage_t_impl<void, E> +{ +public: + storage_t() = default; + ~storage_t() = default; + + explicit storage_t( bool has_value ) + : storage_t_impl<void, E>( has_value ) + {} + + storage_t( storage_t const & other ) = delete; + + storage_t( storage_t && other ) + : storage_t_impl<void, E>( other.has_value() ) + { + if ( this->has_value() ) ; + else this->construct_error( std::move( other.error() ) ); + } +}; + +#if nsel_P2505R >= 3 +// C++11 invoke implementation +template< typename > +struct is_reference_wrapper : std::false_type {}; +template< typename T > +struct is_reference_wrapper< std::reference_wrapper< T > > : std::true_type {}; + +template< typename FnT, typename ClassT, typename ObjectT, typename... Args + nsel_REQUIRES_T( + std::is_function<FnT>::value + && ( std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value ) + ) +> +nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args ) + noexcept( noexcept( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... ) ) ) + -> decltype( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )...) ) +{ + return (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... ); +} + +template< typename FnT, typename ClassT, typename ObjectT, typename... Args + nsel_REQUIRES_T( + std::is_function<FnT>::value + && is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args ) + noexcept( noexcept( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) ) ) + -> decltype( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) ) +{ + return (obj.get().*memfnptr)( std::forward< Args >( args ) ... ); +} + +template< typename FnT, typename ClassT, typename ObjectT, typename... Args + nsel_REQUIRES_T( + std::is_function<FnT>::value + && !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args ) + noexcept( noexcept( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) ) ) + -> decltype( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) ) +{ + return ((*std::forward<ObjectT>(obj)).*memfnptr)( std::forward< Args >( args ) ... ); +} + +template< typename MemberT, typename ClassT, typename ObjectT + nsel_REQUIRES_T( + std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj ) + noexcept( noexcept( std::forward< ObjectT >( obj ).*memobjptr ) ) + -> decltype( std::forward< ObjectT >( obj ).*memobjptr ) +{ + return std::forward< ObjectT >( obj ).*memobjptr; +} + +template< typename MemberT, typename ClassT, typename ObjectT + nsel_REQUIRES_T( + is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj ) + noexcept( noexcept( obj.get().*memobjptr ) ) + -> decltype( obj.get().*memobjptr ) +{ + return obj.get().*memobjptr; +} + +template< typename MemberT, typename ClassT, typename ObjectT + nsel_REQUIRES_T( + !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj ) + noexcept( noexcept( (*std::forward< ObjectT >( obj )).*memobjptr ) ) + -> decltype( (*std::forward< ObjectT >( obj )).*memobjptr ) +{ + return (*std::forward< ObjectT >( obj )).*memobjptr; +} + +template< typename F, typename... Args + nsel_REQUIRES_T( + std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value + ) +> +nsel_constexpr auto invoke( F && f, Args && ... args ) + noexcept( noexcept( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) ) + -> decltype( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) +{ + return invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ); +} + +template< typename F, typename... Args + nsel_REQUIRES_T( + std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value + ) +> +nsel_constexpr auto invoke( F && f, Args && ... args ) + noexcept( noexcept( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) ) + -> decltype( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) +{ + return invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ); +} + +template< typename F, typename... Args + nsel_REQUIRES_T( + !std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value + && !std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value + ) +> +nsel_constexpr auto invoke( F && f, Args && ... args ) + noexcept( noexcept( std::forward< F >( f )( std::forward< Args >( args ) ... ) ) ) + -> decltype( std::forward< F >( f )( std::forward< Args >( args ) ... ) ) +{ + return std::forward< F >( f )( std::forward< Args >( args ) ... ); +} + +template< typename F, typename ... Args > +using invoke_result_nocvref_t = typename std20::remove_cvref< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type; + +#if nsel_P2505R >= 5 +template< typename F, typename ... Args > +using transform_invoke_result_t = typename std::remove_cv< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type; +#else +template< typename F, typename ... Args > +using transform_invoke_result_t = invoke_result_nocvref_t +#endif // nsel_P2505R >= 5 + +template< typename T > +struct valid_expected_value_type : std::integral_constant< bool, std::is_destructible< T >::value && !std::is_reference< T >::value && !std::is_array< T >::value > {}; + +#endif // nsel_P2505R >= 3 +} // namespace detail + +/// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected. + +#if nsel_P0323R <= 2 +template< typename E = std::exception_ptr > +class unexpected_type +#else +template< typename E > +class unexpected_type +#endif // nsel_P0323R +{ +public: + using error_type = E; + + // x.x.5.2.1 Constructors + +// unexpected_type() = delete; + + constexpr unexpected_type( unexpected_type const & ) = default; + constexpr unexpected_type( unexpected_type && ) = default; + + template< typename... Args + nsel_REQUIRES_T( + std::is_constructible<E, Args&&...>::value + ) + > + constexpr explicit unexpected_type( future_std_lite_in_place_t(E), Args &&... args ) + : m_error( std::forward<Args>( args )...) + {} + + template< typename U, typename... Args + nsel_REQUIRES_T( + std::is_constructible<E, std::initializer_list<U>, Args&&...>::value + ) + > + constexpr explicit unexpected_type( future_std_lite_in_place_t(E), std::initializer_list<U> il, Args &&... args ) + : m_error( il, std::forward<Args>( args )...) + {} + + template< typename E2 + nsel_REQUIRES_T( + std::is_constructible<E,E2>::value + && !std::is_same< typename std20::remove_cvref<E2>::type, future_std_lite_in_place_t(E2) >::value + && !std::is_same< typename std20::remove_cvref<E2>::type, unexpected_type >::value + ) + > + constexpr explicit unexpected_type( E2 && error ) + : m_error( std::forward<E2>( error ) ) + {} + + template< typename E2 + nsel_REQUIRES_T( + std::is_constructible< E, E2>::value + && !std::is_constructible<E, unexpected_type<E2> & >::value + && !std::is_constructible<E, unexpected_type<E2> >::value + && !std::is_constructible<E, unexpected_type<E2> const & >::value + && !std::is_constructible<E, unexpected_type<E2> const >::value + && !std::is_convertible< unexpected_type<E2> &, E>::value + && !std::is_convertible< unexpected_type<E2> , E>::value + && !std::is_convertible< unexpected_type<E2> const &, E>::value + && !std::is_convertible< unexpected_type<E2> const , E>::value + && !std::is_convertible< E2 const &, E>::value /*=> explicit */ + ) + > + constexpr explicit unexpected_type( unexpected_type<E2> const & error ) + : m_error( E{ error.value() } ) + {} + + template< typename E2 + nsel_REQUIRES_T( + std::is_constructible< E, E2>::value + && !std::is_constructible<E, unexpected_type<E2> & >::value + && !std::is_constructible<E, unexpected_type<E2> >::value + && !std::is_constructible<E, unexpected_type<E2> const & >::value + && !std::is_constructible<E, unexpected_type<E2> const >::value + && !std::is_convertible< unexpected_type<E2> &, E>::value + && !std::is_convertible< unexpected_type<E2> , E>::value + && !std::is_convertible< unexpected_type<E2> const &, E>::value + && !std::is_convertible< unexpected_type<E2> const , E>::value + && std::is_convertible< E2 const &, E>::value /*=> explicit */ + ) + > + constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> const & error ) + : m_error( error.value() ) + {} + + template< typename E2 + nsel_REQUIRES_T( + std::is_constructible< E, E2>::value + && !std::is_constructible<E, unexpected_type<E2> & >::value + && !std::is_constructible<E, unexpected_type<E2> >::value + && !std::is_constructible<E, unexpected_type<E2> const & >::value + && !std::is_constructible<E, unexpected_type<E2> const >::value + && !std::is_convertible< unexpected_type<E2> &, E>::value + && !std::is_convertible< unexpected_type<E2> , E>::value + && !std::is_convertible< unexpected_type<E2> const &, E>::value + && !std::is_convertible< unexpected_type<E2> const , E>::value + && !std::is_convertible< E2 const &, E>::value /*=> explicit */ + ) + > + constexpr explicit unexpected_type( unexpected_type<E2> && error ) + : m_error( E{ std::move( error.value() ) } ) + {} + + template< typename E2 + nsel_REQUIRES_T( + std::is_constructible< E, E2>::value + && !std::is_constructible<E, unexpected_type<E2> & >::value + && !std::is_constructible<E, unexpected_type<E2> >::value + && !std::is_constructible<E, unexpected_type<E2> const & >::value + && !std::is_constructible<E, unexpected_type<E2> const >::value + && !std::is_convertible< unexpected_type<E2> &, E>::value + && !std::is_convertible< unexpected_type<E2> , E>::value + && !std::is_convertible< unexpected_type<E2> const &, E>::value + && !std::is_convertible< unexpected_type<E2> const , E>::value + && std::is_convertible< E2 const &, E>::value /*=> non-explicit */ + ) + > + constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> && error ) + : m_error( std::move( error.value() ) ) + {} + + // x.x.5.2.2 Assignment + + nsel_constexpr14 unexpected_type& operator=( unexpected_type const & ) = default; + nsel_constexpr14 unexpected_type& operator=( unexpected_type && ) = default; + + template< typename E2 = E > + nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> const & other ) + { + unexpected_type{ other.value() }.swap( *this ); + return *this; + } + + template< typename E2 = E > + nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> && other ) + { + unexpected_type{ std::move( other.value() ) }.swap( *this ); + return *this; + } + + // x.x.5.2.3 Observers + + nsel_constexpr14 E & value() & noexcept + { + return m_error; + } + + constexpr E const & value() const & noexcept + { + return m_error; + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + + nsel_constexpr14 E && value() && noexcept + { + return std::move( m_error ); + } + + constexpr E const && value() const && noexcept + { + return std::move( m_error ); + } + +#endif + + // x.x.5.2.4 Swap + + template< typename U=E > + nsel_REQUIRES_R( void, + std17::is_swappable<U>::value + ) + swap( unexpected_type & other ) noexcept ( + std17::is_nothrow_swappable<U>::value + ) + { + using std::swap; + swap( m_error, other.m_error ); + } + + // TODO: ??? unexpected_type: in-class friend operator==, != + +private: + error_type m_error; +}; + +#if nsel_CPP17_OR_GREATER + +/// template deduction guide: + +template< typename E > +unexpected_type( E ) -> unexpected_type< E >; + +#endif + +/// class unexpected_type, std::exception_ptr specialization (P0323R2) + +#if !nsel_CONFIG_NO_EXCEPTIONS +#if nsel_P0323R <= 2 + +// TODO: Should expected be specialized for particular E types such as exception_ptr and how? +// See p0323r7 2.1. Ergonomics, http://wg21.link/p0323 +template<> +class unexpected_type< std::exception_ptr > +{ +public: + using error_type = std::exception_ptr; + + unexpected_type() = delete; + + ~unexpected_type(){} + + explicit unexpected_type( std::exception_ptr const & error ) + : m_error( error ) + {} + + explicit unexpected_type(std::exception_ptr && error ) + : m_error( std::move( error ) ) + {} + + template< typename E > + explicit unexpected_type( E error ) + : m_error( std::make_exception_ptr( error ) ) + {} + + std::exception_ptr const & value() const + { + return m_error; + } + + std::exception_ptr & value() + { + return m_error; + } + +private: + std::exception_ptr m_error; +}; + +#endif // nsel_P0323R +#endif // !nsel_CONFIG_NO_EXCEPTIONS + +/// x.x.4, Unexpected equality operators + +template< typename E1, typename E2 > +constexpr bool operator==( unexpected_type<E1> const & x, unexpected_type<E2> const & y ) +{ + return x.value() == y.value(); +} + +template< typename E1, typename E2 > +constexpr bool operator!=( unexpected_type<E1> const & x, unexpected_type<E2> const & y ) +{ + return ! ( x == y ); +} + +#if nsel_P0323R <= 2 + +template< typename E > +constexpr bool operator<( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return x.value() < y.value(); +} + +template< typename E > +constexpr bool operator>( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ( y < x ); +} + +template< typename E > +constexpr bool operator<=( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ! ( y < x ); +} + +template< typename E > +constexpr bool operator>=( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ! ( x < y ); +} + +#endif // nsel_P0323R + +/// x.x.5 Specialized algorithms + +template< typename E + nsel_REQUIRES_T( + std17::is_swappable<E>::value + ) +> +void swap( unexpected_type<E> & x, unexpected_type<E> & y) noexcept ( noexcept ( x.swap(y) ) ) +{ + x.swap( y ); +} + +#if nsel_P0323R <= 2 + +// unexpected: relational operators for std::exception_ptr: + +inline constexpr bool operator<( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ ) +{ + return false; +} + +inline constexpr bool operator>( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ ) +{ + return false; +} + +inline constexpr bool operator<=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y ) +{ + return ( x == y ); +} + +inline constexpr bool operator>=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y ) +{ + return ( x == y ); +} + +#endif // nsel_P0323R + +// unexpected: traits + +#if nsel_P0323R <= 3 + +template< typename E> +struct is_unexpected : std::false_type {}; + +template< typename E> +struct is_unexpected< unexpected_type<E> > : std::true_type {}; + +#endif // nsel_P0323R + +// unexpected: factory + +// keep make_unexpected() removed in p0323r2 for pre-C++17: + +template< typename E> +nsel_constexpr14 auto +make_unexpected( E && value ) -> unexpected_type< typename std::decay<E>::type > +{ + return unexpected_type< typename std::decay<E>::type >( std::forward<E>(value) ); +} + +#if nsel_P0323R <= 3 + +/*nsel_constexpr14*/ auto inline +make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr > +{ + return unexpected_type< std::exception_ptr >( std::current_exception() ); +} + +#endif // nsel_P0323R + +/// x.x.6, x.x.7 expected access error + +template< typename E > +class bad_expected_access; + +/// x.x.7 bad_expected_access<void>: expected access error + +template <> +class bad_expected_access< void > : public std::exception +{ +public: + explicit bad_expected_access() + : std::exception() + {} +}; + +/// x.x.6 bad_expected_access: expected access error + +#if !nsel_CONFIG_NO_EXCEPTIONS + +template< typename E > +class bad_expected_access : public bad_expected_access< void > +{ +public: + using error_type = E; + + explicit bad_expected_access( error_type error ) + : m_error( error ) + {} + + virtual char const * what() const noexcept override + { + return "bad_expected_access"; + } + + nsel_constexpr14 error_type & error() & + { + return m_error; + } + + constexpr error_type const & error() const & + { + return m_error; + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + + nsel_constexpr14 error_type && error() && + { + return std::move( m_error ); + } + + constexpr error_type const && error() const && + { + return std::move( m_error ); + } + +#endif + +private: + error_type m_error; +}; + +#endif // nsel_CONFIG_NO_EXCEPTIONS + +/// x.x.8 unexpect tag, in_place_unexpected tag: construct an error + +struct unexpect_t{}; +using in_place_unexpected_t = unexpect_t; + +nsel_inline17 constexpr unexpect_t unexpect{}; +nsel_inline17 constexpr unexpect_t in_place_unexpected{}; + +/// class error_traits + +#if nsel_CONFIG_NO_EXCEPTIONS + +namespace detail { + inline bool text( char const * /*text*/ ) { return true; } +} + +template< typename Error > +struct error_traits +{ + static void rethrow( Error const & /*e*/ ) + { +#if nsel_CONFIG_NO_EXCEPTIONS_SEH + RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL ); +#else + assert( false && detail::text("throw bad_expected_access<Error>{ e };") ); +#endif + } +}; + +template<> +struct error_traits< std::exception_ptr > +{ + static void rethrow( std::exception_ptr const & /*e*/ ) + { +#if nsel_CONFIG_NO_EXCEPTIONS_SEH + RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL ); +#else + assert( false && detail::text("throw bad_expected_access<std::exception_ptr>{ e };") ); +#endif + } +}; + +template<> +struct error_traits< std::error_code > +{ + static void rethrow( std::error_code const & /*e*/ ) + { +#if nsel_CONFIG_NO_EXCEPTIONS_SEH + RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL ); +#else + assert( false && detail::text("throw std::system_error( e );") ); +#endif + } +}; + +#else // nsel_CONFIG_NO_EXCEPTIONS + +template< typename Error > +struct error_traits +{ + static void rethrow( Error const & e ) + { + throw bad_expected_access<Error>{ e }; + } +}; + +template<> +struct error_traits< std::exception_ptr > +{ + static void rethrow( std::exception_ptr const & e ) + { + std::rethrow_exception( e ); + } +}; + +template<> +struct error_traits< std::error_code > +{ + static void rethrow( std::error_code const & e ) + { + throw std::system_error( e ); + } +}; + +#endif // nsel_CONFIG_NO_EXCEPTIONS + +#if nsel_P2505R >= 3 +namespace detail { + +// from https://en.cppreference.com/w/cpp/utility/expected/unexpected: +// "the type of the unexpected value. The type must not be an array type, a non-object type, a specialization of std::unexpected, or a cv-qualified type." +template< typename T > +struct valid_unexpected_type : std::integral_constant< bool, + std::is_same< T, typename std20::remove_cvref< T >::type >::value + && std::is_object< T >::value + && !std::is_array< T >::value +> {}; + +template< typename T > +struct valid_unexpected_type< unexpected_type< T > > : std::false_type {}; + +} // namespace detail +#endif // nsel_P2505R >= 3 + +} // namespace expected_lite + +// provide future_std::unexpected_type: + +using expected_lite::unexpected_type; + +namespace expected_lite { + +/// class expected + +#if nsel_P0323R <= 2 +template< typename T, typename E = std::exception_ptr > +class expected +#else +template< typename T, typename E > +class expected +#endif // nsel_P0323R +{ +private: + template< typename, typename > friend class expected; + +public: + using value_type = T; + using error_type = E; + using unexpected_type = future_std::unexpected_type<E>; + + template< typename U > + struct rebind + { + using type = expected<U, error_type>; + }; + + // x.x.4.1 constructors + + nsel_REQUIRES_0( + std::is_default_constructible<T>::value + ) + nsel_constexpr14 expected() + : contained( true ) + { + contained.construct_value(); + } + + nsel_constexpr14 expected( expected const & ) = default; + nsel_constexpr14 expected( expected && ) = default; + + template< typename U, typename G + nsel_REQUIRES_T( + std::is_constructible< T, U const &>::value + && std::is_constructible<E, G const &>::value + && !std::is_constructible<T, expected<U, G> & >::value + && !std::is_constructible<T, expected<U, G> && >::value + && !std::is_constructible<T, expected<U, G> const & >::value + && !std::is_constructible<T, expected<U, G> const && >::value + && !std::is_convertible< expected<U, G> & , T>::value + && !std::is_convertible< expected<U, G> &&, T>::value + && !std::is_convertible< expected<U, G> const & , T>::value + && !std::is_convertible< expected<U, G> const &&, T>::value + && (!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( expected<U, G> const & other ) + : contained( other.has_value() ) + { + if ( has_value() ) contained.construct_value( T{ other.contained.value() } ); + else contained.construct_error( E{ other.contained.error() } ); + } + + template< typename U, typename G + nsel_REQUIRES_T( + std::is_constructible< T, U const &>::value + && std::is_constructible<E, G const &>::value + && !std::is_constructible<T, expected<U, G> & >::value + && !std::is_constructible<T, expected<U, G> && >::value + && !std::is_constructible<T, expected<U, G> const & >::value + && !std::is_constructible<T, expected<U, G> const && >::value + && !std::is_convertible< expected<U, G> & , T>::value + && !std::is_convertible< expected<U, G> &&, T>::value + && !std::is_convertible< expected<U, G> const &, T>::value + && !std::is_convertible< expected<U, G> const &&, T>::value + && !(!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> const & other ) + : contained( other.has_value() ) + { + if ( has_value() ) contained.construct_value( other.contained.value() ); + else contained.construct_error( other.contained.error() ); + } + + template< typename U, typename G + nsel_REQUIRES_T( + std::is_constructible< T, U>::value + && std::is_constructible<E, G>::value + && !std::is_constructible<T, expected<U, G> & >::value + && !std::is_constructible<T, expected<U, G> && >::value + && !std::is_constructible<T, expected<U, G> const & >::value + && !std::is_constructible<T, expected<U, G> const && >::value + && !std::is_convertible< expected<U, G> & , T>::value + && !std::is_convertible< expected<U, G> &&, T>::value + && !std::is_convertible< expected<U, G> const & , T>::value + && !std::is_convertible< expected<U, G> const &&, T>::value + && (!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( expected<U, G> && other ) + : contained( other.has_value() ) + { + if ( has_value() ) contained.construct_value( T{ std::move( other.contained.value() ) } ); + else contained.construct_error( E{ std::move( other.contained.error() ) } ); + } + + template< typename U, typename G + nsel_REQUIRES_T( + std::is_constructible< T, U>::value + && std::is_constructible<E, G>::value + && !std::is_constructible<T, expected<U, G> & >::value + && !std::is_constructible<T, expected<U, G> && >::value + && !std::is_constructible<T, expected<U, G> const & >::value + && !std::is_constructible<T, expected<U, G> const && >::value + && !std::is_convertible< expected<U, G> & , T>::value + && !std::is_convertible< expected<U, G> &&, T>::value + && !std::is_convertible< expected<U, G> const & , T>::value + && !std::is_convertible< expected<U, G> const &&, T>::value + && !(!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> && other ) + : contained( other.has_value() ) + { + if ( has_value() ) contained.construct_value( std::move( other.contained.value() ) ); + else contained.construct_error( std::move( other.contained.error() ) ); + } + + template< typename U = T + nsel_REQUIRES_T( + std::is_copy_constructible<U>::value + ) + > + nsel_constexpr14 expected( value_type const & value ) + : contained( true ) + { + contained.construct_value( value ); + } + + template< typename U = T + nsel_REQUIRES_T( + std::is_constructible<T,U&&>::value + && !std::is_same<typename std20::remove_cvref<U>::type, future_std_lite_in_place_t(U)>::value + && !std::is_same< expected<T,E> , typename std20::remove_cvref<U>::type>::value + && !std::is_same<future_std::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value + && !std::is_convertible<U&&,T>::value /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( U && value ) noexcept + ( + std::is_nothrow_move_constructible<U>::value && + std::is_nothrow_move_constructible<E>::value + ) + : contained( true ) + { + contained.construct_value( T{ std::forward<U>( value ) } ); + } + + template< typename U = T + nsel_REQUIRES_T( + std::is_constructible<T,U&&>::value + && !std::is_same<typename std20::remove_cvref<U>::type, future_std_lite_in_place_t(U)>::value + && !std::is_same< expected<T,E> , typename std20::remove_cvref<U>::type>::value + && !std::is_same<future_std::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value + && std::is_convertible<U&&,T>::value /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( U && value ) noexcept + ( + std::is_nothrow_move_constructible<U>::value && + std::is_nothrow_move_constructible<E>::value + ) + : contained( true ) + { + contained.construct_value( std::forward<U>( value ) ); + } + + // construct error: + + template< typename G = E + nsel_REQUIRES_T( + std::is_constructible<E, G const & >::value + && !std::is_convertible< G const &, E>::value /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( future_std::unexpected_type<G> const & error ) + : contained( false ) + { + contained.construct_error( E{ error.value() } ); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_constructible<E, G const & >::value + && std::is_convertible< G const &, E>::value /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> const & error ) + : contained( false ) + { + contained.construct_error( error.value() ); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_constructible<E, G&& >::value + && !std::is_convertible< G&&, E>::value /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( future_std::unexpected_type<G> && error ) + : contained( false ) + { + contained.construct_error( E{ std::move( error.value() ) } ); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_constructible<E, G&& >::value + && std::is_convertible< G&&, E>::value /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> && error ) + : contained( false ) + { + contained.construct_error( std::move( error.value() ) ); + } + + // in-place construction, value + + template< typename... Args + nsel_REQUIRES_T( + std::is_constructible<T, Args&&...>::value + ) + > + nsel_constexpr14 explicit expected( future_std_lite_in_place_t(T), Args&&... args ) + : contained( true ) + { + contained.emplace_value( std::forward<Args>( args )... ); + } + + template< typename U, typename... Args + nsel_REQUIRES_T( + std::is_constructible<T, std::initializer_list<U>, Args&&...>::value + ) + > + nsel_constexpr14 explicit expected( future_std_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args ) + : contained( true ) + { + contained.emplace_value( il, std::forward<Args>( args )... ); + } + + // in-place construction, error + + template< typename... Args + nsel_REQUIRES_T( + std::is_constructible<E, Args&&...>::value + ) + > + nsel_constexpr14 explicit expected( unexpect_t, Args&&... args ) + : contained( false ) + { + contained.emplace_error( std::forward<Args>( args )... ); + } + + template< typename U, typename... Args + nsel_REQUIRES_T( + std::is_constructible<E, std::initializer_list<U>, Args&&...>::value + ) + > + nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args ) + : contained( false ) + { + contained.emplace_error( il, std::forward<Args>( args )... ); + } + + // x.x.4.2 destructor + + // TODO: ~expected: triviality + // Effects: If T is not cv void and is_trivially_destructible_v<T> is false and bool(*this), calls val.~T(). If is_trivially_destructible_v<E> is false and !bool(*this), calls unexpect.~unexpected<E>(). + // Remarks: If either T is cv void or is_trivially_destructible_v<T> is true, and is_trivially_destructible_v<E> is true, then this destructor shall be a trivial destructor. + + ~expected() + { + if ( has_value() ) contained.destruct_value(); + else contained.destruct_error(); + } + + // x.x.4.3 assignment + + expected & operator=( expected const & other ) + { + expected( other ).swap( *this ); + return *this; + } + + expected & operator=( expected && other ) noexcept + ( + std::is_nothrow_move_constructible< T>::value + && std::is_nothrow_move_assignable< T>::value + && std::is_nothrow_move_constructible<E>::value // added for missing + && std::is_nothrow_move_assignable< E>::value ) // nothrow above + { + expected( std::move( other ) ).swap( *this ); + return *this; + } + + template< typename U + nsel_REQUIRES_T( + !std::is_same<expected<T,E>, typename std20::remove_cvref<U>::type>::value + && std17::conjunction<std::is_scalar<T>, std::is_same<T, std::decay<U>> >::value + && std::is_constructible<T ,U>::value + && std::is_assignable< T&,U>::value + && std::is_nothrow_move_constructible<E>::value ) + > + expected & operator=( U && value ) + { + expected( std::forward<U>( value ) ).swap( *this ); + return *this; + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_constructible<E, G const&>::value && + std::is_copy_constructible<G>::value // TODO: std::is_nothrow_copy_constructible<G> + && std::is_copy_assignable<G>::value + ) + > + expected & operator=( future_std::unexpected_type<G> const & error ) + { + expected( unexpect, error.value() ).swap( *this ); + return *this; + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_constructible<E, G&&>::value && + std::is_move_constructible<G>::value // TODO: std::is_nothrow_move_constructible<G> + && std::is_move_assignable<G>::value + ) + > + expected & operator=( future_std::unexpected_type<G> && error ) + { + expected( unexpect, std::move( error.value() ) ).swap( *this ); + return *this; + } + + template< typename... Args + nsel_REQUIRES_T( + std::is_nothrow_constructible<T, Args&&...>::value + ) + > + value_type & emplace( Args &&... args ) + { + expected( future_std_lite_in_place(T), std::forward<Args>(args)... ).swap( *this ); + return value(); + } + + template< typename U, typename... Args + nsel_REQUIRES_T( + std::is_nothrow_constructible<T, std::initializer_list<U>&, Args&&...>::value + ) + > + value_type & emplace( std::initializer_list<U> il, Args &&... args ) + { + expected( future_std_lite_in_place(T), il, std::forward<Args>(args)... ).swap( *this ); + return value(); + } + + // x.x.4.4 swap + + template< typename U=T, typename G=E > + nsel_REQUIRES_R( void, + std17::is_swappable< U>::value + && std17::is_swappable<G>::value + && ( std::is_move_constructible<U>::value || std::is_move_constructible<G>::value ) + ) + swap( expected & other ) noexcept + ( + std::is_nothrow_move_constructible<T>::value && std17::is_nothrow_swappable<T&>::value && + std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value + ) + { + using std::swap; + + if ( bool(*this) && bool(other) ) { swap( contained.value(), other.contained.value() ); } + else if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); } + else if ( bool(*this) && ! bool(other) ) { error_type t( std::move( other.error() ) ); + other.contained.destruct_error(); + other.contained.construct_value( std::move( contained.value() ) ); + contained.destruct_value(); + contained.construct_error( std::move( t ) ); + bool has_value = contained.has_value(); + bool other_has_value = other.has_value(); + other.contained.set_has_value(has_value); + contained.set_has_value(other_has_value); + } + else if ( ! bool(*this) && bool(other) ) { other.swap( *this ); } + } + + // x.x.4.5 observers + + constexpr value_type const * operator ->() const + { + return assert( has_value() ), contained.value_ptr(); + } + + value_type * operator ->() + { + return assert( has_value() ), contained.value_ptr(); + } + + constexpr value_type const & operator *() const & + { + return assert( has_value() ), contained.value(); + } + + value_type & operator *() & + { + return assert( has_value() ), contained.value(); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + + constexpr value_type const && operator *() const && + { + return std::move( ( assert( has_value() ), contained.value() ) ); + } + + nsel_constexpr14 value_type && operator *() && + { + return std::move( ( assert( has_value() ), contained.value() ) ); + } + +#endif + + constexpr explicit operator bool() const noexcept + { + return has_value(); + } + + constexpr bool has_value() const noexcept + { + return contained.has_value(); + } + + constexpr value_type const & value() const & + { + return has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ); + } + + value_type & value() & + { + return has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + + constexpr value_type const && value() const && + { + return std::move( has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) ); + } + + nsel_constexpr14 value_type && value() && + { + return std::move( has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) ); + } + +#endif + + constexpr error_type const & error() const & + { + return assert( ! has_value() ), contained.error(); + } + + error_type & error() & + { + return assert( ! has_value() ), contained.error(); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + + constexpr error_type const && error() const && + { + return std::move( ( assert( ! has_value() ), contained.error() ) ); + } + + error_type && error() && + { + return std::move( ( assert( ! has_value() ), contained.error() ) ); + } + +#endif + + constexpr unexpected_type get_unexpected() const + { + return make_unexpected( contained.error() ); + } + + template< typename Ex > + bool has_exception() const + { + using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type; + return ! has_value() && std::is_base_of< Ex, ContainedEx>::value; + } + + template< typename U + nsel_REQUIRES_T( + std::is_copy_constructible< T>::value + && std::is_convertible<U&&, T>::value + ) + > + value_type value_or( U && v ) const & + { + return has_value() + ? contained.value() + : static_cast<T>( std::forward<U>( v ) ); + } + + template< typename U + nsel_REQUIRES_T( + std::is_move_constructible< T>::value + && std::is_convertible<U&&, T>::value + ) + > + value_type value_or( U && v ) && + { + return has_value() + ? std::move( contained.value() ) + : static_cast<T>( std::forward<U>( v ) ); + } + +#if nsel_P2505R >= 4 + template< typename G = E + nsel_REQUIRES_T( + std::is_copy_constructible< E >::value + && std::is_convertible< G, E >::value + ) + > + nsel_constexpr error_type error_or( G && e ) const & + { + return has_value() + ? static_cast< E >( std::forward< G >( e ) ) + : contained.error(); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_move_constructible< E >::value + && std::is_convertible< G, E >::value + ) + > + nsel_constexpr14 error_type error_or( G && e ) && + { + return has_value() + ? static_cast< E >( std::forward< G >( e ) ) + : std::move( contained.error() ); + } +#endif // nsel_P2505R >= 4 + +#if nsel_P2505R >= 3 + // Monadic operations (P2505) + template< typename F + nsel_REQUIRES_T( + detail::is_expected < detail::invoke_result_nocvref_t< F, value_type & > > ::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type & >::error_type, error_type >::value + && std::is_constructible< error_type, error_type & >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type & > and_then( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, value_type & >( detail::invoke( std::forward< F >( f ), value() ) ) + : detail::invoke_result_nocvref_t< F, value_type & >( unexpect, error() ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, const value_type & > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type & >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type & > and_then( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const value_type & >( detail::invoke( std::forward< F >( f ), value() ) ) + : detail::invoke_result_nocvref_t< F, const value_type & >( unexpect, error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, value_type && > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type && >::error_type, error_type >::value + && std::is_constructible< error_type, error_type && >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type && > and_then( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) ) + : detail::invoke_result_nocvref_t< F, value_type && >( unexpect, std::move( error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, const value_type && > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type && >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type && > and_then( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) ) + : detail::invoke_result_nocvref_t< F, const value_type && >( unexpect, std::move( error() ) ); + } +#endif + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, error_type & > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type, value_type >::value + && std::is_constructible< value_type, value_type & >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type & >( value() ) + : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type & > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type, value_type >::value + && std::is_constructible< value_type, const value_type & >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type & >( value() ) + : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, error_type && > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type, value_type >::value + && std::is_constructible< value_type, value_type && >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type && >( std::move( value() ) ) + : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type && > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type, value_type >::value + && std::is_constructible< value_type, const value_type && >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type && >( std::move( value() ) ) + : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type & >::value + && !std::is_void< detail::transform_invoke_result_t< F, value_type & > >::value + && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, value_type & > >::value + ) + > + nsel_constexpr14 expected< detail::transform_invoke_result_t< F, value_type & >, error_type > transform( F && f ) & + { + return has_value() + ? expected< detail::transform_invoke_result_t< F, value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type & >::value + && std::is_void< detail::transform_invoke_result_t< F, value_type & > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type & >::value + && !std::is_void< detail::transform_invoke_result_t< F, const value_type & > >::value + && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, const value_type & > >::value + ) + > + nsel_constexpr expected< detail::transform_invoke_result_t< F, const value_type & >, error_type > transform( F && f ) const & + { + return has_value() + ? expected< detail::transform_invoke_result_t< F, const value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type & >::value + && std::is_void< detail::transform_invoke_result_t< F, const value_type & > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type && >::value + && !std::is_void< detail::transform_invoke_result_t< F, value_type && > >::value + && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, value_type && > >::value + ) + > + nsel_constexpr14 expected< detail::transform_invoke_result_t< F, value_type && >, error_type > transform( F && f ) && + { + return has_value() + ? expected< detail::transform_invoke_result_t< F, value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) ) + : make_unexpected( std::move( error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type && >::value + && std::is_void< detail::transform_invoke_result_t< F, value_type && > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( std::move( error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type && >::value + && !std::is_void< detail::transform_invoke_result_t< F, const value_type && > >::value + && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, const value_type && > >::value + ) + > + nsel_constexpr expected< detail::transform_invoke_result_t< F, const value_type && >, error_type > transform( F && f ) const && + { + return has_value() + ? expected< detail::transform_invoke_result_t< F, const value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) ) + : make_unexpected( std::move( error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type && >::value + && std::is_void< detail::transform_invoke_result_t< F, const value_type && > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( std::move( error() ) ); + } +#endif + + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type & > >::value + && std::is_constructible< value_type, value_type & >::value + ) + > + nsel_constexpr14 expected< value_type, detail::transform_invoke_result_t< F, error_type & > > transform_error( F && f ) & + { + return has_value() + ? expected< value_type, detail::transform_invoke_result_t< F, error_type & > >( in_place, **this ) + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type & > >::value + && std::is_constructible< value_type, const value_type & >::value + ) + > + nsel_constexpr expected< value_type, detail::transform_invoke_result_t< F, const error_type & > > transform_error( F && f ) const & + { + return has_value() + ? expected< value_type, detail::transform_invoke_result_t< F, const error_type & > >( in_place, **this ) + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type && > >::value + && std::is_constructible< value_type, value_type && >::value + ) + > + nsel_constexpr14 expected< value_type, detail::transform_invoke_result_t< F, error_type && > > transform_error( F && f ) && + { + return has_value() + ? expected< value_type, detail::transform_invoke_result_t< F, error_type && > >( in_place, std::move( **this ) ) + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type && > >::value + && std::is_constructible< value_type, const value_type && >::value + ) + > + nsel_constexpr expected< value_type, detail::transform_invoke_result_t< F, const error_type && > > transform_error( F && f ) const && + { + return has_value() + ? expected< value_type, detail::transform_invoke_result_t< F, const error_type && > >( in_place, std::move( **this ) ) + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif +#endif // nsel_P2505R >= 3 + // unwrap() + +// template <class U, class E> +// constexpr expected<U,E> expected<expected<U,E>,E>::unwrap() const&; + +// template <class T, class E> +// constexpr expected<T,E> expected<T,E>::unwrap() const&; + +// template <class U, class E> +// expected<U,E> expected<expected<U,E>, E>::unwrap() &&; + +// template <class T, class E> +// template expected<T,E> expected<T,E>::unwrap() &&; + + // factories + +// template< typename Ex, typename F> +// expected<T,E> catch_exception(F&& f); + +// template< typename F> +// expected<decltype(func(declval<T>())),E> map(F&& func) ; + +// template< typename F> +// 'see below' bind(F&& func); + +// template< typename F> +// expected<T,E> catch_error(F&& f); + +// template< typename F> +// 'see below' then(F&& func); + +private: + detail::storage_t + < + T + ,E + , std::is_copy_constructible<T>::value && std::is_copy_constructible<E>::value + , std::is_move_constructible<T>::value && std::is_move_constructible<E>::value + > + contained; +}; + +/// class expected, void specialization + +template< typename E > +class expected<void, E> +{ +private: + template< typename, typename > friend class expected; + +public: + using value_type = void; + using error_type = E; + using unexpected_type = future_std::unexpected_type<E>; + + // x.x.4.1 constructors + + constexpr expected() noexcept + : contained( true ) + {} + + nsel_constexpr14 expected( expected const & other ) = default; + nsel_constexpr14 expected( expected && other ) = default; + + constexpr explicit expected( future_std_lite_in_place_t(void) ) + : contained( true ) + {} + + template< typename G = E + nsel_REQUIRES_T( + !std::is_convertible<G const &, E>::value /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( future_std::unexpected_type<G> const & error ) + : contained( false ) + { + contained.construct_error( E{ error.value() } ); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_convertible<G const &, E>::value /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> const & error ) + : contained( false ) + { + contained.construct_error( error.value() ); + } + + template< typename G = E + nsel_REQUIRES_T( + !std::is_convertible<G&&, E>::value /*=> explicit */ + ) + > + nsel_constexpr14 explicit expected( future_std::unexpected_type<G> && error ) + : contained( false ) + { + contained.construct_error( E{ std::move( error.value() ) } ); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_convertible<G&&, E>::value /*=> non-explicit */ + ) + > + nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> && error ) + : contained( false ) + { + contained.construct_error( std::move( error.value() ) ); + } + + template< typename... Args + nsel_REQUIRES_T( + std::is_constructible<E, Args&&...>::value + ) + > + nsel_constexpr14 explicit expected( unexpect_t, Args&&... args ) + : contained( false ) + { + contained.emplace_error( std::forward<Args>( args )... ); + } + + template< typename U, typename... Args + nsel_REQUIRES_T( + std::is_constructible<E, std::initializer_list<U>, Args&&...>::value + ) + > + nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args ) + : contained( false ) + { + contained.emplace_error( il, std::forward<Args>( args )... ); + } + + // destructor + + ~expected() + { + if ( ! has_value() ) + { + contained.destruct_error(); + } + } + + // x.x.4.3 assignment + + expected & operator=( expected const & other ) + { + expected( other ).swap( *this ); + return *this; + } + + expected & operator=( expected && other ) noexcept + ( + std::is_nothrow_move_assignable<E>::value && + std::is_nothrow_move_constructible<E>::value ) + { + expected( std::move( other ) ).swap( *this ); + return *this; + } + + void emplace() + { + expected().swap( *this ); + } + + // x.x.4.4 swap + + template< typename G = E > + nsel_REQUIRES_R( void, + std17::is_swappable<G>::value + && std::is_move_constructible<G>::value + ) + swap( expected & other ) noexcept + ( + std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value + ) + { + using std::swap; + + if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); } + else if ( bool(*this) && ! bool(other) ) { contained.construct_error( std::move( other.error() ) ); + bool has_value = contained.has_value(); + bool other_has_value = other.has_value(); + other.contained.set_has_value(has_value); + contained.set_has_value(other_has_value); + } + else if ( ! bool(*this) && bool(other) ) { other.swap( *this ); } + } + + // x.x.4.5 observers + + constexpr explicit operator bool() const noexcept + { + return has_value(); + } + + constexpr bool has_value() const noexcept + { + return contained.has_value(); + } + + void value() const + { + if ( ! has_value() ) + { + error_traits<error_type>::rethrow( contained.error() ); + } + } + + constexpr error_type const & error() const & + { + return assert( ! has_value() ), contained.error(); + } + + error_type & error() & + { + return assert( ! has_value() ), contained.error(); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + + constexpr error_type const && error() const && + { + return std::move( ( assert( ! has_value() ), contained.error() ) ); + } + + error_type && error() && + { + return std::move( ( assert( ! has_value() ), contained.error() ) ); + } + +#endif + + constexpr unexpected_type get_unexpected() const + { + return make_unexpected( contained.error() ); + } + + template< typename Ex > + bool has_exception() const + { + using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type; + return ! has_value() && std::is_base_of< Ex, ContainedEx>::value; + } + +#if nsel_P2505R >= 4 + template< typename G = E + nsel_REQUIRES_T( + std::is_copy_constructible< E >::value + && std::is_convertible< G, E >::value + ) + > + nsel_constexpr error_type error_or( G && e ) const & + { + return has_value() + ? static_cast< E >( std::forward< G >( e ) ) + : contained.error(); + } + + template< typename G = E + nsel_REQUIRES_T( + std::is_move_constructible< E >::value + && std::is_convertible< G, E >::value + ) + > + nsel_constexpr14 error_type error_or( G && e ) && + { + return has_value() + ? static_cast< E >( std::forward< G >( e ) ) + : std::move( contained.error() ); + } +#endif // nsel_P2505R >= 4 + +#if nsel_P2505R >= 3 + // Monadic operations (P2505) + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, error_type & >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, error() ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type & >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, error_type && >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F > >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type && >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) ); + } +#endif + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, error_type & > >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type & >() + : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type & > >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type & >() + : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, error_type && > >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type && >() + : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type && > >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type && >() + : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type & >::value + && !std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr14 expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) & + { + return has_value() + ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type & >::value + && std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type & >::value + && !std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) const & + { + return has_value() + ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type & >::value + && std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type && >::value + && !std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr14 expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) && + { + return has_value() + ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, error_type && >::value + && std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type && >::value + && !std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) const && + { + return has_value() + ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template<typename F + nsel_REQUIRES_T( + std::is_constructible< error_type, const error_type && >::value + && std::is_void< detail::transform_invoke_result_t< F > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } +#endif + + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type & > >::value + ) + > + nsel_constexpr14 expected< void, detail::transform_invoke_result_t< F, error_type & > > transform_error( F && f ) & + { + return has_value() + ? expected< void, detail::transform_invoke_result_t< F, error_type & > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type & > >::value + ) + > + nsel_constexpr expected< void, detail::transform_invoke_result_t< F, const error_type & > > transform_error( F && f ) const & + { + return has_value() + ? expected< void, detail::transform_invoke_result_t< F, const error_type & > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type && > >::value + ) + > + nsel_constexpr14 expected< void, detail::transform_invoke_result_t< F, error_type && > > transform_error( F && f ) && + { + return has_value() + ? expected< void, detail::transform_invoke_result_t< F, error_type && > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template<typename F + nsel_REQUIRES_T( + detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type && > >::value + ) + > + nsel_constexpr expected< void, detail::transform_invoke_result_t< F, const error_type && > > transform_error( F && f ) const && + { + return has_value() + ? expected< void, detail::transform_invoke_result_t< F, const error_type && > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif +#endif // nsel_P2505R >= 3 + +// template constexpr 'see below' unwrap() const&; +// +// template 'see below' unwrap() &&; + + // factories + +// template< typename Ex, typename F> +// expected<void,E> catch_exception(F&& f); +// +// template< typename F> +// expected<decltype(func()), E> map(F&& func) ; +// +// template< typename F> +// 'see below' bind(F&& func) ; +// +// template< typename F> +// expected<void,E> catch_error(F&& f); +// +// template< typename F> +// 'see below' then(F&& func); + +private: + detail::storage_t + < + void + , E + , std::is_copy_constructible<E>::value + , std::is_move_constructible<E>::value + > + contained; +}; + +// x.x.4.6 expected<>: comparison operators + +template< typename T1, typename E1, typename T2, typename E2 + nsel_REQUIRES_T( + !std::is_void<T1>::value && !std::is_void<T2>::value + ) +> +constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y ) +{ + return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error(); +} + +template< typename T1, typename E1, typename T2, typename E2 + nsel_REQUIRES_T( + std::is_void<T1>::value && std::is_void<T2>::value + ) +> +constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y ) +{ + return bool(x) != bool(y) ? false : bool(x) || static_cast<bool>( x.error() == y.error() ); +} + +template< typename T1, typename E1, typename T2, typename E2 > +constexpr bool operator!=( expected<T1,E1> const & x, expected<T2,E2> const & y ) +{ + return !(x == y); +} + +#if nsel_P0323R <= 2 + +template< typename T, typename E > +constexpr bool operator<( expected<T,E> const & x, expected<T,E> const & y ) +{ + return (!y) ? false : (!x) ? true : *x < *y; +} + +template< typename T, typename E > +constexpr bool operator>( expected<T,E> const & x, expected<T,E> const & y ) +{ + return (y < x); +} + +template< typename T, typename E > +constexpr bool operator<=( expected<T,E> const & x, expected<T,E> const & y ) +{ + return !(y < x); +} + +template< typename T, typename E > +constexpr bool operator>=( expected<T,E> const & x, expected<T,E> const & y ) +{ + return !(x < y); +} + +#endif + +// x.x.4.7 expected: comparison with T + +template< typename T1, typename E1, typename T2 + nsel_REQUIRES_T( + !std::is_void<T1>::value + ) +> +constexpr bool operator==( expected<T1,E1> const & x, T2 const & v ) +{ + return bool(x) ? *x == v : false; +} + +template< typename T1, typename E1, typename T2 + nsel_REQUIRES_T( + !std::is_void<T1>::value + ) +> +constexpr bool operator==(T2 const & v, expected<T1,E1> const & x ) +{ + return bool(x) ? v == *x : false; +} + +template< typename T1, typename E1, typename T2 > +constexpr bool operator!=( expected<T1,E1> const & x, T2 const & v ) +{ + return bool(x) ? *x != v : true; +} + +template< typename T1, typename E1, typename T2 > +constexpr bool operator!=( T2 const & v, expected<T1,E1> const & x ) +{ + return bool(x) ? v != *x : true; +} + +#if nsel_P0323R <= 2 + +template< typename T, typename E > +constexpr bool operator<( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? *x < v : true; +} + +template< typename T, typename E > +constexpr bool operator<( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? v < *x : false; +} + +template< typename T, typename E > +constexpr bool operator>( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? *x < v : false; +} + +template< typename T, typename E > +constexpr bool operator>( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? v < *x : false; +} + +template< typename T, typename E > +constexpr bool operator<=( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? ! ( *x < v ) : false; +} + +template< typename T, typename E > +constexpr bool operator<=( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? ! ( v < *x ) : true; +} + +template< typename T, typename E > +constexpr bool operator>=( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? ! ( *x < v ) : false; +} + +template< typename T, typename E > +constexpr bool operator>=( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? ! ( v < *x ) : true; +} + +#endif // nsel_P0323R + +// x.x.4.8 expected: comparison with unexpected_type + +template< typename T1, typename E1 , typename E2 > +constexpr bool operator==( expected<T1,E1> const & x, unexpected_type<E2> const & u ) +{ + return (!x) ? x.get_unexpected() == u : false; +} + +template< typename T1, typename E1 , typename E2 > +constexpr bool operator==( unexpected_type<E2> const & u, expected<T1,E1> const & x ) +{ + return ( x == u ); +} + +template< typename T1, typename E1 , typename E2 > +constexpr bool operator!=( expected<T1,E1> const & x, unexpected_type<E2> const & u ) +{ + return ! ( x == u ); +} + +template< typename T1, typename E1 , typename E2 > +constexpr bool operator!=( unexpected_type<E2> const & u, expected<T1,E1> const & x ) +{ + return ! ( x == u ); +} + +#if nsel_P0323R <= 2 + +template< typename T, typename E > +constexpr bool operator<( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return (!x) ? ( x.get_unexpected() < u ) : false; +} + +template< typename T, typename E > +constexpr bool operator<( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return (!x) ? ( u < x.get_unexpected() ) : true ; +} + +template< typename T, typename E > +constexpr bool operator>( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ( u < x ); +} + +template< typename T, typename E > +constexpr bool operator>( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return ( x < u ); +} + +template< typename T, typename E > +constexpr bool operator<=( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ! ( u < x ); +} + +template< typename T, typename E > +constexpr bool operator<=( unexpected_type<E> const & u, expected<T,E> const & x) +{ + return ! ( x < u ); +} + +template< typename T, typename E > +constexpr bool operator>=( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ! ( u > x ); +} + +template< typename T, typename E > +constexpr bool operator>=( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return ! ( x > u ); +} + +#endif // nsel_P0323R + +/// x.x.x Specialized algorithms + +template< typename T, typename E + nsel_REQUIRES_T( + ( std::is_void<T>::value || std::is_move_constructible<T>::value ) + && std::is_move_constructible<E>::value + && std17::is_swappable<T>::value + && std17::is_swappable<E>::value ) +> +void swap( expected<T,E> & x, expected<T,E> & y ) noexcept ( noexcept ( x.swap(y) ) ) +{ + x.swap( y ); +} + +#if nsel_P0323R <= 3 + +template< typename T > +constexpr auto make_expected( T && v ) -> expected< typename std::decay<T>::type > +{ + return expected< typename std::decay<T>::type >( std::forward<T>( v ) ); +} + +// expected<void> specialization: + +auto inline make_expected() -> expected<void> +{ + return expected<void>( in_place ); +} + +template< typename T > +constexpr auto make_expected_from_current_exception() -> expected<T> +{ + return expected<T>( make_unexpected_from_current_exception() ); +} + +template< typename T > +auto make_expected_from_exception( std::exception_ptr v ) -> expected<T> +{ + return expected<T>( unexpected_type<std::exception_ptr>( std::forward<std::exception_ptr>( v ) ) ); +} + +template< typename T, typename E > +constexpr auto make_expected_from_error( E e ) -> expected<T, typename std::decay<E>::type> +{ + return expected<T, typename std::decay<E>::type>( make_unexpected( e ) ); +} + +template< typename F + nsel_REQUIRES_T( ! std::is_same<typename std::result_of<F()>::type, void>::value ) +> +/*nsel_constexpr14*/ +auto make_expected_from_call( F f ) -> expected< typename std::result_of<F()>::type > +{ + try + { + return make_expected( f() ); + } + catch (...) + { + return make_unexpected_from_current_exception(); + } +} + +template< typename F + nsel_REQUIRES_T( std::is_same<typename std::result_of<F()>::type, void>::value ) +> +/*nsel_constexpr14*/ +auto make_expected_from_call( F f ) -> expected<void> +{ + try + { + f(); + return make_expected(); + } + catch (...) + { + return make_unexpected_from_current_exception(); + } +} + +#endif // nsel_P0323R + +} // namespace expected_lite + +using namespace expected_lite; + +// using expected_lite::expected; +// using ... + +} // namespace future_std + +namespace std { + +// expected: hash support + +template< typename T, typename E > +struct hash< future_std::expected<T,E> > +{ + using result_type = std::size_t; + using argument_type = future_std::expected<T,E>; + + constexpr result_type operator()(argument_type const & arg) const + { + return arg ? std::hash<T>{}(*arg) : result_type{}; + } +}; + +// TBD - ?? remove? see spec. +template< typename T, typename E > +struct hash< future_std::expected<T&,E> > +{ + using result_type = std::size_t; + using argument_type = future_std::expected<T&,E>; + + constexpr result_type operator()(argument_type const & arg) const + { + return arg ? std::hash<T>{}(*arg) : result_type{}; + } +}; + +// TBD - implement +// bool(e), hash<expected<void,E>>()(e) shall evaluate to the hashing true; +// otherwise it evaluates to an unspecified value if E is exception_ptr or +// a combination of hashing false and hash<E>()(e.error()). + +template< typename E > +struct hash< future_std::expected<void,E> > +{ +}; + +} // namespace std + +namespace future_std { + +// void unexpected() is deprecated && removed in C++17 + +#if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141 +template< typename E > +using unexpected = unexpected_type<E>; +#endif + +} // namespace future_std + +#undef nsel_REQUIRES +#undef nsel_REQUIRES_0 +#undef nsel_REQUIRES_T + +nsel_RESTORE_WARNINGS() + +#endif // nsel_USES_STD_EXPECTED + +#endif // AIDGE_CORE_UTILS_FUTURE_STD_EXPECTED_H_ diff --git a/python_binding/backend/pybind_OperatorImpl.cpp b/python_binding/backend/pybind_OperatorImpl.cpp index 11189f2f3c4a46b31d8e08d73bea17f27df07765..34610069079ee792ebbe4b261b57177b3bbe2997 100644 --- a/python_binding/backend/pybind_OperatorImpl.cpp +++ b/python_binding/backend/pybind_OperatorImpl.cpp @@ -10,11 +10,112 @@ ********************************************************************************/ #include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +#include "aidge/operator/Operator.hpp" #include "aidge/backend/OperatorImpl.hpp" namespace py = pybind11; namespace Aidge { + +/** + * @brief Trampoline class for binding + * + */ +class pyOperatorImpl: public OperatorImpl { +public: + using OperatorImpl::OperatorImpl; // Inherit constructors + + void forward() override { + PYBIND11_OVERRIDE( + void, + OperatorImpl, + forward, + + ); + } + void backward() override { + PYBIND11_OVERRIDE( + void, + OperatorImpl, + backward, + + ); + } + NbElts_t getNbRequiredData(const IOIndex_t inputIdx) const override { + PYBIND11_OVERRIDE_NAME( + NbElts_t, + OperatorImpl, + "get_nb_required_data", + getNbRequiredData, + inputIdx + ); + } + NbElts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override { + PYBIND11_OVERRIDE_NAME( + NbElts_t, + OperatorImpl, + "get_nb_required_protected", + getNbRequiredProtected, + inputIdx + + ); + } + NbElts_t getRequiredMemory(const IOIndex_t outputIdx, + const std::vector<DimSize_t> &inputsSize) const override { + PYBIND11_OVERRIDE_NAME( + NbElts_t, + OperatorImpl, + "get_required_memory", + getRequiredMemory, + outputIdx, + inputsSize + + ); + } + NbElts_t getNbConsumedData(const IOIndex_t inputIdx) const override { + PYBIND11_OVERRIDE_NAME( + NbElts_t, + OperatorImpl, + "get_nb_consumed_data", + getNbConsumedData, + inputIdx + + ); + } + NbElts_t getNbProducedData(const IOIndex_t outputIdx) const override { + PYBIND11_OVERRIDE_NAME( + NbElts_t, + OperatorImpl, + "get_nb_produced_data", + getNbProducedData, + outputIdx + + ); + } + void updateConsummerProducer() override { + PYBIND11_OVERRIDE_NAME( + void, + OperatorImpl, + "update_consummer_producer", + updateConsummerProducer, + + ); + } +}; + void init_OperatorImpl(py::module& m){ - py::class_<OperatorImpl, std::shared_ptr<OperatorImpl>>(m, "OperatorImpl"); + + py::class_<OperatorImpl, std::shared_ptr<OperatorImpl>, pyOperatorImpl>(m, "OperatorImpl", py::dynamic_attr()) + .def(py::init<const Operator&>()) + .def("forward", &OperatorImpl::forward) + .def("backward", &OperatorImpl::backward) + .def("get_nb_required_data", &OperatorImpl::getNbRequiredData) + .def("get_nb_required_protected", &OperatorImpl::getNbRequiredProtected) + .def("get_required_memory", &OperatorImpl::getRequiredMemory) + .def("get_nb_consumed_data", &OperatorImpl::getNbConsumedData) + .def("get_nb_produced_data", &OperatorImpl::getNbProducedData) + .def("update_consummer_producer", &OperatorImpl::updateConsummerProducer) + ; } } diff --git a/python_binding/operator/pybind_Add.cpp b/python_binding/operator/pybind_Add.cpp index ab8b4cf7b91d5eea2db5245a8c5122ab004b4766..0b2323c5cfb660415ec3ae009beaa7aa78afca0b 100644 --- a/python_binding/operator/pybind_Add.cpp +++ b/python_binding/operator/pybind_Add.cpp @@ -20,7 +20,9 @@ namespace py = pybind11; namespace Aidge { template <std::size_t NUM> void declare_Add(py::module &m) { - py::class_<Add_Op<NUM>, std::shared_ptr<Add_Op<NUM>>, Operator>(m, "Add_Op", py::multiple_inheritance()); + py::class_<Add_Op<NUM>, std::shared_ptr<Add_Op<NUM>>, Operator>(m, "AddOp", py::multiple_inheritance()) + .def("get_inputs_name", &Add_Op<NUM>::getInputsName) + .def("get_outputs_name", &Add_Op<NUM>::getOutputsName); m.def("Add", &Add<NUM>, py::arg("name") = ""); } diff --git a/python_binding/operator/pybind_AvgPooling.cpp b/python_binding/operator/pybind_AvgPooling.cpp index 5820e94c5cbd24150a4e81b0db34328ac35e1bf5..fe67fcb7a26f6ea1f05577b47444df5cb271110a 100644 --- a/python_binding/operator/pybind_AvgPooling.cpp +++ b/python_binding/operator/pybind_AvgPooling.cpp @@ -8,7 +8,7 @@ * SPDX-License-Identifier: EPL-2.0 * ********************************************************************************/ -#ifdef PYBIND + #include <pybind11/pybind11.h> #include <pybind11/stl.h> @@ -32,34 +32,21 @@ template <DimIdx_t DIM> void declare_AvgPoolingOp(py::module &m) { .def(py::init<const std::array<DimSize_t, DIM> &, const std::array<DimSize_t, DIM> &>(), py::arg("kernel_dims"), - py::arg("stride_dims")); - - m.def(("AvgPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, + py::arg("stride_dims")) + .def("get_inputs_name", &AvgPooling_Op<DIM>::getInputsName) + .def("get_outputs_name", &AvgPooling_Op<DIM>::getOutputsName); + + m.def(("AvgPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, const std::string& name, const std::vector<DimSize_t> &stride_dims) { - // Lambda function wrapper because PyBind fails to convert const array. - // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array. - if (kernel_dims.size() != DIM) { - throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - if (stride_dims.size() != DIM) { - throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - DimSize_t tmp_kernel_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_kernel_dims_array[i] = kernel_dims[i]; - } - DimSize_t tmp_stride_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_stride_dims_array[i] = stride_dims[i]; - } - const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array; - const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array; - return AvgPooling<DIM>(to_array(kernel_dims_array), name, to_array(stride_dims_array)); + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + + return AvgPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin())); }, py::arg("kernel_dims"), py::arg("name") = "", py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1)); - + } @@ -67,10 +54,9 @@ void init_AvgPooling(py::module &m) { declare_AvgPoolingOp<1>(m); declare_AvgPoolingOp<2>(m); declare_AvgPoolingOp<3>(m); - + // FIXME: // m.def("AvgPooling1D", static_cast<NodeAPI(*)(const char*, int, int, int const // (&)[1])>(&AvgPooling)); } } // namespace Aidge -#endif \ No newline at end of file diff --git a/python_binding/operator/pybind_BatchNorm.cpp b/python_binding/operator/pybind_BatchNorm.cpp index f43381fecc689a292e166c4da40ea0cb4842c9e6..cabaa2edd7053718160fa5013492d1914ee4cf16 100644 --- a/python_binding/operator/pybind_BatchNorm.cpp +++ b/python_binding/operator/pybind_BatchNorm.cpp @@ -21,7 +21,9 @@ namespace Aidge { template <DimSize_t DIM> void declare_BatchNormOp(py::module& m) { - py::class_<BatchNorm_Op<DIM>, std::shared_ptr<BatchNorm_Op<DIM>>, Operator, Attributes>(m, ("BatchNorm_Op" + std::to_string(DIM) + "D").c_str(), py::multiple_inheritance()); + py::class_<BatchNorm_Op<DIM>, std::shared_ptr<BatchNorm_Op<DIM>>, Operator, Attributes>(m, ("BatchNormOp" + std::to_string(DIM) + "D").c_str(), py::multiple_inheritance()) + .def("get_inputs_name", &BatchNorm_Op<DIM>::getInputsName) + .def("get_outputs_name", &BatchNorm_Op<DIM>::getOutputsName); m.def(("BatchNorm" + std::to_string(DIM) + "D").c_str(), &BatchNorm<DIM>, py::arg("epsilon") = 1.0e-5F, py::arg("momentum") = 0.1F, py::arg("name") = ""); } diff --git a/python_binding/operator/pybind_Conv.cpp b/python_binding/operator/pybind_Conv.cpp index 91ede7b6a289f3def2a9c8261ff04d2ab9836cdd..f4f7946c6ecc180f83e4bf58eee16102752f0c6e 100644 --- a/python_binding/operator/pybind_Conv.cpp +++ b/python_binding/operator/pybind_Conv.cpp @@ -11,7 +11,7 @@ #include <pybind11/pybind11.h> #include <pybind11/stl.h> - +#include <iostream> #include <string> #include <vector> #include <array> @@ -37,48 +37,28 @@ template <DimIdx_t DIM> void declare_ConvOp(py::module &m) { py::arg("out_channels"), py::arg("kernel_dims"), py::arg("stride_dims"), - py::arg("dilation_dims")); - + py::arg("dilation_dims")) + .def("get_inputs_name", &Conv_Op<DIM>::getInputsName) + .def("get_outputs_name", &Conv_Op<DIM>::getOutputsName) + ; + m.def(("Conv" + std::to_string(DIM) + "D").c_str(), [](DimSize_t in_channels, DimSize_t out_channels, const std::vector<DimSize_t>& kernel_dims, - const std::string& name, + const std::string& name, const std::vector<DimSize_t> &stride_dims, const std::vector<DimSize_t> &dilation_dims) { - // Lambda function wrapper because PyBind fails to convert const array. - // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array. - if (kernel_dims.size() != DIM) { - throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - if (stride_dims.size() != DIM) { - throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - if (dilation_dims.size() != DIM) { - throw std::runtime_error("dilation_dims size [" + std::to_string(dilation_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - DimSize_t tmp_kernel_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_kernel_dims_array[i] = kernel_dims[i]; - } - DimSize_t tmp_stride_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_stride_dims_array[i] = stride_dims[i]; - } - DimSize_t tmp_dilation_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_dilation_dims_array[i] = dilation_dims[i]; - } - const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array; - const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array; - const DimSize_t (&dilation_dims_array)[DIM] = tmp_dilation_dims_array; - return Conv<DIM>(in_channels, out_channels, to_array(kernel_dims_array), name, to_array(stride_dims_array), to_array(dilation_dims_array)); + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM); + + return Conv<DIM>(in_channels, out_channels, to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<DIM>(dilation_dims.begin())); }, py::arg("in_channels"), py::arg("out_channels"), py::arg("kernel_dims"), py::arg("name") = "", py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1)); - } @@ -86,7 +66,7 @@ void init_Conv(py::module &m) { declare_ConvOp<1>(m); declare_ConvOp<2>(m); declare_ConvOp<3>(m); - + // FIXME: // m.def("Conv1D", static_cast<NodeAPI(*)(const char*, int, int, int const // (&)[1])>(&Conv)); diff --git a/python_binding/operator/pybind_ConvDepthWise.cpp b/python_binding/operator/pybind_ConvDepthWise.cpp index 446bcdcceb3ba805223fc22e6fc19a22dcf354ec..4745ef345264763f1a890d566235be072c8e50d8 100644 --- a/python_binding/operator/pybind_ConvDepthWise.cpp +++ b/python_binding/operator/pybind_ConvDepthWise.cpp @@ -34,44 +34,24 @@ template <DimIdx_t DIM> void declare_ConvDepthWiseOp(py::module &m) { const std::array<DimSize_t, DIM> &>(), py::arg("kernel_dims"), py::arg("stride_dims"), - py::arg("dilation_dims")); - - m.def(("ConvDepthWise" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, + py::arg("dilation_dims")) + .def("get_inputs_name", &ConvDepthWise_Op<DIM>::getInputsName) + .def("get_outputs_name", &ConvDepthWise_Op<DIM>::getOutputsName); + + m.def(("ConvDepthWise" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, const std::string& name, const std::vector<DimSize_t> &stride_dims, const std::vector<DimSize_t> &dilation_dims) { - // Lambda function wrapper because PyBind fails to convert const array. - // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array. - if (kernel_dims.size() != DIM) { - throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - if (stride_dims.size() != DIM) { - throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - if (dilation_dims.size() != DIM) { - throw std::runtime_error("dilation_dims size [" + std::to_string(dilation_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - DimSize_t tmp_kernel_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_kernel_dims_array[i] = kernel_dims[i]; - } - DimSize_t tmp_stride_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_stride_dims_array[i] = stride_dims[i]; - } - DimSize_t tmp_dilation_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_dilation_dims_array[i] = dilation_dims[i]; - } - const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array; - const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array; - const DimSize_t (&dilation_dims_array)[DIM] = tmp_dilation_dims_array; - return ConvDepthWise<DIM>(to_array(kernel_dims_array), name, to_array(stride_dims_array), to_array(dilation_dims_array)); + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM); + + return ConvDepthWise<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<DIM>(dilation_dims.begin())); }, py::arg("kernel_dims"), py::arg("name") = "", py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1)); - + } @@ -79,7 +59,7 @@ void init_ConvDepthWise(py::module &m) { declare_ConvDepthWiseOp<1>(m); declare_ConvDepthWiseOp<2>(m); declare_ConvDepthWiseOp<3>(m); - + // FIXME: // m.def("ConvDepthWise1D", static_cast<NodeAPI(*)(const char*, int, int, int const // (&)[1])>(&ConvDepthWise)); diff --git a/python_binding/operator/pybind_FC.cpp b/python_binding/operator/pybind_FC.cpp index 4b9d61d082ebed4d426b41efa071d3943f83d231..c6a1c70000e3e6d604a6652716667efa1c18e956 100644 --- a/python_binding/operator/pybind_FC.cpp +++ b/python_binding/operator/pybind_FC.cpp @@ -20,7 +20,9 @@ namespace py = pybind11; namespace Aidge { void declare_FC(py::module &m) { - py::class_<FC_Op, std::shared_ptr<FC_Op>, Operator, Attributes>(m, "FC_Op", py::multiple_inheritance()); + py::class_<FC_Op, std::shared_ptr<FC_Op>, Operator, Attributes>(m, "FCOp", py::multiple_inheritance()) + .def("get_inputs_name", &FC_Op::getInputsName) + .def("get_outputs_name", &FC_Op::getOutputsName); m.def("FC", &FC, py::arg("out_channels"), py::arg("nobias") = false, py::arg("name") = ""); } diff --git a/python_binding/operator/pybind_GenericOperator.cpp b/python_binding/operator/pybind_GenericOperator.cpp index 4cf4dae2234900722058d6555582c5b78900ab7d..241fc7f4a003f53de15a42859b078c54cc98b63a 100644 --- a/python_binding/operator/pybind_GenericOperator.cpp +++ b/python_binding/operator/pybind_GenericOperator.cpp @@ -27,7 +27,7 @@ void init_GenericOperator(py::module& m) { .def("compute_output_dims", &GenericOperator_Op::computeOutputDims) .def("set_compute_output_dims", &GenericOperator_Op::setComputeOutputDims, py::arg("computation_function")); - m.def("GenericOperator", &GenericOperator, py::arg("type"), py::arg("nbDataIn"), py::arg("nbIn"), py::arg("nbOut"), + m.def("GenericOperator", &GenericOperator, py::arg("type"), py::arg("nb_data_in"), py::arg("nb_in"), py::arg("nb_out"), py::arg("name") = ""); } } // namespace Aidge diff --git a/python_binding/operator/pybind_LeakyReLU.cpp b/python_binding/operator/pybind_LeakyReLU.cpp index cae8a88bab7b59189dfbc6528cd653f1c97cb73a..af7689f0e64dd4ca8f798dcb34ea968972ace464 100644 --- a/python_binding/operator/pybind_LeakyReLU.cpp +++ b/python_binding/operator/pybind_LeakyReLU.cpp @@ -18,7 +18,9 @@ namespace py = pybind11; namespace Aidge { void init_LeakyReLU(py::module& m) { - py::class_<LeakyReLU_Op, std::shared_ptr<LeakyReLU_Op>, Operator, Attributes>(m, "LeakyReLU_Op", py::multiple_inheritance()); + py::class_<LeakyReLU_Op, std::shared_ptr<LeakyReLU_Op>, Operator, Attributes>(m, "LeakyReLUOp", py::multiple_inheritance()) + .def("get_inputs_name", &LeakyReLU_Op::getInputsName) + .def("get_outputs_name", &LeakyReLU_Op::getOutputsName); m.def("LeakyReLU", &LeakyReLU, py::arg("negative_slope") = 0.0f, py::arg("name") = ""); } diff --git a/python_binding/operator/pybind_Matmul.cpp b/python_binding/operator/pybind_Matmul.cpp index 2f738550041bcdb1ae809d68fa24fdf5a72e9164..fdb51b24a87ce358c1e7808873ebc569ca2227c8 100644 --- a/python_binding/operator/pybind_Matmul.cpp +++ b/python_binding/operator/pybind_Matmul.cpp @@ -20,7 +20,9 @@ namespace py = pybind11; namespace Aidge { void declare_MatMul(py::module &m) { - py::class_<MatMul_Op, std::shared_ptr<MatMul_Op>, Operator, Attributes>(m, "MatMul_Op", py::multiple_inheritance()); + py::class_<MatMul_Op, std::shared_ptr<MatMul_Op>, Operator, Attributes>(m, "MatMulOp", py::multiple_inheritance()) + .def("get_inputs_name", &MatMul_Op::getInputsName) + .def("get_outputs_name", &MatMul_Op::getOutputsName); m.def("MatMul", &MatMul, py::arg("out_channels"), py::arg("name") = ""); } diff --git a/python_binding/operator/pybind_MaxPooling.cpp b/python_binding/operator/pybind_MaxPooling.cpp index a930b496b49280629d71725cee79aea4d850358e..907e8cfaa6cde2451677b72beab38bd9a3938735 100644 --- a/python_binding/operator/pybind_MaxPooling.cpp +++ b/python_binding/operator/pybind_MaxPooling.cpp @@ -8,7 +8,7 @@ * SPDX-License-Identifier: EPL-2.0 * ********************************************************************************/ -#ifdef PYBIND + #include <pybind11/pybind11.h> #include <pybind11/stl.h> @@ -30,36 +30,27 @@ template <DimIdx_t DIM> void declare_MaxPoolingOp(py::module &m) { m, ("MaxPoolingOp" + std::to_string(DIM) + "D").c_str(), py::multiple_inheritance()) .def(py::init<const std::array<DimSize_t, DIM> &, - const std::array<DimSize_t, DIM> &>(), + const std::array<DimSize_t, DIM> &, + bool>(), py::arg("kernel_dims"), - py::arg("stride_dims")); - - m.def(("MaxPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, + py::arg("stride_dims"), + py::arg("ceil_mode")) + .def("get_inputs_name", &MaxPooling_Op<DIM>::getInputsName) + .def("get_outputs_name", &MaxPooling_Op<DIM>::getOutputsName); + + m.def(("MaxPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, const std::string& name, - const std::vector<DimSize_t> &stride_dims) { - // Lambda function wrapper because PyBind fails to convert const array. - // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array. - if (kernel_dims.size() != DIM) { - throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - if (stride_dims.size() != DIM) { - throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]"); - } - DimSize_t tmp_kernel_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_kernel_dims_array[i] = kernel_dims[i]; - } - DimSize_t tmp_stride_dims_array[DIM]; - for (size_t i = 0; i < DIM; ++i) { - tmp_stride_dims_array[i] = stride_dims[i]; - } - const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array; - const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array; - return MaxPooling<DIM>(to_array(kernel_dims_array), name, to_array(stride_dims_array)); + const std::vector<DimSize_t> &stride_dims, + bool ceil_mode) { + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + + return MaxPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), ceil_mode); }, py::arg("kernel_dims"), py::arg("name") = "", - py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1)); - + py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), + py::arg("ceil_mode") = false); + } @@ -67,10 +58,6 @@ void init_MaxPooling(py::module &m) { declare_MaxPoolingOp<1>(m); declare_MaxPoolingOp<2>(m); declare_MaxPoolingOp<3>(m); - - // FIXME: - // m.def("MaxPooling1D", static_cast<NodeAPI(*)(const char*, int, int, int const - // (&)[1])>(&MaxPooling)); + } } // namespace Aidge -#endif \ No newline at end of file diff --git a/python_binding/operator/pybind_MetaOperatorDefs.cpp b/python_binding/operator/pybind_MetaOperatorDefs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa9f3c50e6b8c6ab9e7be46776d5fba30d775be2 --- /dev/null +++ b/python_binding/operator/pybind_MetaOperatorDefs.cpp @@ -0,0 +1,126 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +#include <string> +#include <vector> +#include <array> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/operator/MetaOperatorDefs.hpp" +#include "aidge/operator/Operator.hpp" +#include "aidge/utils/Types.h" + +namespace py = pybind11; +namespace Aidge { + +template <DimIdx_t DIM> void declare_PaddedConvOp(py::module &m) { + m.def(("PaddedConv" + std::to_string(DIM) + "D").c_str(), [](DimSize_t in_channels, + DimSize_t out_channels, + const std::vector<DimSize_t>& kernel_dims, + const std::string& name, + const std::vector<DimSize_t> &stride_dims, + const std::vector<DimSize_t> &padding_dims, + const std::vector<DimSize_t> &dilation_dims) + { + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM); + AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM); + + return PaddedConv<DIM>(in_channels, out_channels, to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()), to_array<DIM>(dilation_dims.begin())); + }, py::arg("in_channels"), + py::arg("out_channels"), + py::arg("kernel_dims"), + py::arg("name") = "", + py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), + py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0), + py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1)); +} + +template <DimIdx_t DIM> void declare_PaddedConvDepthWiseOp(py::module &m) { + m.def(("PaddedConvDepthWise" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, + const std::string& name, + const std::vector<DimSize_t> &stride_dims, + const std::vector<DimSize_t> &padding_dims, + const std::vector<DimSize_t> &dilation_dims) + { + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM); + AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM); + + return PaddedConvDepthWise<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()), to_array<DIM>(dilation_dims.begin())); + }, py::arg("kernel_dims"), + py::arg("name") = "", + py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), + py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0), + py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1)); + +} + +template <DimIdx_t DIM> void declare_PaddedAvgPoolingOp(py::module &m) { + m.def(("PaddedAvgPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, + const std::string& name, + const std::vector<DimSize_t> &stride_dims, + const std::vector<DimSize_t> &padding_dims) + { + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM); + + return PaddedAvgPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin())); + }, py::arg("kernel_dims"), + py::arg("name") = "", + py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), + py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0)); + +} + +template <DimIdx_t DIM> void declare_PaddedMaxPoolingOp(py::module &m) { + m.def(("PaddedMaxPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims, + const std::string& name, + const std::vector<DimSize_t> &stride_dims, + const std::vector<DimSize_t> &padding_dims, + bool ceil_mode) + { + AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM); + AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM); + AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM); + + return PaddedMaxPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()), ceil_mode); + }, py::arg("kernel_dims"), + py::arg("name") = "", + py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1), + py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0), + py::arg("ceil_mode") = false); + +} + +void init_MetaOperatorDefs(py::module &m) { + declare_PaddedConvOp<1>(m); + declare_PaddedConvOp<2>(m); + declare_PaddedConvOp<3>(m); + declare_PaddedConvDepthWiseOp<1>(m); + declare_PaddedConvDepthWiseOp<2>(m); + declare_PaddedConvDepthWiseOp<3>(m); + declare_PaddedAvgPoolingOp<1>(m); + declare_PaddedAvgPoolingOp<2>(m); + declare_PaddedAvgPoolingOp<3>(m); + declare_PaddedMaxPoolingOp<1>(m); + declare_PaddedMaxPoolingOp<2>(m); + declare_PaddedMaxPoolingOp<3>(m); + + +} +} // namespace Aidge diff --git a/python_binding/operator/pybind_Operator.cpp b/python_binding/operator/pybind_Operator.cpp index d945b212ff6fb643302ca7512e91c7a778a39419..6b535e8cf3293b26aaa64f95ca2f9a394768935f 100644 --- a/python_binding/operator/pybind_Operator.cpp +++ b/python_binding/operator/pybind_Operator.cpp @@ -24,6 +24,9 @@ void init_Operator(py::module& m){ .def("associate_input", &Operator::associateInput, py::arg("inputIdx"), py::arg("data")) .def("set_datatype", &Operator::setDatatype, py::arg("datatype")) .def("set_backend", &Operator::setBackend, py::arg("name")) + .def("forward", &Operator::forward) + // py::keep_alive forbide Python to garbage collect implementation will the Operator is not garbade collected ! + .def("set_impl", &Operator::setImpl, py::arg("implementation"), py::keep_alive<1, 2>()) ; } } diff --git a/python_binding/operator/pybind_Producer.cpp b/python_binding/operator/pybind_Producer.cpp index 1c62cd0adf6b8712073ec0674754ce7c8c2014a5..107b7ba00e4077d9f7c215257bf7fd46629481c1 100644 --- a/python_binding/operator/pybind_Producer.cpp +++ b/python_binding/operator/pybind_Producer.cpp @@ -35,7 +35,9 @@ void init_Producer(py::module &m) { "ProducerOp", py::multiple_inheritance()) .def("dims", &Producer_Op::dims) - .def("set_output_tensor", &Producer_Op::setOutputTensor); + .def("set_output_tensor", &Producer_Op::setOutputTensor) + .def("get_inputs_name", &Producer_Op::getInputsName) + .def("get_outputs_name", &Producer_Op::getOutputsName); m.def("Producer", static_cast<std::shared_ptr<Node>(*)(const std::shared_ptr<Tensor>, const std::string&)>(&Producer), py::arg("tensor"), py::arg("name") = ""); declare_Producer<1>(m); diff --git a/python_binding/operator/pybind_ReLU.cpp b/python_binding/operator/pybind_ReLU.cpp index 820589d76507b39ca65ac2397614aabd1221fe3e..dbcb483e8089373bc8599c2d09fed00049e2a2ac 100644 --- a/python_binding/operator/pybind_ReLU.cpp +++ b/python_binding/operator/pybind_ReLU.cpp @@ -18,7 +18,9 @@ namespace py = pybind11; namespace Aidge { void init_ReLU(py::module& m) { - py::class_<ReLU_Op, std::shared_ptr<ReLU_Op>, Operator>(m, "ReLU_Op", py::multiple_inheritance()); + py::class_<ReLU_Op, std::shared_ptr<ReLU_Op>, Operator>(m, "ReLUOp", py::multiple_inheritance()) + .def("get_inputs_name", &ReLU_Op::getInputsName) + .def("get_outputs_name", &ReLU_Op::getOutputsName); m.def("ReLU", &ReLU, py::arg("name") = ""); } diff --git a/python_binding/operator/pybind_Softmax.cpp b/python_binding/operator/pybind_Softmax.cpp index 72ac1107181c1d7e2f578e31a965636dbb5c111b..8e50ab7c83bf43285b357cb803c0ce3eb42f4cc7 100644 --- a/python_binding/operator/pybind_Softmax.cpp +++ b/python_binding/operator/pybind_Softmax.cpp @@ -19,7 +19,9 @@ namespace py = pybind11; namespace Aidge { void init_Softmax(py::module& m) { - py::class_<Softmax_Op, std::shared_ptr<Softmax_Op>, Operator>(m, "Softmax_Op", py::multiple_inheritance()); + py::class_<Softmax_Op, std::shared_ptr<Softmax_Op>, Operator>(m, "SoftmaxOp", py::multiple_inheritance()) + .def("get_inputs_name", &Softmax_Op::getInputsName) + .def("get_outputs_name", &Softmax_Op::getOutputsName); m.def("Softmax", &Softmax, py::arg("name") = ""); } diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp index d1287c0a928ae2ad27a839cec1c3d3955da65538..04e39b11e58718dfcc5f9faef24b140132367700 100644 --- a/python_binding/pybind_core.cpp +++ b/python_binding/pybind_core.cpp @@ -30,6 +30,7 @@ void init_GenericOperator(py::module&); void init_LeakyReLU(py::module&); void init_MatMul(py::module&); void init_MaxPooling(py::module&); +void init_MetaOperatorDefs(py::module&); void init_Producer(py::module&); void init_ReLU(py::module&); void init_Softmax(py::module&); @@ -48,14 +49,8 @@ void init_Recipies(py::module&); void init_Scheduler(py::module&); void init_TensorUtils(py::module&); -void set_python_flag(){ - // Set an env variable to know if we run with ypthon or cpp - py::module os_module = py::module::import("os"); - os_module.attr("environ")["AIDGE_CORE_WITH_PYBIND"] = "1"; -} void init_Aidge(py::module& m){ - set_python_flag(); init_Data(m); init_Tensor(m); @@ -77,6 +72,7 @@ void init_Aidge(py::module& m){ init_LeakyReLU(m); init_MatMul(m); init_MaxPooling(m); + init_MetaOperatorDefs(m); init_ReLU(m); init_Softmax(m); diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..24ce15ab7ead32f98c7ac3edcd34bb2010ff4326 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +numpy diff --git a/setup.py b/setup.py index b88329e54feab78e39bd79be0a129030098e216a..60807df560510ad4cfacfdd2b178aca957306439 100644 --- a/setup.py +++ b/setup.py @@ -66,12 +66,13 @@ class CMakeBuild(build_ext): # used to launch setup.py to setup PythonInterp param_py = "-DPYTHON_EXECUTABLE=" + sys.executable + compile_type = 'Debug' install_path = os.path.join(sys.prefix, "lib", "libAidge") if "AIDGE_INSTALL" not in os.environ else os.environ["AIDGE_INSTALL"] - self.spawn(['cmake', str(cwd), param_py, '-DTEST=OFF', f'-DCMAKE_INSTALL_PREFIX:PATH={install_path}']) + self.spawn(['cmake', str(cwd), param_py, '-DTEST=OFF', f'-DCMAKE_INSTALL_PREFIX:PATH={install_path}', f'-DCMAKE_BUILD_TYPE={compile_type}']) if not self.dry_run: - self.spawn(['cmake', '--build', '.', '--config', 'Debug', '-j', max_jobs]) - self.spawn(['cmake', '--install', '.', '--config', 'Debug']) + self.spawn(['cmake', '--build', '.', '--config', compile_type, '-j', max_jobs]) + self.spawn(['cmake', '--install', '.', '--config', compile_type]) os.chdir(str(cwd)) aidge_package = build_lib / (get_project_name()) diff --git a/src/backend/OperatorImpl.cpp b/src/backend/OperatorImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..166754cc9fe9774d922ef523ab35f569673701fd --- /dev/null +++ b/src/backend/OperatorImpl.cpp @@ -0,0 +1,77 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <cassert> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/operator/Operator.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/ErrorHandling.hpp" + +Aidge::OperatorImpl::OperatorImpl(const Operator& op): + mOp(op), + mNbConsumedData(mOp.nbInputs(), 0), + mNbProducedData(mOp.nbOutputs(), 0) +{ + //ctor +} + +Aidge::NbElts_t Aidge::OperatorImpl::getNbRequiredData(const Aidge::IOIndex_t inputIdx) const { + assert(mOp.getInput(inputIdx) && "requires valid input"); + + // Requires the whole tensor by default + return std::static_pointer_cast<Tensor>(mOp.getInput(inputIdx))->size(); +} + +Aidge::NbElts_t Aidge::OperatorImpl::getNbRequiredProtected(IOIndex_t inputIdx) const { + assert(mOp.getInput(inputIdx) && "requires valid input"); + + // Protect the whole tensor by default + return std::static_pointer_cast<Tensor>(mOp.getInput(inputIdx))->size(); +} + +Aidge::NbElts_t Aidge::OperatorImpl::getRequiredMemory(const Aidge::IOIndex_t outputIdx, + const std::vector<Aidge::DimSize_t> &/*inputsSize*/) const { + assert(mOp.getOutput(outputIdx) && "requires valid output"); + + // Requires the whole tensor by default, regardless of available data on inputs + return std::static_pointer_cast<Tensor>(mOp.getOutput(outputIdx))->size(); +} + +Aidge::NbElts_t Aidge::OperatorImpl::getNbConsumedData(Aidge::IOIndex_t inputIdx) const { + assert(static_cast<std::size_t>(inputIdx) < mNbConsumedData.size()); + return mNbConsumedData[static_cast<std::size_t>(inputIdx)]; +} + +Aidge::NbElts_t Aidge::OperatorImpl::getNbProducedData(Aidge::IOIndex_t outputIdx) const { + assert(static_cast<std::size_t>(outputIdx) < mNbProducedData.size()); + return mNbProducedData[static_cast<std::size_t>(outputIdx)]; +} + +void Aidge::OperatorImpl::updateConsummerProducer(){ + // Update producer-consumer data + for (std::size_t inputIdx = 0; inputIdx < mNbConsumedData.size(); ++inputIdx) { + // each input is consumed by the minimum amount for a forward pass + mNbConsumedData[inputIdx] += getNbRequiredData(static_cast<IOIndex_t>(inputIdx)); + } + + for (std::size_t outputIdx = 0; outputIdx < mNbProducedData.size(); ++outputIdx) { + mNbProducedData[outputIdx] += getRequiredMemory(outputIdx, {}); + } +} + +void Aidge::OperatorImpl::forward() { + AIDGE_THROW_OR_ABORT(std::runtime_error, "forward() not implemented"); +} + +void Aidge::OperatorImpl::backward() { + AIDGE_THROW_OR_ABORT(std::runtime_error, "backward() not implemented"); +} diff --git a/src/graphRegex/GraphRegex.cpp b/src/graphRegex/GraphRegex.cpp index 4d2d14470a7f8ca72a3bc284231a3982de23fd1f..d8e45595d92fe3235f58c490791dde420627e9a1 100644 --- a/src/graphRegex/GraphRegex.cpp +++ b/src/graphRegex/GraphRegex.cpp @@ -119,6 +119,7 @@ void GraphRegex::setNodeKey(const std::string key,std::function<bool(NodePtr)> f if(mAllLambda.find(key) != mAllLambda.end()){ throw std::runtime_error(key + " is define"); } + mAllLambda[key] = f; _majConditionalInterpreterLambda(); } diff --git a/src/graphRegex/matchFsm/FsmGraph.cpp b/src/graphRegex/matchFsm/FsmGraph.cpp index a5c9945b387d99c92f15a3ab9909aa37a6a55054..a56474e042cc44a68938b1d19e19a0c6841cb8cb 100644 --- a/src/graphRegex/matchFsm/FsmGraph.cpp +++ b/src/graphRegex/matchFsm/FsmGraph.cpp @@ -1,6 +1,6 @@ #include "aidge/graphRegex/matchFsm/FsmGraph.hpp" -using namespace Aidge; +using namespace Aidge; @@ -15,7 +15,7 @@ FsmGraph::FsmGraph(const std::string query):mQuery(query){ if(startNodes.size() != startNodesFsm.size()){ throw std::runtime_error("bad number of Start nodes"); } - + std::vector<std::shared_ptr<FsmRunTimeContext>> walks; for(std::size_t i = 0; i < startNodes.size(); i++){ walks.push_back(std::make_shared<FsmRunTimeContext>(startNodesFsm[i],startNodes[i])); @@ -33,16 +33,16 @@ FsmGraph::FsmGraph(const std::string query):mQuery(query){ for(auto fsmContext : walks){ allContextSee.push_back(fsmContext); //if we are in a valid st we save it - //it's one solution of the posible solution of the matching + //it's one solution of the posible solution of the matching if(fsmContext->isOnValidState()){ - //not save 2 time the same end point + //not save 2 time the same end point if(!std::any_of(allValidContext.begin(), allValidContext.end(), [&](std::shared_ptr<Aidge::FsmRunTimeContext> oldValid) { return fsmContext->areEqual(oldValid); })){ allValidContext.push_back(fsmContext); } - + } //dont test 2 time a fsmContext @@ -69,7 +69,7 @@ FsmGraph::FsmGraph(const std::string query):mQuery(query){ /////////////// -// FSM construction +// FSM construction /////////////// const std::set<std::shared_ptr<FsmEdge>>& FsmGraph::getEdge(void){ return mEdges; @@ -141,7 +141,7 @@ void FsmGraph::mergeOneStartOneValid(const std::shared_ptr<FsmGraph> fsmGraph){ } unionG(fsmGraph); - //for loop useless but for future merge it's coudl be used + //for loop useless but for future merge it's coudl be used for(auto valid : validNodes){ valid->unValid(); for(auto start : startNodes){ @@ -164,9 +164,11 @@ void FsmGraph::incOriginAllNodeBy(std::size_t incr){ for(auto node :nodes){ node->incOrigin(incr); } + std::set<std::size_t> updatedOrigin; for(auto origin : mAllOrigin){ - origin += incr; + updatedOrigin.insert(origin + incr); } + mAllOrigin.swap(updatedOrigin); } void FsmGraph::_mergeNode(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest){ @@ -194,7 +196,7 @@ void FsmGraph::_mergeNode(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNod } } - //check is source is not in graph + //check is source is not in graph nodes = getNodes(); if(nodes.find(source) != nodes.end() ){ throw std::runtime_error("FsmGraph merge node not effective"); diff --git a/src/nodeTester/ConditionalInterpreter.cpp b/src/nodeTester/ConditionalInterpreter.cpp index db2bc40ce73c4e628af0a1dfae65fef6c9351177..59515d0acd77a6202e698ca1e8f1bb28b105266c 100644 --- a/src/nodeTester/ConditionalInterpreter.cpp +++ b/src/nodeTester/ConditionalInterpreter.cpp @@ -1,7 +1,7 @@ #include "aidge/nodeTester/ConditionalInterpreter.hpp" -using namespace Aidge; +using namespace Aidge; /////////////////////////////// @@ -63,7 +63,7 @@ using namespace Aidge; }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << "Error in test " << "\n\t" << e.what() << "\n"; - throw std::runtime_error(errorMessage.str()); + throw std::runtime_error(errorMessage.str()); } } @@ -80,7 +80,7 @@ using namespace Aidge; switch (node->getType()){ /////////////////////////////////// //OPERATOR - /////////////////////////////////// + /////////////////////////////////// case ConditionalTokenTypes::NOT: { visit(node->getChilds(),nodeOp); @@ -115,10 +115,10 @@ using namespace Aidge; /////////////////////////////////// //VALUE - /////////////////////////////////// - + /////////////////////////////////// + case ConditionalTokenTypes::KEY: - + break; case ConditionalTokenTypes::INTEGER: { @@ -128,7 +128,7 @@ using namespace Aidge; case ConditionalTokenTypes::FLOAT: { fStrToFloat(node); - + } break; case ConditionalTokenTypes::STRING: @@ -136,7 +136,7 @@ using namespace Aidge; fStrToStr(node); } break; - + case ConditionalTokenTypes::NODE: //TODO { @@ -151,14 +151,14 @@ using namespace Aidge; { visit(node->getChilds(),nodeOp); fLambda(node); - + } break; case ConditionalTokenTypes::BOOL: //TODO { ConditionalData* data = new ConditionalData; - + if(node->getValue() == "true"){ data->setValue<bool>(true); }else{ @@ -202,7 +202,7 @@ using namespace Aidge; void ConditionalInterpreter::fStrToFloat(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node) { - + ConditionalData* data = new ConditionalData; data->setValue<float>(std::stof(node->getValue())); mResolution.push_back(data); @@ -215,7 +215,7 @@ using namespace Aidge; mResolution.push_back(data); } - void ConditionalInterpreter::fLambda(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node) + void ConditionalInterpreter::fLambda(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node) { //if the lambda have input ConditionalData* data; @@ -224,7 +224,7 @@ using namespace Aidge; } catch (const std::exception& e) { std::ostringstream errorMessage; errorMessage << "Error in conditional interpretation when run the "<< node->getValue() <<" Lambda\n\t" << e.what() << "\n"; - throw std::runtime_error(errorMessage.str()); + throw std::runtime_error(errorMessage.str()); } clearRes(); @@ -234,7 +234,7 @@ using namespace Aidge; void ConditionalInterpreter::fEq(void) { if (mResolution.size() != 2){ - throw std::runtime_error("EQ need 2 arg and get :" + mResolution.size()); + throw std::runtime_error("EQ need 2 arg and get :" + std::to_string(mResolution.size())); } auto a = mResolution[0]; auto b = mResolution[1]; @@ -266,7 +266,7 @@ using namespace Aidge; void ConditionalInterpreter::fNeq(void) { if (mResolution.size() != 2){ - throw std::runtime_error("NEQ need 2 arg and get :" + mResolution.size()); + throw std::runtime_error("NEQ need 2 arg and get :" + std::to_string(mResolution.size())); } auto a = mResolution[0]; auto b = mResolution[1]; @@ -295,7 +295,7 @@ using namespace Aidge; void ConditionalInterpreter::fAnd(void) { if (mResolution.size() != 2){ - throw std::runtime_error("AND need 2 arg and get :" + mResolution.size()); + throw std::runtime_error("AND need 2 arg and get :" + std::to_string(mResolution.size())); } auto a = mResolution[0]; auto b = mResolution[1]; @@ -307,7 +307,7 @@ using namespace Aidge; ConditionalData* data = new ConditionalData; data->setValue<bool>( a->getValue<bool>() && b->getValue<bool>()); - + clearRes(); mResolution.push_back(data); @@ -316,7 +316,7 @@ using namespace Aidge; void ConditionalInterpreter::fOr(void) { if (mResolution.size() != 2){ - throw std::runtime_error("OR need 2 arg and get :" + mResolution.size()); + throw std::runtime_error("OR need 2 arg and get :" + std::to_string(mResolution.size())); } auto a = mResolution[0]; auto b = mResolution[1]; @@ -328,7 +328,7 @@ using namespace Aidge; ConditionalData* data = new ConditionalData; data->setValue<bool>( a->getValue<bool>() || b->getValue<bool>()); - + clearRes(); mResolution.push_back(data); @@ -337,7 +337,7 @@ using namespace Aidge; void ConditionalInterpreter::fNot() { if (mResolution.size() != 1){ - throw std::runtime_error("not need 1 arg and get :" + mResolution.size()); + throw std::runtime_error("NOT need 1 arg and get :" + std::to_string(mResolution.size())); } auto a = mResolution[0]; @@ -347,7 +347,7 @@ using namespace Aidge; ConditionalData* data = new ConditionalData; data->setValue<bool>( !a->getValue<bool>() ); - + clearRes(); mResolution.push_back(data); diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp index e9718fc694d29713797565d3ae8c8107cc7612de..c090427914390369452ce3259f47830f01ab1754 100644 --- a/unit_tests/operator/Test_MetaOperator.cpp +++ b/unit_tests/operator/Test_MetaOperator.cpp @@ -20,7 +20,7 @@ using namespace Aidge; TEST_CASE("[core/operators] MetaOperator", "[Operator]") { SECTION("PaddedConv") { - auto op = PaddedConv(1, 3, {3, 3}, "padded_conv", {1, 1}, {{{1, 1}, {1, 1}}}); + auto op = PaddedConv(1, 3, {3, 3}, "padded_conv", {1, 1}, {1, 1, 1, 1}); auto microGraph = std::dynamic_pointer_cast<MetaOperator_Op>(op->getOperator())->getMicroGraph();