Add traits for Reactor arguments and return types
Provides a number of template trait types for Reactor primitive types:
CToReactorT<T> transforms the C-type (T) into a suitable Reactor equivalent. Pointer types are now generalized, allowing for translations of T** types.
IsRValue<T>::value and IsLValue<T>::value are true iff T is of the respective Reactor type.
ReactorType<T> resolves to the corresponding LValue Reactor type for T, where T can be a C-type, RValue or LValue.
ValueOf<T> returns a rr::Value* for a C-value, RValue or LValue. This is now used by a single definition of Return(), fixing bad type coercion which in turn produced illegal LLVM IR.
CanBeUsedAsReturn<T>::value is true iff T can be used as a function return type.
CanBeUsedAsParameter<T>::value is true iff T can be used as a function parameter type.
Function signatures are now statically checked that all parameter types and the return type are valid, providing a human readable error message if an invalid type is used (verified on gcc, clang and msvc).
Added a whole bunch of static asserts in ReactorUnitTests.cpp to sanity check the templates.
Bug: b/131914569
Change-Id: I3267354d6a3f68545079365a657757969ec0047d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30455
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index f079f32..56ab1da 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -4200,13 +4200,6 @@
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
}
- void Return(RValue<Int> ret)
- {
- Nucleus::createRet(ret.value);
- // Place any unreachable instructions in an unreferenced block.
- Nucleus::setInsertBlock(Nucleus::createBasicBlock());
- }
-
void branch(RValue<Bool> cmp, BasicBlock *bodyBB, BasicBlock *endBB)
{
Nucleus::createCondBr(cmp.value, bodyBB, endBB);
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 82fa23a..d201904 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -17,6 +17,7 @@
#include "Nucleus.hpp"
#include "Routine.hpp"
+#include "Traits.hpp"
#include <cassert>
#include <cstddef>
@@ -230,6 +231,8 @@
class RValue
{
public:
+ using rvalue_underlying_type = T;
+
explicit RValue(Value *rvalue);
#ifdef ENABLE_RR_DEBUG_INFO
@@ -2399,14 +2402,23 @@
void branch(RValue<Bool> cmp, BasicBlock *bodyBB, BasicBlock *endBB);
+ // ValueOf returns a rr::Value* for the given C-type, RValue<T>, or LValue<T>.
+ template <typename T>
+ inline Value* ValueOf(const T &v)
+ {
+ return ReactorType<T>(v).loadValue();
+ }
+
void Return();
- void Return(RValue<Int> ret);
template<class T>
- void Return(const Pointer<T> &ret);
-
- template<class T>
- void Return(RValue<Pointer<T>> ret);
+ void Return(const T &ret)
+ {
+ static_assert(CanBeUsedAsReturn< ReactorType<T> >::value, "Unsupported type for Return()");
+ Nucleus::createRet(ValueOf<T>(ret));
+ // Place any unreachable instructions in an unreferenced block.
+ Nucleus::setInsertBlock(Nucleus::createBasicBlock());
+ }
// Generic template, leave undefined!
template<typename FunctionType>
@@ -2416,6 +2428,9 @@
template<typename Return, typename... Arguments>
class Function<Return(Arguments...)>
{
+ // Static assert that the function signature is valid.
+ static_assert(sizeof(AssertFunctionSignatureIsValid<Return(Arguments...)>) >= 0, "Invalid function signature");
+
public:
Function();
@@ -2968,24 +2983,6 @@
return RValue<T>(Nucleus::createSelect(condition.value, trueValue, falseValue));
}
- template<class T>
- void Return(const Pointer<T> &ret)
- {
- RR_DEBUG_INFO_UPDATE_LOC();
- Nucleus::createRet(Nucleus::createLoad(ret.address, Pointer<T>::getType()));
- // Place any unreachable instructions in an unreferenced block.
- Nucleus::setInsertBlock(Nucleus::createBasicBlock());
- }
-
- template<class T>
- void Return(RValue<Pointer<T>> ret)
- {
- RR_DEBUG_INFO_UPDATE_LOC();
- Nucleus::createRet(ret.value);
- // Place any unreachable instructions in an unreferenced block.
- Nucleus::setInsertBlock(Nucleus::createBasicBlock());
- }
-
template<typename Return, typename... Arguments>
Function<Return(Arguments...)>::Function()
{
@@ -3069,26 +3066,6 @@
return ReinterpretCast<T>(val);
}
- template <typename T>
- inline Value* valueOf(RValue<T> v) { return v.value; }
-
- template <typename T>
- inline Value* valueOf(LValue<T> v) { return valueOf(RValue<T>(v.loadValue())); }
-
- template<typename T>
- struct CToReactor;
-
- template<> struct CToReactor<void> { using type = Void; };
- template<> struct CToReactor<int> { using type = Int; };
- template<> struct CToReactor<unsigned int> { using type = UInt; };
- template<> struct CToReactor<float> { using type = Float; };
- template<> struct CToReactor<int*> { using type = Pointer<Int>; };
- template<> struct CToReactor<float*> { using type = Pointer<Float>; };
-
- // Pointers to non-reactor types are treated as uint8_t*.
- template<typename T>
- struct CToReactor<T*> { using type = Pointer<Byte>; };
-
// Returns a reactor pointer to the fixed-address ptr.
RValue<Pointer<Byte>> ConstantPointer(void const * ptr);
@@ -3104,24 +3081,24 @@
class CallHelper<Return(Arguments...)>
{
public:
- using RReturn = typename CToReactor<Return>::type;
+ using RReturn = CToReactor<Return>;
- static inline RReturn Call(Return(fptr)(Arguments...), typename CToReactor<Arguments>::type... args)
+ static inline RReturn Call(Return(fptr)(Arguments...), CToReactor<Arguments>... args)
{
return RValue<RReturn>(rr::Call(
ConstantPointer(reinterpret_cast<void*>(fptr)),
RReturn::getType(),
- { valueOf(args) ... },
- { CToReactor<Arguments>::type::getType() ... }));
+ { ValueOf(args) ... },
+ { CToReactor<Arguments>::getType() ... }));
}
- static inline RReturn Call(Pointer<Byte> fptr, typename CToReactor<Arguments>::type... args)
+ static inline RReturn Call(Pointer<Byte> fptr, CToReactor<Arguments>... args)
{
return RValue<RReturn>(rr::Call(
fptr,
RReturn::getType(),
- { valueOf(args) ... },
- { CToReactor<Arguments>::type::getType() ... }));
+ { ValueOf(args) ... },
+ { CToReactor<Arguments>::getType() ... }));
}
};
@@ -3129,26 +3106,26 @@
class CallHelper<void(Arguments...)>
{
public:
- static inline void Call(void(fptr)(Arguments...), typename CToReactor<Arguments>::type... args)
+ static inline void Call(void(fptr)(Arguments...), CToReactor<Arguments>... args)
{
rr::Call(ConstantPointer(reinterpret_cast<void*>(fptr)),
Void::getType(),
- { valueOf(args) ... },
- { CToReactor<Arguments>::type::getType() ... });
+ { ValueOf(args) ... },
+ { CToReactor<Arguments>::getType() ... });
}
- static inline void Call(Pointer<Byte> fptr, typename CToReactor<Arguments>::type... args)
+ static inline void Call(Pointer<Byte> fptr, CToReactor<Arguments>... args)
{
rr::Call(fptr,
Void::getType(),
- { valueOf(args) ... },
- { CToReactor<Arguments>::type::getType() ... });
+ { ValueOf(args) ... },
+ { CToReactor<Arguments>::getType() ... });
}
};
// Calls the function pointer fptr with the given arguments args.
template<typename Return, typename ... Arguments>
- inline typename CToReactor<Return>::type Call(Return(fptr)(Arguments...), typename CToReactor<Arguments>::type... args)
+ inline CToReactor<Return> Call(Return(fptr)(Arguments...), CToReactor<Arguments>... args)
{
return CallHelper<Return(Arguments...)>::Call(fptr, args...);
}
diff --git a/src/Reactor/ReactorUnitTests.cpp b/src/Reactor/ReactorUnitTests.cpp
index b52b722..81c3700 100644
--- a/src/Reactor/ReactorUnitTests.cpp
+++ b/src/Reactor/ReactorUnitTests.cpp
@@ -1374,3 +1374,149 @@
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+
+////////////////////////////////
+// Trait compile time checks. //
+////////////////////////////////
+
+// Assert CToReactor resolves to expected types.
+static_assert(std::is_same<CToReactor<void>, Void>::value, "");
+static_assert(std::is_same<CToReactor<bool>, Bool>::value, "");
+static_assert(std::is_same<CToReactor<uint8_t>, Byte>::value, "");
+static_assert(std::is_same<CToReactor<int8_t>, SByte>::value, "");
+static_assert(std::is_same<CToReactor<int16_t>, Short>::value, "");
+static_assert(std::is_same<CToReactor<uint16_t>, UShort>::value, "");
+static_assert(std::is_same<CToReactor<int32_t>, Int>::value, "");
+static_assert(std::is_same<CToReactor<uint64_t>, Long>::value, "");
+static_assert(std::is_same<CToReactor<uint32_t>, UInt>::value, "");
+static_assert(std::is_same<CToReactor<float>, Float>::value, "");
+
+// Assert CToReactor for known pointer types resolves to expected types.
+static_assert(std::is_same<CToReactor<void*>, Pointer<Byte>>::value, "");
+static_assert(std::is_same<CToReactor<bool*>, Pointer<Bool>>::value, "");
+static_assert(std::is_same<CToReactor<uint8_t*>, Pointer<Byte>>::value, "");
+static_assert(std::is_same<CToReactor<int8_t*>, Pointer<SByte>>::value, "");
+static_assert(std::is_same<CToReactor<int16_t*>, Pointer<Short>>::value, "");
+static_assert(std::is_same<CToReactor<uint16_t*>, Pointer<UShort>>::value, "");
+static_assert(std::is_same<CToReactor<int32_t*>, Pointer<Int>>::value, "");
+static_assert(std::is_same<CToReactor<uint64_t*>, Pointer<Long>>::value, "");
+static_assert(std::is_same<CToReactor<uint32_t*>, Pointer<UInt>>::value, "");
+static_assert(std::is_same<CToReactor<float*>, Pointer<Float>>::value, "");
+static_assert(std::is_same<CToReactor<uint16_t**>, Pointer<Pointer<UShort>>>::value, "");
+static_assert(std::is_same<CToReactor<uint16_t***>, Pointer<Pointer<Pointer<UShort>>>>::value, "");
+
+// Assert CToReactor for unknown pointer types resolves to Pointer<Byte>.
+struct S{};
+static_assert(std::is_same<CToReactor<S*>, Pointer<Byte>>::value, "");
+static_assert(std::is_same<CToReactor<S**>, Pointer<Pointer<Byte>>>::value, "");
+static_assert(std::is_same<CToReactor<S***>, Pointer<Pointer<Pointer<Byte>>>>::value, "");
+
+// Assert IsRValue<> resolves true for RValue<> types.
+static_assert(IsRValue<RValue<Void>>::value, "");
+static_assert(IsRValue<RValue<Bool>>::value, "");
+static_assert(IsRValue<RValue<Byte>>::value, "");
+static_assert(IsRValue<RValue<SByte>>::value, "");
+static_assert(IsRValue<RValue<Short>>::value, "");
+static_assert(IsRValue<RValue<UShort>>::value, "");
+static_assert(IsRValue<RValue<Int>>::value, "");
+static_assert(IsRValue<RValue<Long>>::value, "");
+static_assert(IsRValue<RValue<UInt>>::value, "");
+static_assert(IsRValue<RValue<Float>>::value, "");
+
+// Assert IsLValue<> resolves true for LValue types.
+static_assert(IsLValue<Bool>::value, "");
+static_assert(IsLValue<Byte>::value, "");
+static_assert(IsLValue<SByte>::value, "");
+static_assert(IsLValue<Short>::value, "");
+static_assert(IsLValue<UShort>::value, "");
+static_assert(IsLValue<Int>::value, "");
+static_assert(IsLValue<Long>::value, "");
+static_assert(IsLValue<UInt>::value, "");
+static_assert(IsLValue<Float>::value, "");
+
+// Assert IsRValue<> resolves false for LValue types.
+static_assert(!IsRValue<Void>::value, "");
+static_assert(!IsRValue<Bool>::value, "");
+static_assert(!IsRValue<Byte>::value, "");
+static_assert(!IsRValue<SByte>::value, "");
+static_assert(!IsRValue<Short>::value, "");
+static_assert(!IsRValue<UShort>::value, "");
+static_assert(!IsRValue<Int>::value, "");
+static_assert(!IsRValue<Long>::value, "");
+static_assert(!IsRValue<UInt>::value, "");
+static_assert(!IsRValue<Float>::value, "");
+
+// Assert IsRValue<> resolves false for C types.
+static_assert(!IsRValue<void>::value, "");
+static_assert(!IsRValue<bool>::value, "");
+static_assert(!IsRValue<uint8_t>::value, "");
+static_assert(!IsRValue<int8_t>::value, "");
+static_assert(!IsRValue<int16_t>::value, "");
+static_assert(!IsRValue<uint16_t>::value, "");
+static_assert(!IsRValue<int32_t>::value, "");
+static_assert(!IsRValue<uint64_t>::value, "");
+static_assert(!IsRValue<uint32_t>::value, "");
+static_assert(!IsRValue<float>::value, "");
+
+// Assert IsLValue<> resolves false for RValue<> types.
+static_assert(!IsLValue<RValue<Void>>::value, "");
+static_assert(!IsLValue<RValue<Bool>>::value, "");
+static_assert(!IsLValue<RValue<Byte>>::value, "");
+static_assert(!IsLValue<RValue<SByte>>::value, "");
+static_assert(!IsLValue<RValue<Short>>::value, "");
+static_assert(!IsLValue<RValue<UShort>>::value, "");
+static_assert(!IsLValue<RValue<Int>>::value, "");
+static_assert(!IsLValue<RValue<Long>>::value, "");
+static_assert(!IsLValue<RValue<UInt>>::value, "");
+static_assert(!IsLValue<RValue<Float>>::value, "");
+
+// Assert IsLValue<> resolves false for Void type.
+static_assert(!IsLValue<Void>::value, "");
+
+// Assert IsLValue<> resolves false for C types.
+static_assert(!IsLValue<void>::value, "");
+static_assert(!IsLValue<bool>::value, "");
+static_assert(!IsLValue<uint8_t>::value, "");
+static_assert(!IsLValue<int8_t>::value, "");
+static_assert(!IsLValue<int16_t>::value, "");
+static_assert(!IsLValue<uint16_t>::value, "");
+static_assert(!IsLValue<int32_t>::value, "");
+static_assert(!IsLValue<uint64_t>::value, "");
+static_assert(!IsLValue<uint32_t>::value, "");
+static_assert(!IsLValue<float>::value, "");
+
+// Assert IsDefined<> resolves true for RValue<> types.
+static_assert(IsDefined<RValue<Void>>::value, "");
+static_assert(IsDefined<RValue<Bool>>::value, "");
+static_assert(IsDefined<RValue<Byte>>::value, "");
+static_assert(IsDefined<RValue<SByte>>::value, "");
+static_assert(IsDefined<RValue<Short>>::value, "");
+static_assert(IsDefined<RValue<UShort>>::value, "");
+static_assert(IsDefined<RValue<Int>>::value, "");
+static_assert(IsDefined<RValue<Long>>::value, "");
+static_assert(IsDefined<RValue<UInt>>::value, "");
+static_assert(IsDefined<RValue<Float>>::value, "");
+
+// Assert IsDefined<> resolves true for LValue types.
+static_assert(IsDefined<Void>::value, "");
+static_assert(IsDefined<Bool>::value, "");
+static_assert(IsDefined<Byte>::value, "");
+static_assert(IsDefined<SByte>::value, "");
+static_assert(IsDefined<Short>::value, "");
+static_assert(IsDefined<UShort>::value, "");
+static_assert(IsDefined<Int>::value, "");
+static_assert(IsDefined<Long>::value, "");
+static_assert(IsDefined<UInt>::value, "");
+static_assert(IsDefined<Float>::value, "");
+
+// Assert IsDefined<> resolves true for C types.
+static_assert(IsDefined<void>::value, "");
+static_assert(IsDefined<bool>::value, "");
+static_assert(IsDefined<uint8_t>::value, "");
+static_assert(IsDefined<int8_t>::value, "");
+static_assert(IsDefined<int16_t>::value, "");
+static_assert(IsDefined<uint16_t>::value, "");
+static_assert(IsDefined<int32_t>::value, "");
+static_assert(IsDefined<uint64_t>::value, "");
+static_assert(IsDefined<uint32_t>::value, "");
+static_assert(IsDefined<float>::value, "");
diff --git a/src/Reactor/Traits.hpp b/src/Reactor/Traits.hpp
new file mode 100644
index 0000000..ce6ac34
--- /dev/null
+++ b/src/Reactor/Traits.hpp
@@ -0,0 +1,175 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef rr_Traits_hpp
+#define rr_Traits_hpp
+
+#include <stdint.h>
+#include <type_traits>
+
+#ifdef Bool
+#undef Bool // b/127920555
+#endif
+
+namespace rr
+{
+ // Forward declarations
+ class Value;
+
+ class Void;
+ class Bool;
+ class Byte;
+ class SByte;
+ class Short;
+ class UShort;
+ class Int;
+ class UInt;
+ class Long;
+ class Half;
+ class Float;
+
+ template<class T> class Pointer;
+ template<class T> class LValue;
+ template<class T> class RValue;
+
+ // IsDefined<T>::value is true if T is a valid type, otherwise false.
+ template <typename T, typename Enable = void>
+ struct IsDefined
+ {
+ static constexpr bool value = false;
+ };
+
+ template <typename T>
+ struct IsDefined<T, typename std::enable_if<(sizeof(T)>0)>::type>
+ {
+ static constexpr bool value = true;
+ };
+
+ template <>
+ struct IsDefined<void>
+ {
+ static constexpr bool value = true;
+ };
+
+ // CToReactor<T> resolves to the corresponding Reactor type for the given C
+ // template type T.
+ template<typename T, typename ENABLE = void> struct CToReactorT;
+ template<typename T> using CToReactor = typename CToReactorT<T>::type;
+
+ // CToReactorT specializations for POD types.
+ template<> struct CToReactorT<void> { using type = Void; };
+ template<> struct CToReactorT<bool> { using type = Bool; };
+ template<> struct CToReactorT<uint8_t> { using type = Byte; };
+ template<> struct CToReactorT<int8_t> { using type = SByte; };
+ template<> struct CToReactorT<int16_t> { using type = Short; };
+ template<> struct CToReactorT<uint16_t> { using type = UShort; };
+ template<> struct CToReactorT<int32_t> { using type = Int; };
+ template<> struct CToReactorT<uint64_t> { using type = Long; };
+ template<> struct CToReactorT<uint32_t> { using type = UInt; };
+ template<> struct CToReactorT<float> { using type = Float; };
+
+ // CToReactorPtrT<T>::type resolves to the corresponding Reactor Pointer<>
+ // type for T*.
+ // For T types that have a CToReactorT<> specialization,
+ // CToReactorPtrT<T>::type resolves to Pointer< CToReactorT<T> >, otherwise
+ // CToReactorPtrT<T>::type resolves to Pointer<Byte>.
+ template<typename T, typename ENABLE = void> struct CToReactorPtrT { using type = Pointer<Byte>; };
+ template<typename T> using CToReactorPtr = typename CToReactorPtrT<T>::type;
+ template<typename T> struct CToReactorPtrT<T, typename std::enable_if< IsDefined< typename CToReactorT<T>::type >::value>::type >
+ {
+ using type = Pointer< typename CToReactorT<T>::type >;
+ };
+
+ // CToReactorT specialization for pointer types.
+ // For T types that have a CToReactorT<> specialization,
+ // CToReactorT<T*>::type resolves to Pointer< CToReactorT<T> >, otherwise
+ // CToReactorT<T*>::type resolves to Pointer<Byte>.
+ template<typename T>
+ struct CToReactorT<T, typename std::enable_if<std::is_pointer<T>::value>::type>
+ {
+ using elem = typename std::remove_pointer<T>::type;
+ using type = CToReactorPtr<elem>;
+ };
+
+ // CToReactorT specialization for void*.
+ // Maps to Pointer<Byte> instead of Pointer<Void>.
+ template<> struct CToReactorT<void*> { using type = Pointer<Byte>; };
+
+ // CToReactorT specialization for enum types.
+ template<typename T>
+ struct CToReactorT<T, typename std::enable_if<std::is_enum<T>::value>::type>
+ {
+ using underlying = typename std::underlying_type<T>::type;
+ using type = typename CToReactorT<underlying>::type;
+ };
+
+ // IsRValue::value is true if T is of type RValue<X>, where X is any type.
+ template <typename T, typename Enable = void> struct IsRValue { static constexpr bool value = false; };
+ template <typename T> struct IsRValue<T, typename std::enable_if<IsDefined<typename T::rvalue_underlying_type>::value>::type> { static constexpr bool value = true; };
+
+ // IsLValue::value is true if T is of, or derives from type LValue<T>.
+ template <typename T> struct IsLValue { static constexpr bool value = std::is_base_of<LValue<T>, T>::value; };
+
+ // ReactorType<T> returns the LValue Reactor type for T.
+ // T can be a C-type, RValue or LValue.
+ template<typename T, typename ENABLE = void> struct ReactorTypeT;
+ template<typename T> using ReactorType = typename ReactorTypeT<T>::type;
+ template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsDefined<CToReactor<T>>::value>::type> { using type = CToReactor<T>; };
+ template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsRValue<T>::value>::type> { using type = typename T::rvalue_underlying_type; };
+ template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsLValue<T>::value>::type> { using type = T; };
+
+ // Reactor types that can be used as a return type for a function.
+ template <typename T> struct CanBeUsedAsReturn { static constexpr bool value = false; };
+ template <> struct CanBeUsedAsReturn<Void> { static constexpr bool value = true; };
+ template <> struct CanBeUsedAsReturn<Int> { static constexpr bool value = true; };
+ template <> struct CanBeUsedAsReturn<UInt> { static constexpr bool value = true; };
+ template <> struct CanBeUsedAsReturn<Float> { static constexpr bool value = true; };
+ template <typename T> struct CanBeUsedAsReturn<Pointer<T>> { static constexpr bool value = true; };
+
+ // Reactor types that can be used as a parameter types for a function.
+ template <typename T> struct CanBeUsedAsParameter { static constexpr bool value = false; };
+ template <> struct CanBeUsedAsParameter<Int> { static constexpr bool value = true; };
+ template <> struct CanBeUsedAsParameter<UInt> { static constexpr bool value = true; };
+ template <> struct CanBeUsedAsParameter<Float> { static constexpr bool value = true; };
+ template <typename T> struct CanBeUsedAsParameter<Pointer<T>> { static constexpr bool value = true; };
+
+ // AssertParameterTypeIsValid statically asserts that all template parameter
+ // types can be used as a Reactor function parameter.
+ template<typename T, typename ... other>
+ struct AssertParameterTypeIsValid : AssertParameterTypeIsValid<other...>
+ {
+ static_assert(CanBeUsedAsParameter<T>::value, "Invalid parameter type");
+ };
+ template<typename T>
+ struct AssertParameterTypeIsValid<T>
+ {
+ static_assert(CanBeUsedAsParameter<T>::value, "Invalid parameter type");
+ };
+
+ // AssertFunctionSignatureIsValid statically asserts that the Reactor
+ // function signature is valid.
+ template<typename Return, typename... Arguments>
+ class AssertFunctionSignatureIsValid;
+ template<typename Return>
+ class AssertFunctionSignatureIsValid<Return(Void)> {};
+ template<typename Return, typename... Arguments>
+ class AssertFunctionSignatureIsValid<Return(Arguments...)>
+ {
+ static_assert(CanBeUsedAsReturn<Return>::value, "Invalid return type");
+ static_assert(sizeof(AssertParameterTypeIsValid<Arguments...>) >= 0, "");
+ };
+
+} // namespace rr
+
+#endif // rr_Traits_hpp