-
Notifications
You must be signed in to change notification settings - Fork 404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
iox-#1394 iox-#1196 Address Axivion and clang-tidy findings for cxx::function_ref
#1456
Changes from all commits
e5bffb0
f49d5fc
c9c54dd
16fd6c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,15 +15,15 @@ | |
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// AXIVION DISABLE STYLE AutosarC++19_03-A16.2.3 : <type_traits> is included through "type_traits.hpp" | ||
|
||
#ifndef IOX_HOOFS_CXX_FUNCTION_REF_HPP | ||
#define IOX_HOOFS_CXX_FUNCTION_REF_HPP | ||
|
||
// AXIVION Next Line AutosarC++19_03-A16.2.2 : Needed for Expects and Ensures macros | ||
#include "iceoryx_hoofs/cxx/requires.hpp" | ||
#include "iceoryx_hoofs/cxx/type_traits.hpp" | ||
|
||
#include <cstddef> | ||
#include <iostream> | ||
#include <memory> | ||
#include <type_traits> | ||
|
||
namespace iox | ||
|
@@ -33,6 +33,12 @@ namespace cxx | |
template <typename SignatureType> | ||
class function_ref; | ||
|
||
/// @brief Type trait which checks for the same decayed type | ||
/// @tparam[in] T1 first type | ||
/// @tparam[in] T2 second type | ||
template <typename T1, typename T2> | ||
using has_same_decayed_type = typename std:: | ||
integral_constant<bool, bool(std::is_same<typename std::decay<T1>::type, typename std::decay<T2>::type>::value)>; | ||
|
||
/// @brief cxx::function_ref is a non-owning reference to a callable. | ||
/// | ||
|
@@ -61,41 +67,37 @@ class function_ref; | |
/// callback(); | ||
/// @endcode | ||
template <class ReturnType, class... ArgTypes> | ||
class function_ref<ReturnType(ArgTypes...)> | ||
class function_ref<ReturnType(ArgTypes...)> final | ||
{ | ||
using SignatureType = ReturnType(ArgTypes...); | ||
|
||
template <typename T1, typename T2> | ||
using has_same_decayed_type = typename std::integral_constant< | ||
bool, | ||
bool(std::is_same<typename std::decay<T1>::type, typename std::decay<T2>::type>::value)>; | ||
|
||
public: | ||
~function_ref() noexcept = default; | ||
|
||
function_ref(const function_ref&) noexcept = default; | ||
|
||
function_ref& operator=(const function_ref&) noexcept = default; | ||
function_ref& operator=(const function_ref&) & noexcept = default; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you add a reference here? Was it a warning? Should we do this for all copy assignments? So this rule forbids code like: std::move(a) = b; Which is valid and well defined but on the first glimpse weird. But take a look at this here template<typename T>
void initializeSomeArgs(T&& args) {
std::forward<T>(args) = someT;
} Here we take There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @elfenpiff In the example you provided it can have outside effect, depending on the type it is instantiated with so anything can go wrong but this is no reason not to allow since this holds for any references or pointers. While it will not be useful often (or at all) for @mossmaurice Edit: I thought about this. For It should not be done for all classes without considering their purpose, but it is rarely useful I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @elfenpiff That's due to AUTOSAR rule
is weird and does not make sense. You can find more examples in the AUTOSAR document. For your 2nd example, I suppose if it is needed in some cases, we should explicitly define There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If both implementations ( It is fine if we only have one like here. |
||
|
||
/// @brief Creates a function_ref with a callable whose lifetime has to be longer than function_ref | ||
/// @param[in] callable that is not a function_ref | ||
template <typename CallableType, | ||
typename = std::enable_if_t<!is_function_pointer<CallableType>::value | ||
&& !has_same_decayed_type<CallableType, function_ref>::value | ||
&& is_invocable<CallableType, ArgTypes...>::value>> | ||
function_ref(CallableType&& callable) noexcept; | ||
typename = std::enable_if_t<(!is_function_pointer<CallableType>::value) | ||
&& (!has_same_decayed_type<CallableType, function_ref>::value) | ||
&& (is_invocable<CallableType, ArgTypes...>::value)>> | ||
// AXIVION Next Line AutosarC++19_03-A12.1.4 : Implicit conversion is needed for lambdas | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
function_ref(CallableType&& callable) noexcept; // NOLINT(hicpp-explicit-conversions) | ||
|
||
/// @brief Creates a function_ref from a function pointer | ||
/// @param[in] function function reference to function we want to reference | ||
/// | ||
/// @note This overload is needed, as the general implementation | ||
/// will not work properly for function pointers. | ||
/// This ctor is not needed anymore once we can use user-defined-deduction guides (C++17) | ||
// Implicit conversion needed for method pointers | ||
// NOLINTNEXTLINE(hicpp-explicit-conversions) | ||
function_ref(ReturnType (&function)(ArgTypes...)) noexcept; | ||
|
||
function_ref(function_ref&& rhs) noexcept; | ||
|
||
function_ref& operator=(function_ref&& rhs) noexcept; | ||
function_ref& operator=(function_ref&& rhs) & noexcept; | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// @brief Calls the provided callable | ||
/// @param[in] Arguments are forwarded to the underlying function pointer | ||
|
@@ -112,9 +114,13 @@ class function_ref<ReturnType(ArgTypes...)> | |
ReturnType (*m_functionPointer)(void*, ArgTypes...){nullptr}; | ||
}; | ||
|
||
template <class ReturnType, class... ArgTypes> | ||
void swap(function_ref<ReturnType(ArgTypes...)>& lhs, function_ref<ReturnType(ArgTypes...)>& rhs) noexcept; | ||
|
||
} // namespace cxx | ||
} // namespace iox | ||
|
||
// AXIVION Next Line AutosarC++19_03-M16.0.1 : Include needed to split template declaration and definition | ||
#include "iceoryx_hoofs/internal/cxx/function_ref.inl" | ||
|
||
#endif | ||
#endif // IOX_HOOFS_CXX_FUNCTION_REF_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,15 +15,34 @@ | |
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#ifndef IOX_HOOFS_CXX_FUNCTION_REF_INL | ||
#define IOX_HOOFS_CXX_FUNCTION_REF_INL | ||
|
||
#include "iceoryx_hoofs/cxx/function_ref.hpp" | ||
#include "iceoryx_hoofs/cxx/requires.hpp" | ||
|
||
#include <memory> | ||
|
||
namespace iox | ||
{ | ||
namespace cxx | ||
{ | ||
template <class ReturnType, class... ArgTypes> | ||
template <typename CallableType, typename> | ||
// AXIVION Next Construct AutosarC++19_03-A12.1.2 : Members are initialized in the same manner, NSDMI with nullptr is | ||
// explicit | ||
// AXIVION Next Construct AutosarC++19_03-A8.4.6 : Only ArgTypes needs to be forwarded | ||
inline function_ref<ReturnType(ArgTypes...)>::function_ref(CallableType&& callable) noexcept | ||
// AXIVION Next Construct AutosarC++19_03-A5.2.4, AutosarC++19_03-A5.2.3, CertC++-EXP55 : Type-safety ensured by | ||
// casting back on call | ||
// NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast, cppcoreguidelines-pro-type-const-cast) | ||
: m_pointerToCallable(const_cast<void*>(reinterpret_cast<const void*>(std::addressof(callable)))) | ||
// AXIVION Next Line AutosarC++19_03-A15.4.4 : Lambda not 'noexcept' as callable might throw | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this argument the call operator must be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 15-4-4 states that "A declaration of non-throwing function shall contain noexcept specification.". I have no problem with that. However, any function that might throw cannot have this specificier, which means any user-defined function cannot. Hence I do not see why Axivion should complain unless it is better analyzing whether a function can throw or not than the compiler. And even modern compilers are in general not able to do this in general, non-trivial cases (like this one). Not sure the rule is useful to enforce with this tool for this reason. If the tool produces too many false positives/negatives it is useless for this rule (while the rule itself makes sense). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @elfenpiff I don't get this, the @MatthiasKillat Correct, we need to monitor that. |
||
, m_functionPointer([](void* target, ArgTypes... args) -> ReturnType { | ||
// AXIVION Next Construct AutosarC++19_03-A5.2.4, CertC++-EXP36 : The class design ensures a cast to the actual | ||
// type of target | ||
// AXIVION Next Construct AutosarC++19_03-A5.3.2, AutosarC++19_03-M5.2.8 : Check for 'nullptr' is | ||
// performed on call NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) | ||
return (*reinterpret_cast<typename std::add_pointer<CallableType>::type>(target))( | ||
std::forward<ArgTypes>(args)...); | ||
}) | ||
|
@@ -32,16 +51,23 @@ inline function_ref<ReturnType(ArgTypes...)>::function_ref(CallableType&& callab | |
|
||
template <class ReturnType, class... ArgTypes> | ||
inline function_ref<ReturnType(ArgTypes...)>::function_ref(ReturnType (&function)(ArgTypes...)) noexcept | ||
{ | ||
// the cast is required to work on POSIX systems | ||
m_pointerToCallable = reinterpret_cast<void*>(function); | ||
|
||
// AXIVION Next Construct AutosarC++19_03-A5.2.4, AutosarC++19_03-A5.2.4-M5.2.6 : Type-safety ensured by casting | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// back function pointer on call | ||
// NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) | ||
: m_pointerToCallable(reinterpret_cast<void*>(function)) | ||
, | ||
// the lambda does not capture and is thus convertible to a function pointer | ||
// (required by the C++ standard) | ||
m_functionPointer = [](void* target, ArgTypes... args) -> ReturnType { | ||
auto f = reinterpret_cast<ReturnType (*)(ArgTypes...)>(target); | ||
m_functionPointer([](void* target, ArgTypes... args) -> ReturnType { | ||
using PointerType = ReturnType (*)(ArgTypes...); | ||
// AXIVION Next Construct AutosarC++19_03-A5.2.4 : The class design ensures a cast to the actual type of target | ||
// NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast) | ||
PointerType f = reinterpret_cast<PointerType>(target); | ||
// AXIVION Next Line AutosarC++19_03-A5.3.2 : Check for 'nullptr' is performed on call | ||
return f(args...); | ||
}; | ||
}) | ||
{ | ||
} | ||
|
||
template <class ReturnType, class... ArgTypes> | ||
|
@@ -52,7 +78,7 @@ inline function_ref<ReturnType(ArgTypes...)>::function_ref(function_ref&& rhs) n | |
|
||
template <class ReturnType, class... ArgTypes> | ||
inline function_ref<ReturnType(ArgTypes...)>& | ||
function_ref<ReturnType(ArgTypes...)>::operator=(function_ref<ReturnType(ArgTypes...)>&& rhs) noexcept | ||
function_ref<ReturnType(ArgTypes...)>::operator=(function_ref<ReturnType(ArgTypes...)>&& rhs) & noexcept | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
if (this != &rhs) | ||
{ | ||
|
@@ -69,6 +95,7 @@ template <class ReturnType, class... ArgTypes> | |
inline ReturnType function_ref<ReturnType(ArgTypes...)>::operator()(ArgTypes... args) const noexcept | ||
{ | ||
// Expect that a callable was assigned beforehand | ||
// AXIVION Next Line AutosarC++19_03-M5.3.1 : 'nullptr' check shall be performed explicitly | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
cxx::Expects((m_pointerToCallable != nullptr) && "Empty function_ref invoked"); | ||
return m_functionPointer(m_pointerToCallable, std::forward<ArgTypes>(args)...); | ||
} | ||
|
@@ -81,10 +108,13 @@ inline void function_ref<ReturnType(ArgTypes...)>::swap(function_ref<ReturnType( | |
} | ||
|
||
template <class ReturnType, class... ArgTypes> | ||
// AXIVION Next Line AutosarC++19_03-A2.10.4 : Overload for swap(function_ref, function_ref) as in STL | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
inline void swap(function_ref<ReturnType(ArgTypes...)>& lhs, function_ref<ReturnType(ArgTypes...)>& rhs) noexcept | ||
mossmaurice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
lhs.swap(rhs); | ||
} | ||
|
||
} // namespace cxx | ||
} // namespace iox | ||
|
||
#endif // IOX_HOOFS_CXX_FUNCTION_REF_INL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the problem here? Does it not recognize that something from this header is used? (maybe because it is a macro?) ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MatthiasKillat AFAIU Axivion does not detect that the macro from
requires.hpp
is used.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike that tool failure results in these suppression comments.
-1 for the tool I guess