IVGCVSW-6639 Add GetParameters to IConnectableLayer

Change-Id: Id55a460ecb510f5b30235b03f54390f2c8188fc2
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
diff --git a/delegate/src/MultiLayerFacade.hpp b/delegate/src/MultiLayerFacade.hpp
index fa23980..ccd011a 100644
--- a/delegate/src/MultiLayerFacade.hpp
+++ b/delegate/src/MultiLayerFacade.hpp
@@ -129,9 +129,14 @@
         return m_FirstLayer->GetType();
     }
 
+    virtual const armnn::BaseDescriptor& GetParameters() const override { return m_NullDescriptor; }
+
 private:
     armnn::IConnectableLayer* m_FirstLayer;
     armnn::IConnectableLayer* m_LastLayer;
+
+    // to satisfy the GetParameters method need to hand back a NullDescriptor
+    armnn::NullDescriptor m_NullDescriptor;
 };
 
 } // namespace armnnDelegate
diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp
index b37db54..280c18e 100644
--- a/include/armnn/Descriptors.hpp
+++ b/include/armnn/Descriptors.hpp
@@ -21,9 +21,17 @@
 /// Base class for all descriptors.
 struct BaseDescriptor
 {
+    virtual bool IsNull() const { return false; }
     virtual ~BaseDescriptor() = default;
 };
 
+/// Null Descriptor used as a return value from the IConnectableLayer GetParameters method
+/// by layers which do not have a descriptor
+struct NullDescriptor : BaseDescriptor
+{
+    bool IsNull() const override { return true; }
+};
+
 /// An ActivationDescriptor for the ActivationLayer.
 struct ActivationDescriptor : BaseDescriptor
 {
diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp
index a48ee25..073f119 100644
--- a/include/armnn/INetwork.hpp
+++ b/include/armnn/INetwork.hpp
@@ -112,6 +112,13 @@
     /// Returns the armnn::LayerType of this layer
     virtual LayerType GetType() const = 0;
 
+    /// If the layer has a descriptor return it.
+    /// The base descriptor can then be cast to the correct descriptor class.
+    /// If the layer has no associated descriptor a struct of type NullDescriptor will be returned.
+    /// Note: NullDescriptors can be detected because they return true when
+    /// the BaseDescriptor IsNull function is invoked.
+    virtual const BaseDescriptor& GetParameters() const = 0;
+
 protected:
       /// Objects are not deletable via the handle
     ~IConnectableLayer() {}
diff --git a/src/armnn/Layer.cpp b/src/armnn/Layer.cpp
index 98fc14b..88bc6d5 100644
--- a/src/armnn/Layer.cpp
+++ b/src/armnn/Layer.cpp
@@ -17,6 +17,9 @@
 namespace armnn
 {
 
+// Instantiate the static member variable
+NullDescriptor Layer::m_NullDescriptor;
+
 void InputSlot::Insert(Layer& layer)
 {
     ARMNN_ASSERT(layer.GetNumOutputSlots() == 1);
diff --git a/src/armnn/Layer.hpp b/src/armnn/Layer.hpp
index f2ea6cb..ecfa1d9 100644
--- a/src/armnn/Layer.hpp
+++ b/src/armnn/Layer.hpp
@@ -351,6 +351,8 @@
         m_AdditionalInfoObject = additionalInfo;
     }
 
+    virtual const BaseDescriptor& GetParameters() const override { return m_NullDescriptor; }
+
 protected:
     // Graph needs access to the virtual destructor.
     friend class Graph;
@@ -427,6 +429,10 @@
 
     std::list<std::string> m_RelatedLayerNames;
 
+    /// returned by layers which have no parameters associated with them.
+    /// has to be a member as it is returned as a const reference
+    /// declared static so that there is only ever one of them in memory
+    static NullDescriptor m_NullDescriptor;
 };
 
 // A layer user-provided data can be bound to (e.g. inputs, outputs).
diff --git a/src/armnn/layers/LayerWithParameters.hpp b/src/armnn/layers/LayerWithParameters.hpp
index 952eff6..2ac16c5 100644
--- a/src/armnn/layers/LayerWithParameters.hpp
+++ b/src/armnn/layers/LayerWithParameters.hpp
@@ -15,7 +15,7 @@
 public:
     using DescriptorType = Parameters;
 
-    const Parameters& GetParameters() const { return m_Param; }
+    const Parameters& GetParameters() const override { return m_Param; }
 
     /// Helper to serialize the layer parameters to string
     /// (currently used in DotSerializer and company).
diff --git a/src/armnn/test/NetworkTests.cpp b/src/armnn/test/NetworkTests.cpp
index d4edf5d..66dbb4e 100644
--- a/src/armnn/test/NetworkTests.cpp
+++ b/src/armnn/test/NetworkTests.cpp
@@ -600,4 +600,57 @@
     CHECK(standIn->GetOutputSlot(1).GetConnection(0) == &output1->GetInputSlot(0));
 }
 
+TEST_CASE("ObtainConv2DDescriptorFromIConnectableLayer")
+{
+    armnn::NetworkImpl net;
+
+    unsigned int dims[] = { 10,1,1,1 };
+    std::vector<float> convWeightsData(10);
+    armnn::ConstTensor weights(armnn::TensorInfo(4, dims, armnn::DataType::Float32, 0.0f, 0, true), convWeightsData);
+
+    armnn::Convolution2dDescriptor convDesc2d;
+    convDesc2d.m_PadLeft = 2;
+    convDesc2d.m_PadRight = 3;
+    convDesc2d.m_PadTop = 4;
+    convDesc2d.m_PadBottom = 5;
+    convDesc2d.m_StrideX = 2;
+    convDesc2d.m_StrideY = 1;
+    convDesc2d.m_DilationX = 3;
+    convDesc2d.m_DilationY = 3;
+    convDesc2d.m_BiasEnabled = false;
+    convDesc2d.m_DataLayout = armnn::DataLayout::NCHW;
+    armnn::IConnectableLayer* const convLayer = net.AddConvolution2dLayer(convDesc2d,
+                                                                          weights,
+                                                                          armnn::EmptyOptional(),
+                                                                          "conv layer");
+    CHECK(convLayer);
+
+    const armnn::BaseDescriptor& descriptor = convLayer->GetParameters();
+    CHECK(descriptor.IsNull() == false);
+    const armnn::Convolution2dDescriptor& originalDescriptor =
+        static_cast<const armnn::Convolution2dDescriptor&>(descriptor);
+    CHECK(originalDescriptor.m_PadLeft == 2);
+    CHECK(originalDescriptor.m_PadRight == 3);
+    CHECK(originalDescriptor.m_PadTop == 4);
+    CHECK(originalDescriptor.m_PadBottom == 5);
+    CHECK(originalDescriptor.m_StrideX == 2);
+    CHECK(originalDescriptor.m_StrideY == 1);
+    CHECK(originalDescriptor.m_DilationX == 3);
+    CHECK(originalDescriptor.m_DilationY == 3);
+    CHECK(originalDescriptor.m_BiasEnabled == false);
+    CHECK(originalDescriptor.m_DataLayout == armnn::DataLayout::NCHW);
+}
+
+TEST_CASE("CheckNullDescriptor")
+{
+    armnn::NetworkImpl net;
+    armnn::IConnectableLayer* const addLayer = net.AddAdditionLayer();
+
+    CHECK(addLayer);
+
+    const armnn::BaseDescriptor& descriptor = addLayer->GetParameters();
+    // additional layer has no descriptor so a NullDescriptor will be returned
+    CHECK(descriptor.IsNull() == true);
+}
+
 }