diff --git a/include/grenade/vx/vertex_concept.h b/include/grenade/vx/vertex_concept.h index 03eb5db95b887d8997b05a7cf667b5d8067797ee..c9c47dbbfc4d502421ace01806247ffcde9f85ca 100644 --- a/include/grenade/vx/vertex_concept.h +++ b/include/grenade/vx/vertex_concept.h @@ -33,33 +33,72 @@ template <typename Vertex> struct VertexConcept { /* Each vertex has a single output port. */ - static_assert( - std::is_same_v<decltype(&Vertex::output), Port (Vertex::*)() const>, - "Vertex missing output method."); + template <typename V, typename = void> + struct has_output : std::false_type + {}; + template <typename V> + struct has_output<V, std::void_t<decltype(&V::output)>> + { + constexpr static bool value = std::is_same_v<decltype(&V::output), Port (V::*)() const>; + }; + template <typename V> + constexpr static bool has_output_v = has_output<V>::value; + static_assert(has_output_v<Vertex>, "Vertex missing output method."); /* * The inputs are to be iterable (`std::array` or `std::vector`) of type `Port`. */ - static_assert( - detail::IsInputsReturn<decltype(std::declval<Vertex const>().inputs())>::value, - "Vertex missing inputs method."); + template <typename V, typename = void> + struct has_inputs : std::false_type + {}; + template <typename V> + struct has_inputs<V, std::void_t<decltype(&V::inputs)>> + { + constexpr static bool value = + detail::IsInputsReturn<decltype(std::declval<V const>().inputs())>::value; + }; + template <typename V> + constexpr static bool has_inputs_v = has_inputs<V>::value; + static_assert(has_inputs_v<Vertex>, "Vertex missing inputs method."); /* * The last element of the input ports can be variadic meaning it can be extended to an * arbitrary number (including zero) of distinct ports. For this to be the case `variadic_input` * has to be set to true. */ - static_assert( - std::is_same_v<decltype(Vertex::variadic_input), bool const>, - "Vertex missing variadic_input specifier."); + template <typename V, typename = void> + struct has_variadic_input : std::false_type + {}; + template <typename V> + struct has_variadic_input<V, std::void_t<decltype(&V::variadic_input)>> + { + constexpr static bool value = std::is_same_v<decltype(V::variadic_input), bool const>; + }; + template <typename V> + constexpr static bool has_variadic_input_v = has_variadic_input<V>::value; + static_assert(has_variadic_input_v<Vertex>, "Vertex missing variadic_input specifier."); /* * Vertices which are allowed connections between different execution instances are to set * `can_connect_different_execution_instances` to true. * TODO: this is rather a ConnectionType property, right? */ + template <typename V, typename = void> + struct has_can_connect_different_execution_instances : std::false_type + {}; + template <typename V> + struct has_can_connect_different_execution_instances< + V, + std::void_t<decltype(&V::can_connect_different_execution_instances)>> + { + constexpr static bool value = + std::is_same_v<decltype(V::can_connect_different_execution_instances), bool const>; + }; + template <typename V> + constexpr static bool has_can_connect_different_execution_instances_v = + has_can_connect_different_execution_instances<V>::value; static_assert( - std::is_same_v<decltype(Vertex::can_connect_different_execution_instances), bool const>, + has_can_connect_different_execution_instances_v<Vertex>, "Vertex missing can_connect_different_execution_instances specifier."); /** diff --git a/tests/sw/grenade/vx/test-vertex_concept.cpp b/tests/sw/grenade/vx/test-vertex_concept.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c18b33be4b15d0e7194cfda3cf4d4b5fd03332da --- /dev/null +++ b/tests/sw/grenade/vx/test-vertex_concept.cpp @@ -0,0 +1,84 @@ +#include <gtest/gtest.h> + +#include "grenade/vx/port.h" +#include <stdexcept> +#include <vector> + +/** + * Drop-in replacement for `static_assert` performing checks at runtime. + */ +struct exceptionalized_static_assert +{ + exceptionalized_static_assert(bool const cond, char const* what) + { + if (cond) { + static_cast<void>(what); + } else { + throw std::logic_error(what); + } + } + + exceptionalized_static_assert(bool const cond) : + exceptionalized_static_assert(cond, "static_assert failed.") + {} +}; + +#define TOKENPASTE(x, y) x##y +#define TOKENPASTE2(x, y) TOKENPASTE(x, y) +/** + * Replace `static_assert` by runtime checks in order to be able to unittest failures. + */ +#define static_assert(cond, ...) \ + exceptionalized_static_assert TOKENPASTE2(_test_, __COUNTER__) \ + { \ + cond, __VA_ARGS__ \ + } + +#include "grenade/vx/vertex_concept.h" + +using namespace grenade::vx; + +struct IncorrectOutput +{ + void output() const; +}; + +struct IncorrectInputs +{ + Port output() const; + + std::vector<int> inputs() const; +}; + +struct IncorrectVariadicInput +{ + Port output() const; + std::vector<Port> inputs() const; + + int const variadic_input; +}; + +struct IncorrectCanConnectDifferentExecutionInstances +{ + Port output() const; + std::vector<Port> inputs() const; + bool const variadic_input; + + int const can_connect_different_execution_instances; +}; + +struct Correct +{ + Port output() const; + std::vector<Port> inputs() const; + bool const variadic_input; + bool const can_connect_different_execution_instances; +}; + +TEST(VertexConcept, General) +{ + EXPECT_THROW(VertexConcept<IncorrectOutput>{}, std::logic_error); + EXPECT_THROW(VertexConcept<IncorrectInputs>{}, std::logic_error); + EXPECT_THROW(VertexConcept<IncorrectVariadicInput>{}, std::logic_error); + EXPECT_THROW(VertexConcept<IncorrectCanConnectDifferentExecutionInstances>{}, std::logic_error); +}