Skip to content

Commit

Permalink
iox-eclipse-iceoryx#1032 Implement minimal error handling
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Killat <[email protected]>
  • Loading branch information
MatthiasKillat committed Jun 17, 2022
1 parent d72c6e1 commit b7ac1e7
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 0 deletions.
25 changes: 25 additions & 0 deletions iceoryx_hoofs/include/iceoryx_hoofs/error_handling_2/api.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include "error_reporting.hpp"

using namespace eh;

// macros are required for SOURCE_LOCATION
// macros start with IOX_ but constants do not (avoids some clashes)

#define IOX_RAISE(...) \
{ \
raise(SOURCE_LOCATION, ##__VA_ARGS__); \
}

#define IOX_FATAL(...) \
{ \
raise(SOURCE_LOCATION, FATAL, ##__VA_ARGS__); \
}

// this will defer expression evaluation
#define IOX_RAISE_IF(expr, ...) \
{ \
auto e = [&] { return expr; }; \
raise_if(SOURCE_LOCATION, e, ##__VA_ARGS__); \
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

#include "error_codes.hpp"
#include "error_levels.hpp"
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#pragma once

#include <stdint.h>


using error_code_t = uint32_t;

enum class ErrorCode : error_code_t
{
Default = 0,
OutOfMemory = 1,
OutOfBounds = 2
};

static const char* errorCodes[] = {"Default", "OutOfMemory", "OutOfBounds"};

const char* error_code_to_name(ErrorCode code)

{
return errorCodes[(error_code_t)code];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#pragma once

#include <stdint.h>

namespace eh
{
using error_level_t = uint32_t;

#if 0
// not needed and not ideal for compile time dispatch
// enforce type safety
enum class ErrorLevel : error_level_t
{
Fatal = 0,
Error = 1,
Warning = 2
};

#endif

static const char* errorLevels[] = {"Fatal", "Error", "Warning"};

// note that the array apporach is by design (instead of switch) to allow a fast lookup
const char* error_level_to_name(error_level_t level)

{
return errorLevels[level];
}

// tag types

struct Fatal_t
{
operator error_level_t()
{
return 0;
}
};

struct Error_t
{
operator error_level_t()
{
return 1;
}
};

struct Warning_t
{
operator error_level_t()
{
return 2;
}
};

constexpr Fatal_t Fatal{};
constexpr Error_t Error{};
constexpr Warning_t Warning{};

} // namespace eh


// convenience
#define FATAL eh::Fatal
#define ERROR eh::Error
#define WARNING eh::Warning
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#pragma once

#include "error_codes.hpp" // this must be provided by the module
#include "error_levels.hpp" // do we want this dependency?
#include "location.hpp"

#include <iostream>
#include <type_traits>

namespace eh
{
void terminate()
{
std::cout << "TERMINATE" << std::endl;
}

template <class T>
struct is_fatal
{
static constexpr bool value = std::is_same<T, Fatal_t>::value;
};

template <class Level>
void report(const SourceLocation& location, Level level)
{
auto name = error_level_to_name(level);
std::cout << name << "@" << location.file << " " << location.line << " " << location.function << std::endl;
}

template <class Level, class Code>
void report(const SourceLocation& location, Level level, Code code)
{
auto levelName = error_level_to_name(level);
auto codeName = error_code_to_name(code);
std::cout << levelName << "@" << location.file << " " << location.line << " " << location.function << " : "
<< codeName << std::endl;
}

template <class Level>
void raise(const SourceLocation& location, Level level)
{
report(location, level);
// TODO: check whether it is a compile time if as expected
// could be constexpr explicitly with C++17 but should be optimized like this anyway
if (is_fatal<Level>::value)
{
terminate();
}
}

template <class Level, class Code>
void raise(const SourceLocation& location, Level level, Code code)
{
report(location, level, code);
if (is_fatal<Level>::value)
{
terminate();
}
}

template <class Expr, class Level, class Code>
void raise_if(const SourceLocation& location, const Expr& expr, Level level, Code code)
{
// it is possible to add a compile time condition that causes expr() never to be evaluated
// (e.g. for certain error levels)
if (expr())
{
report(location, level, code);
if (is_fatal<Level>::value)
{
terminate();
}
}
}

} // namespace eh
18 changes: 18 additions & 0 deletions iceoryx_hoofs/include/iceoryx_hoofs/error_handling_2/location.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

namespace eh
{
struct SourceLocation
{
const char* file;
unsigned line;
const char* function;
};

} // namespace eh

#define SOURCE_LOCATION \
eh::SourceLocation \
{ \
__FILE__, __LINE__, __func__ \
}
35 changes: 35 additions & 0 deletions iceoryx_hoofs/test/moduletests/test_error_handling_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "iceoryx_hoofs/error_handling_2/api.hpp"
#include "iceoryx_hoofs/error_handling_2/config.hpp"

#include "test.hpp"

#include <iostream>

namespace
{
using namespace ::testing;
using std::cout;
using std::endl;

TEST(EH_test, raise)
{
IOX_RAISE(WARNING);
IOX_RAISE(WARNING, ErrorCode::OutOfMemory);
IOX_RAISE(FATAL, ErrorCode::OutOfMemory);
}

TEST(EH_test, fatal)
{
IOX_FATAL();
IOX_FATAL(ErrorCode::OutOfMemory);
}

TEST(EH_test, raise_if)
{
int x = 11;
IOX_RAISE_IF(x > 10, WARNING, ErrorCode::OutOfBounds);

IOX_RAISE_IF(x > 20, WARNING, ErrorCode::OutOfBounds);
}

} // namespace

0 comments on commit b7ac1e7

Please sign in to comment.