Skip to content

Commit

Permalink
[#1] Add CppRuntimeException from Zserio C++ runtime
Browse files Browse the repository at this point in the history
* without support for bitmasks
* new support for std::string_view
  • Loading branch information
Mi-La committed Aug 28, 2024
1 parent 4f2446e commit d1b52cf
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 0 deletions.
2 changes: 2 additions & 0 deletions runtime/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ project(ZserioCpp17Runtime)
set(ZSERIO_CPP17_RUNTIME_LIB_SRCS
zserio/BuiltInOperators.cpp
zserio/BuiltInOperators.h
zserio/CppRuntimeException.cpp
zserio/CppRuntimeException.h
zserio/CppRuntimeVersion.h
zserio/RebindAlloc.h
zserio/Span.h
Expand Down
83 changes: 83 additions & 0 deletions runtime/src/zserio/CppRuntimeException.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <algorithm>
#include <array>
#include <cstring>

#include "zserio/CppRuntimeException.h"

namespace zserio
{

CppRuntimeException::CppRuntimeException(const char* message) :
m_buffer()
{
append(message);
}

const char* CppRuntimeException::what() const noexcept
{
return m_buffer.data();
}

void CppRuntimeException::append(const char* message)
{
const size_t available = m_buffer.size() - 1 - m_len;
const size_t numCharsToAppend = strnlen(message, available);
appendImpl(Span<const char>(message, numCharsToAppend));
}

void CppRuntimeException::append(const char* message, size_t messageLen)
{
const size_t available = m_buffer.size() - 1 - m_len;
const size_t numCharsToAppend = std::min(messageLen, available);
appendImpl(Span<const char>(message, numCharsToAppend));
}

void CppRuntimeException::appendImpl(Span<const char> message)
{
if (message.size() > 0)
{
(void)std::copy(message.begin(), message.end(), m_buffer.begin() + m_len);
m_len += message.size();
}
m_buffer.at(m_len) = '\0';
}

CppRuntimeException& operator<<(CppRuntimeException& exception, const char* message)
{
exception.append(message);
return exception;
}

CppRuntimeException& operator<<(CppRuntimeException& exception, bool value)
{
return exception << (value ? "true" : "false");
}

CppRuntimeException& operator<<(CppRuntimeException& exception, float value)
{
std::array<char, 24> integerPartBuffer = {};
std::array<char, 24> floatingPartBuffer = {};
const char* integerPartString = nullptr;
const char* floatingPartString = nullptr;
convertFloatToString(integerPartBuffer, floatingPartBuffer, value, integerPartString, floatingPartString);
CppRuntimeException& result = exception << integerPartString;
if (floatingPartString != nullptr)
{
result = result << "." << floatingPartString;
}

return result;
}

CppRuntimeException& operator<<(CppRuntimeException& exception, double value)
{
return exception << (static_cast<float>(value));
}

CppRuntimeException& operator<<(CppRuntimeException& exception, std::string_view value)
{
exception.append(value.data(), value.size());
return exception;
}

} // namespace zserio
200 changes: 200 additions & 0 deletions runtime/src/zserio/CppRuntimeException.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#ifndef ZSERIO_CPP_RUNTIME_EXCEPTION_H_INC
#define ZSERIO_CPP_RUNTIME_EXCEPTION_H_INC

#include <array>
#include <exception>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

#include "zserio/Span.h"
#include "zserio/StringConvertUtil.h"

namespace zserio
{

/**
* Exception thrown when an error within the Zserio C++ runtime library occurs.
*/
class CppRuntimeException : public std::exception
{
public:
/**
* Constructor.
*
* \param message Description of the error.
*/
explicit CppRuntimeException(const char* message = "");

/**
* Method generated by default.
* \{
*/
~CppRuntimeException() override = default;

CppRuntimeException(const CppRuntimeException& other) = default;
CppRuntimeException& operator=(const CppRuntimeException& other) = default;

CppRuntimeException(CppRuntimeException&& other) = default;
CppRuntimeException& operator=(CppRuntimeException&& other) = default;
/**
* \}
*/

const char* what() const noexcept override;

/**
* Appends a message to the description.
*
* \param message Description of the error to append.
*/
void append(const char* message);

/**
* Appends a message of a known length to the description.
*
* \param message Description of the error to append.
* \param messageLen Length of the message.
*/
void append(const char* message, size_t messageLen);

private:
void appendImpl(Span<const char> message);

std::array<char, 512> m_buffer; // note fixed sized array is deeply copied on copy operations and it's OK
size_t m_len = 0;
};

/**
* Appends a message to the exception's description.
*
* \param exception Exception to modify.
* \param message Description of the error to append.
*
* \return Reference to the exception to allow operator chaining.
*/
CppRuntimeException& operator<<(CppRuntimeException& exception, const char* message);

/**
* Appends a bool value to the exception's description.
*
* \param exception Exception to modify.
* \param value Bool value to append.
*
* \return Reference to the exception to allow operator chaining.
*/
CppRuntimeException& operator<<(CppRuntimeException& exception, bool value);

/**
* Appends a float value to the exception's description.
*
* \param exception Exception to modify.
* \param value Float value to append.
*
* \return Reference to the exception to allow operator chaining.
*/
CppRuntimeException& operator<<(CppRuntimeException& exception, float value);

/**
* Appends a double value to the exception's description.
*
* \param exception Exception to modify.
* \param value Double value to append.
*
* \return Reference to the exception to allow operator chaining.
*/
CppRuntimeException& operator<<(CppRuntimeException& exception, double value);

/**
* Appends a string_view to the exception's description.
*
* \param exception Exception to modify.
* \param value String view to append.
*
* \return Reference to the exception to allow operator chaining.
*/
CppRuntimeException& operator<<(CppRuntimeException& exception, std::string_view value);

/**
* Appends an integral value to the exception's description.
*
* \param exception Exception to modify.
* \param value Integral value to append.
*
* \return Reference to the exception to allow operator chaining.
*/
template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
CppRuntimeException& operator<<(CppRuntimeException& exception, T value)
{
std::array<char, 24> buffer = {};
const char* stringValue = convertIntToString(buffer, value);
return exception << stringValue;
}

/**
* Appends a string value to the exception's description.
*
* \param exception Exception to modify.
* \param value String value to append.
*
* \return Reference to the exception to allow operator chaining.
*/
template <typename ALLOC>
CppRuntimeException& operator<<(
CppRuntimeException& exception, const std::basic_string<char, std::char_traits<char>, ALLOC>& value)
{
exception.append(value.c_str(), value.size());
return exception;
}

/**
* Appends a vector value to the exception's description.
*
* \param exception Exception to modify.
* \param value Vector value to append.
*
* \return Reference to the exception to allow operator chaining.
*/
template <typename T, typename ALLOC>
CppRuntimeException& operator<<(CppRuntimeException& exception, const std::vector<T, ALLOC>& value)
{
return exception << "vector([...], " << value.size() << ")";
}

namespace detail
{

// inspired by C++ ostreams - see https://cplusplus.github.io/LWG/issue1203
// note that e.g. in gcc implementation of ostreams there are two constraints, but the second one:
// typename = decltype(std::declval<EXCEPTION&>() << std::declval<const VALUE&>())
// is probably unnecessary and since it caused a compilation error in MSVC 2017 Conformance Mode,
// we intentionally skipped it (even though it was probably a compiler bug)
template <typename EXCEPTION, typename VALUE,
typename = typename std::enable_if<std::is_base_of<CppRuntimeException, EXCEPTION>::value, int>::type>
using CppRuntimeExceptionRValueInsertion = EXCEPTION&&;

} // namespace detail

/**
* Appends any value for which operator<< is implemented to the exception's description.
*
* Overload for rvalue to enable operator<< on a temporary object, .e.g. CppRuntimeException() << "value".
* Moreover note that this overload preserves the concrete type of the exception!
*
* \param exception Exception to modify.
* \param value Value to append.
*
* \return R-value reference to the original exception to allow operator chaining.
*/
template <typename CPP_RUNTIME_EXCEPTION, typename T>
detail::CppRuntimeExceptionRValueInsertion<CPP_RUNTIME_EXCEPTION, T> operator<<(
CPP_RUNTIME_EXCEPTION&& exception, const T& value)
{
exception << value;
return std::forward<CPP_RUNTIME_EXCEPTION>(exception);
}

} // namespace zserio

#endif // ifndef ZSERIO_CPP_RUNTIME_EXCEPTION_H_INC
1 change: 1 addition & 0 deletions runtime/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ gtest_add_library("${ZSERIO_CPP17_PROJECT_ROOT}/3rdparty/cpp/googletest")

set(ZSERIO_CPP17_RUNTIME_TEST_SRCS
zserio/BuiltInOperatorsTest.cpp
zserio/CppRuntimeExceptionTest.cpp
zserio/CppRuntimeVersionTest.cpp
zserio/SpanTest.cpp
zserio/StringConvertUtilTest.cpp
Expand Down
Loading

0 comments on commit d1b52cf

Please sign in to comment.