Skip to content
Snippets Groups Projects
Commit 772b34b8 authored by kongr45gpen's avatar kongr45gpen Committed by Orestis Ousoultzoglou
Browse files

Add forbidden enum magic :mage:

parent d38e0b99
No related branches found
No related tags found
No related merge requests found
...@@ -51,6 +51,7 @@ clang-tidy: ...@@ -51,6 +51,7 @@ clang-tidy:
- clang-tidy-html clang-tidy-output.log - clang-tidy-html clang-tidy-output.log
- mv clang.html clang-tidy-html-report - mv clang.html clang-tidy-html-report
artifacts: artifacts:
when: always
paths: paths:
- ./clang-tidy-html-report - ./clang-tidy-html-report
...@@ -85,9 +86,8 @@ ikos: ...@@ -85,9 +86,8 @@ ikos:
script: script:
- cd $CI_PROJECT_DIR - cd $CI_PROJECT_DIR
- ikos-scan cmake . - ikos-scan cmake .
- ikos-scan make - ikos-scan make tests
- ikos tests.bc - ikos tests.bc
- ikos ecss_services.bc
- ikos-report -o=ikos-report.txt output.db - ikos-report -o=ikos-report.txt output.db
after_script: after_script:
- mv ikos-report.txt ikos-report - mv ikos-report.txt ikos-report
......
#pragma once
#include <string>
#include <utility>
#if !defined(__GNUC__) && !defined(__clang__)
#define __PRETTY_FUNCTION__ "E V = Sorry, feature not supported!"
#endif
/**
* A simplified version of https://github.com/Neargye/magic_enum, this file uses some gcc compiler options to provide an @ref enumName function.
* This function converts an enum value to a string. It's used for debugging purposes only, to improve the readability of values that are hidden
* behind enums.
*
* Use with care. NOT intended for a microcontroller.
*
* @note This functionality is designed for simple, sequence-like enums. Their values should start from 0 and increase 1-by-1 until the end. It will probably
* result in slow compilation or incorrect results if used for other, "weirder" enums.
*
* This hack aims to be safe from errors by using `constexpr` lavishly. Enum values which are out of bounds (e.g. not defined or beyond
* EnumMagic_::MaximumValues) will not throw errors or cause unexpected events, but will probably display some kind of string.
*
* @license MIT, Daniil Goncharov
*/
namespace EnumMagic_ {
/**
* The highest enum value that can be handled.
*
* Values higher than this will just result in a number or malformed text, but _not_ a compilation or runtime error.
*/
constexpr auto MaximumValues = 256;
/**
* Returns the identifier of an enum value.
*
* For example, if you define an enum:
* @code
* enum Something {
* First = 0,
* Second = 1
* }
* @endcode
* then calling this function with `Something::First` will return the string `"Something::First"`.
*
* Internally, this function uses the [`__PRETTY_FUNCTION__`](https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html)
* magic constant defined by GCC. The constant contains the template arguments of the function in a string,
* which themselves contain the identifier of the enum value.
*
* @note Tested with GCC version 11 only, other versions or compilers might return incorrect values
*
* @tparam E Enum type
* @tparam V Enum value (of type E)
* @return The identifier of the enum value
*/
template <typename E, E V>
constexpr std::basic_string_view<char> enumHack() {
static_assert(std::is_enum_v<E>, "enumHack() requires enum type.");
std::string_view function = __PRETTY_FUNCTION__;
auto pos = function.find("E V = ");
std::string_view output = function.substr(pos + 6);
output.remove_suffix(1);
return output;
}
/**
* Shortcut definition of enumHack for easier access
*/
template <typename Enum, Enum Value>
inline constexpr auto enumNameV = enumHack<Enum, Value>();
/**
* Given an enum type and a sequence of values, this function returns a list
*
* @tparam Indexes A list of potential enum values in numerical format
* @return An std::array of names for every enum value in the indexes
*/
template <typename Enum, std::size_t... Indexes>
constexpr auto names(std::index_sequence<Indexes...>) noexcept {
static_assert(std::is_enum_v<Enum>, "names() requires enum type.");
return std::array<std::string_view, sizeof...(Indexes)>{{enumNameV<Enum, static_cast<Enum>(Indexes)>...}};
}
/**
* For the specified Enum enumeration, this variable will contain a list of the names of all its values
*/
template <typename Enum>
inline constexpr std::array namesV = names<Enum>(std::make_index_sequence<MaximumValues>{});
/**
* Given a nested name (e.g. `Services::Parameter::value`), this function returns the innermost name (e.g. `value`)
*/
constexpr std::string_view removeNamespace(std::string_view in) {
auto pos = in.find_last_of("::");
return in.substr(pos + 1);
}
} // namespace EnumMagic_
/**
* Get the name of an enum value, as defined in the source code.
*/
template <typename Enum>
std::string enumName(Enum value) {
using namespace EnumMagic_;
auto index = static_cast<size_t>(value);
if (index >= namesV<Enum>.size()) {
return "(Unknown)";
}
return std::string(removeNamespace(namesV<Enum>[index]));
}
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <Message.hpp> #include <Message.hpp>
#include <Logger.hpp> #include <Logger.hpp>
#include <type_traits> #include <type_traits>
#include "Helpers/EnumMagic.hpp"
// TODO: Find a way to reduce the number of copies of this chunk // TODO: Find a way to reduce the number of copies of this chunk
template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType); template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType);
...@@ -27,7 +28,7 @@ void ErrorHandler::logError(const Message& message, ErrorType errorType) { ...@@ -27,7 +28,7 @@ void ErrorHandler::logError(const Message& message, ErrorType errorType) {
*/ */
<< abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error " << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error "
<< "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType) << "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType)
<< "]: " << std::underlying_type_t<ErrorType>(errorType); << "]: " << enumName(errorType) << " (" << static_cast<std::underlying_type_t<ErrorType>>(errorType) << ")";
} }
template <typename ErrorType> template <typename ErrorType>
...@@ -39,5 +40,5 @@ void ErrorHandler::logError(ErrorType errorType) { ...@@ -39,5 +40,5 @@ void ErrorHandler::logError(ErrorType errorType) {
*/ */
<< abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr)
<< " Error: " << " Error: "
<< std::underlying_type_t<ErrorType>(errorType); << enumName(errorType) << " (" << static_cast<std::underlying_type_t<ErrorType>>(errorType) << ")";
} }
...@@ -363,6 +363,8 @@ int main() { ...@@ -363,6 +363,8 @@ int main() {
LOG_NOTICE << "ECSS Services test complete"; LOG_NOTICE << "ECSS Services test complete";
ErrorHandler::reportInternalError(static_cast<ErrorHandler::InternalErrorType>(254));
std::cout << UTCTimestamp() << std::endl; std::cout << UTCTimestamp() << std::endl;
return 0; return 0;
} }
\ No newline at end of file
...@@ -13,5 +13,5 @@ TEST_CASE("Log string as message TM[128, 128]", "[service][st128]") { ...@@ -13,5 +13,5 @@ TEST_CASE("Log string as message TM[128, 128]", "[service][st128]") {
CHECK(report.messageType == DummyService::MessageType::LogString); CHECK(report.messageType == DummyService::MessageType::LogString);
char logOutput[39]; char logOutput[39];
report.readString(logOutput, 39); report.readString(logOutput, 39);
CHECK(strcmp(logOutput, "An amazing log that is very informative") == 0); CHECK(memcmp(logOutput, "An amazing log that is very informative", 39) == 0);
} }
...@@ -28,6 +28,8 @@ TEST_CASE("TC[6,2]", "[service][st06]") { ...@@ -28,6 +28,8 @@ TEST_CASE("TC[6,2]", "[service][st06]") {
CHECK(pStr[0] == 'h'); CHECK(pStr[0] == 'h');
CHECK(pStr[1] == 'R'); CHECK(pStr[1] == 'R');
CHECK(pStr[2] == 'h'); CHECK(pStr[2] == 'h');
free(pStr);
} }
TEST_CASE("TC[6,5]", "[service][st06]") { TEST_CASE("TC[6,5]", "[service][st06]") {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment