From c4033b8c2028efeb9bac236e6b279bdcd8efec34 Mon Sep 17 00:00:00 2001 From: Claudius Holeksa Date: Tue, 18 Apr 2023 18:34:29 +0200 Subject: First commit to restructure the forstio repo --- .gitignore | 14 ++ SConstruct | 65 +++++++ forstio/SConscript | 7 + forstio/core/SConscript | 37 ++++ forstio/core/buffer.cpp | 434 ++++++++++++++++++++++++++++++++++++++++++ forstio/core/buffer.h | 195 +++++++++++++++++++ forstio/core/common.h | 75 ++++++++ forstio/core/error.cpp | 73 +++++++ forstio/core/error.h | 149 +++++++++++++++ forstio/core/string_literal.h | 40 ++++ 10 files changed, 1089 insertions(+) create mode 100644 .gitignore create mode 100644 SConstruct create mode 100644 forstio/SConscript create mode 100644 forstio/core/SConscript create mode 100644 forstio/core/buffer.cpp create mode 100644 forstio/core/buffer.h create mode 100644 forstio/core/common.h create mode 100644 forstio/core/error.cpp create mode 100644 forstio/core/error.h create mode 100644 forstio/core/string_literal.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e3ab66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# obj +*.so +*.o +*.a +*.os + +# scons +.sconsign.dblite + +# nix +result + +# regular target which is not really used +build diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..ead6b58 --- /dev/null +++ b/SConstruct @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=[]) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; + +Export('env') +SConscript('forstio/SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/forstio/SConscript b/forstio/SConscript new file mode 100644 index 0000000..08ac0f0 --- /dev/null +++ b/forstio/SConscript @@ -0,0 +1,7 @@ +#!/bin/false + +Import('env') + +# Export to other libs +Export('env'); +SConscript('core/SConscript'); diff --git a/forstio/core/SConscript b/forstio/core/SConscript new file mode 100644 index 0000000..184eed1 --- /dev/null +++ b/forstio/core/SConscript @@ -0,0 +1,37 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +core_env = env.Clone(); + +core_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +core_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += core_env.sources; +env.headers += core_env.headers; + +## Shared lib +objects_shared = [] +core_env.add_source_files(objects_shared, env.core_sources, shared=True); +core_env.library_shared = core_env.SharedLibrary('#build/forstio-core', [objects_shared]); + +## Static lib +objects_static = [] +core_env.add_source_files(objects_static, env.core_sources, shared=False); +core_env.library_static = core_env.StaticLibrary('#build/forstio-core', [objects_static]); + +# Set Alias +env.Alias('library_core', [core_env.library_shared, core_env.library_static]); +env.Alias('library', ['library_core']); + +# Install +env.Install('$prefix/lib/', [core_env.library_shared, core_env.library_static]); +env.Install('$prefix/include/forstio/', [core_env.headers]); diff --git a/forstio/core/buffer.cpp b/forstio/core/buffer.cpp new file mode 100644 index 0000000..2f1a6b5 --- /dev/null +++ b/forstio/core/buffer.cpp @@ -0,0 +1,434 @@ +#include "buffer.h" + +#include +#include +#include +#include +#include + +namespace saw { +error buffer::push(const uint8_t &value) { + size_t write_remain = write_composite_length(); + if (write_remain > 0) { + write() = value; + write_advance(1); + } else { + return recoverable_error("Buffer too small"); + } + return no_error(); +} + +error buffer::push(const uint8_t &buffer, size_t size) { + error error = write_require_length(size); + if (error.failed()) { + return error; + } + const uint8_t *buffer_ptr = &buffer; + while (size > 0) { + size_t segment = std::min(write_segment_length(), size); + memcpy(&write(), buffer_ptr, segment); + write_advance(segment); + size -= segment; + buffer_ptr += segment; + } + return no_error(); +} + +error buffer::pop(uint8_t &value) { + if (read_composite_length() > 0) { + value = read(); + read_advance(1); + } else { + return recoverable_error("Buffer too small"); + } + return no_error(); +} + +error buffer::pop(uint8_t &buffer, size_t size) { + if (read_composite_length() >= size) { + uint8_t *buffer_ptr = &buffer; + while (size > 0) { + size_t segment = std::min(read_segment_length(), size); + memcpy(buffer_ptr, &read(), segment); + read_advance(segment); + size -= segment; + buffer_ptr += segment; + } + } else { + return recoverable_error("Buffer too small"); + } + return no_error(); +} + +std::string buffer::to_string() const { + std::ostringstream oss; + for (size_t i = 0; i < read_composite_length(); ++i) { + oss << read(i); + } + return oss.str(); +} + +std::string buffer::to_hex() const { + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (size_t i = 0; i < read_composite_length(); ++i) { + oss << std::setw(2) << (uint16_t)read(i); + if ((i + 1) < read_composite_length()) { + oss << ((i % 4 == 3) ? '\n' : ' '); + } + } + return oss.str(); +} + +buffer_view::buffer_view(buffer &buffer) + : buffer_{buffer}, read_offset_{0}, write_offset_{0} {} + +size_t buffer_view::read_position() const { + return read_offset_ + buffer_.read_position(); +} + +size_t buffer_view::read_composite_length() const { + assert(read_offset_ <= buffer_.read_composite_length()); + if (read_offset_ > buffer_.read_composite_length()) { + return 0; + } + + return buffer_.read_composite_length() - read_offset_; +} + +size_t buffer_view::read_segment_length(size_t offset) const { + size_t off = offset + read_offset_; + assert(off <= buffer_.read_composite_length()); + if (off > buffer_.read_composite_length()) { + return 0; + } + + return buffer_.read_segment_length(off); +} + +void buffer_view::read_advance(size_t bytes) { + size_t offset = bytes + read_offset_; + assert(offset <= buffer_.read_composite_length()); + if (offset > buffer_.read_composite_length()) { + read_offset_ += buffer_.read_composite_length(); + return; + } + + read_offset_ += bytes; +} + +uint8_t &buffer_view::read(size_t i) { + size_t pos = i + read_offset_; + + assert(pos < buffer_.read_composite_length()); + + return buffer_.read(pos); +} + +const uint8_t &buffer_view::read(size_t i) const { + size_t pos = i + read_offset_; + + assert(pos < buffer_.read_composite_length()); + + return buffer_.read(pos); +} + +size_t buffer_view::write_position() const { + return write_offset_ + buffer_.write_position(); +} + +size_t buffer_view::write_composite_length() const { + assert(write_offset_ <= buffer_.write_composite_length()); + if (write_offset_ > buffer_.write_composite_length()) { + return 0; + } + + return buffer_.write_composite_length() - write_offset_; +} + +size_t buffer_view::write_segment_length(size_t offset) const { + size_t off = offset + write_offset_; + assert(off <= buffer_.write_composite_length()); + if (off > buffer_.write_composite_length()) { + return 0; + } + + return buffer_.write_segment_length(off); +} + +void buffer_view::write_advance(size_t bytes) { + size_t offset = bytes + write_offset_; + assert(offset <= buffer_.write_composite_length()); + if (offset > buffer_.write_composite_length()) { + write_offset_ += buffer_.write_composite_length(); + return; + } + + write_offset_ += bytes; +} + +uint8_t &buffer_view::write(size_t i) { + size_t pos = i + write_offset_; + + assert(pos < buffer_.write_composite_length()); + + return buffer_.write(pos); +} + +const uint8_t &buffer_view::write(size_t i) const { + size_t pos = i + write_offset_; + + assert(pos < buffer_.write_composite_length()); + + return buffer_.write(pos); +} + +error buffer_view::write_require_length(size_t bytes) { + return buffer_.write_require_length(bytes + write_offset_); +} + +size_t buffer_view::read_offset() const { return read_offset_; } + +size_t buffer_view::write_offset() const { return write_offset_; } + +ring_buffer::ring_buffer() : read_position_{0}, write_position_{0} { + buffer_.resize(RING_BUFFER_MAX_SIZE); +} + +ring_buffer::ring_buffer(size_t size) : read_position_{0}, write_position_{0} { + buffer_.resize(size); +} + +size_t ring_buffer::read_position() const { return read_position_; } + +/* + * If write is ahead of read it is a simple distance, but if read ist ahead of + * write then there are two segments + * + */ +size_t ring_buffer::read_composite_length() const { + return write_position() < read_position() + ? buffer_.size() - (read_position() - write_position()) + : (write_reached_read_ ? buffer_.size() + : write_position() - read_position()); +} + +/* + * If write is ahead then it's the simple distance again. If read is ahead it's + * until the end of the buffer/segment + */ +size_t ring_buffer::read_segment_length(size_t offset) const { + size_t read_composite = read_composite_length(); + assert(offset <= read_composite); + offset = std::min(offset, read_composite); + size_t remaining = read_composite - offset; + + size_t read_offset = read_position() + offset; + read_offset = read_offset >= buffer_.size() ? read_offset - buffer_.size() + : read_offset; + + // case 1 write is located before read and reached read + // then offset can be used normally + // case 2 write is located at read, but read reached write + // then it is set to zero by readCompositeLength() + // case 3 write is located after read + // since std::min you can use simple subtraction + if (write_position() < read_offset) { + return buffer_.size() - read_offset; + } + + if (write_position() == read_offset) { + if (remaining > 0) { + return buffer_.size() - read_offset; + } else { + return 0; + } + } + + return write_position() - read_offset; +} + +void ring_buffer::read_advance(size_t bytes) { + size_t read_composite = read_composite_length(); + + assert(bytes <= read_composite); + bytes = std::min(bytes, read_composite); + size_t advanced = read_position_ + bytes; + read_position_ = advanced >= buffer_.size() ? advanced - buffer_.size() + : advanced; + write_reached_read_ = bytes > 0 ? false : write_reached_read_; +} + +uint8_t &ring_buffer::read(size_t i) { + assert(i < read_composite_length()); + size_t pos = read_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} + +const uint8_t &ring_buffer::read(size_t i) const { + assert(i < read_composite_length()); + size_t pos = read_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} + +size_t ring_buffer::write_position() const { return write_position_; } + +size_t ring_buffer::write_composite_length() const { + return read_position() > write_position() + ? (read_position() - write_position()) + : (write_reached_read_ + ? 0 + : buffer_.size() - (write_position() - read_position())); +} + +size_t ring_buffer::write_segment_length(size_t offset) const { + size_t write_composite = write_composite_length(); + assert(offset <= write_composite); + offset = std::min(offset, write_composite); + + size_t write_offset = write_position() + offset; + write_offset = write_offset >= buffer_.size() + ? write_offset - buffer_.size() + : write_offset; + + if (read_position_ > write_offset) { + return read_position_ - write_offset; + } + + if (write_reached_read_) { + return 0; + } + + return buffer_.size() - write_offset; +} + +void ring_buffer::write_advance(size_t bytes) { + assert(bytes <= write_composite_length()); + size_t advanced = write_position_ + bytes; + write_position_ = advanced >= buffer_.size() ? advanced - buffer_.size() + : advanced; + + write_reached_read_ = + (write_position_ == read_position_ && bytes > 0 ? true : false); +} + +uint8_t &ring_buffer::write(size_t i) { + assert(i < write_composite_length()); + size_t pos = write_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} + +const uint8_t &ring_buffer::write(size_t i) const { + assert(i < write_composite_length()); + size_t pos = write_position_ + i; + pos = pos >= buffer_.size() ? pos - buffer_.size() : pos; + return buffer_[pos]; +} +/* + Error RingBuffer::increaseSize(size_t size){ + size_t old_size = buffer.size(); + size_t new_size = old_size + size; + buffer.resize(new_size); + if(readPosition() > writePosition() || (readPosition() == + writePosition() && write_reached_read)){ size_t remaining = old_size - + writePosition(); size_t real_remaining = 0; while(remaining > 0){ size_t + segment = std::min(remaining, size); memcpy(&buffer[new_size-segment], + &buffer[old_size-segment], segment); remaining -= segment; size -= segment; + old_size -= segment; + new_size -= segment; + } + } + + return noError(); + } +*/ +error ring_buffer::write_require_length(size_t bytes) { + size_t write_remain = write_composite_length(); + if (bytes > write_remain) { + return recoverable_error("Buffer too small"); + } + return no_error(); +} + +array_buffer::array_buffer(size_t size) + : read_position_{0}, write_position_{0} { + buffer_.resize(size); +} + +size_t array_buffer::read_position() const { return read_position_; } + +size_t array_buffer::read_composite_length() const { + return write_position_ - read_position_; +} + +size_t array_buffer::read_segment_length(size_t offset) const { + size_t read_composite = read_composite_length(); + assert(offset <= read_composite); + + offset = std::min(read_composite, offset); + size_t read_offset = read_position_ + offset; + + return write_position_ - read_offset; +} + +void array_buffer::read_advance(size_t bytes) { + assert(bytes <= read_composite_length()); + read_position_ += bytes; +} + +uint8_t &array_buffer::read(size_t i) { + assert(i < read_composite_length()); + + return buffer_[i + read_position_]; +} + +const uint8_t &array_buffer::read(size_t i) const { + assert(i + read_position_ < buffer_.size()); + + return buffer_[i + read_position_]; +} + +size_t array_buffer::write_position() const { return write_position_; } + +size_t array_buffer::write_composite_length() const { + assert(write_position_ <= buffer_.size()); + return buffer_.size() - write_position_; +} + +size_t array_buffer::write_segment_length(size_t offset) const { + assert(write_position_ <= buffer_.size()); + size_t write_composite = write_composite_length(); + + assert(offset <= write_composite); + offset = std::min(write_composite, offset); + size_t write_offset = write_position_ + offset; + + return buffer_.size() - write_offset; +} + +void array_buffer::write_advance(size_t bytes) { + assert(bytes <= write_composite_length()); + write_position_ += bytes; +} + +uint8_t &array_buffer::write(size_t i) { + assert(i < write_composite_length()); + return buffer_[i + write_position_]; +} + +const uint8_t &array_buffer::write(size_t i) const { + assert(i < write_composite_length()); + return buffer_[i + write_position_]; +} +error array_buffer::write_require_length(size_t bytes) { + size_t write_remain = write_composite_length(); + if (bytes > write_remain) { + return recoverable_error("Buffer too small"); + } + return no_error(); +} + +} // namespace saw diff --git a/forstio/core/buffer.h b/forstio/core/buffer.h new file mode 100644 index 0000000..4485ff1 --- /dev/null +++ b/forstio/core/buffer.h @@ -0,0 +1,195 @@ +#pragma once + +#include "error.h" + +#include +#include +#include +#include +#include +#include + +namespace saw { +/* + * Access class to reduce templated BufferSegments bloat + */ +class buffer { +protected: + ~buffer() = default; + +public: + virtual size_t read_position() const = 0; + virtual size_t read_composite_length() const = 0; + virtual size_t read_segment_length(size_t offset = 0) const = 0; + virtual void read_advance(size_t bytes) = 0; + + virtual uint8_t &read(size_t i = 0) = 0; + virtual const uint8_t &read(size_t i = 0) const = 0; + + virtual size_t write_position() const = 0; + virtual size_t write_composite_length() const = 0; + virtual size_t write_segment_length(size_t offset = 0) const = 0; + virtual void write_advance(size_t bytes) = 0; + + virtual uint8_t &write(size_t i = 0) = 0; + virtual const uint8_t &write(size_t i = 0) const = 0; + + /* + * Sometime buffers need to grow with a little more control + * than with push and pop for more efficient calls. + * There is nothing you can do if read hasn't been filled, but at + * least write can be increased if it is demanded. + */ + virtual error write_require_length(size_t bytes) = 0; + + error push(const uint8_t &value); + error push(const uint8_t &buffer, size_t size); + error pop(uint8_t &value); + error pop(uint8_t &buffer, size_t size); + + /* + * Subject to change + */ + std::string to_string() const; + std::string to_hex() const; +}; + +/* + * A viewer class for buffers. + * Working on the reference buffer invalidates the buffer view + */ +class buffer_view : public buffer { +private: + buffer &buffer_; + size_t read_offset_; + size_t write_offset_; + +public: + buffer_view(buffer &); + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; + + size_t read_offset() const; + size_t write_offset() const; +}; + +/* + * Buffer size meant for default allocation size of the ringbuffer since + * this class currently doesn't support proper resizing + */ +constexpr size_t RING_BUFFER_MAX_SIZE = 4096; +/* + * Buffer wrapping around if read caught up + */ +class ring_buffer final : public buffer { +private: + std::vector buffer_; + size_t read_position_; + size_t write_position_; + bool write_reached_read_ = false; + +public: + ring_buffer(); + ring_buffer(size_t size); + + inline size_t size() const { return buffer_.size(); } + + inline uint8_t &operator[](size_t i) { return buffer_[i]; } + inline const uint8_t &operator[](size_t i) const { return buffer_[i]; } + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; +}; + +/* + * One time buffer + */ +class array_buffer : public buffer { +private: + std::vector buffer_; + + size_t read_position_; + size_t write_position_; + +public: + array_buffer(size_t size); + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; +}; + +class chain_array_buffer : public buffer { +private: + std::deque buffer_; + + size_t read_position_; + size_t write_position_; + +public: + chain_array_buffer(); + + size_t read_position() const override; + size_t read_composite_length() const override; + size_t read_segment_length(size_t offset = 0) const override; + void read_advance(size_t bytes) override; + + uint8_t &read(size_t i = 0) override; + const uint8_t &read(size_t i = 0) const override; + + size_t write_position() const override; + size_t write_composite_length() const override; + size_t write_segment_length(size_t offset = 0) const override; + void write_advance(size_t bytes) override; + + uint8_t &write(size_t i = 0) override; + const uint8_t &write(size_t i = 0) const override; + + error write_require_length(size_t bytes) override; +}; +} // namespace saw diff --git a/forstio/core/common.h b/forstio/core/common.h new file mode 100644 index 0000000..a06c238 --- /dev/null +++ b/forstio/core/common.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include + +namespace saw { + +#define SAW_CONCAT_(x, y) x##y +#define SAW_CONCAT(x, y) SAW_CONCAT_(x, y) +#define SAW_UNIQUE_NAME(prefix) SAW_CONCAT(prefix, __LINE__) + +#define SAW_FORBID_COPY(classname) \ + classname(const classname &) = delete; \ + classname &operator=(const classname &) = delete + +#define SAW_FORBID_MOVE(classname) \ + classname(classname &&) = delete; \ + classname &operator=(classname &&) = delete + +#define SAW_DEFAULT_COPY(classname) \ + classname(const classname &) = default; \ + classname &operator=(const classname &) = default + +#define SAW_DEFAULT_MOVE(classname) \ + classname(classname &&) = default; \ + classname &operator=(classname &&) = default + +// In case of C++20 +#define SAW_ASSERT(expression) \ + assert(expression); \ + if (!(expression)) [[unlikely]] + +template using maybe = std::optional; + +template using own = std::unique_ptr; + +template using our = std::shared_ptr; + +template using lent = std::weak_ptr; + +template own heap(Args &&...args) { + return own(new T(std::forward(args)...)); +} + +template our share(Args &&...args) { + return std::make_shared(std::forward(args)...); +} + +template T instance() noexcept; + +template struct return_type_helper { + typedef decltype(instance()(instance())) Type; +}; +template struct return_type_helper { + typedef decltype(instance()()) Type; +}; + +template +using return_type = typename return_type_helper::Type; + +struct void_t {}; + +template struct void_fix { typedef T Type; }; +template <> struct void_fix { typedef void_t Type; }; +template using fix_void = typename void_fix::Type; + +template struct void_unfix { typedef T Type; }; +template <> struct void_unfix { typedef void Type; }; +template using unfix_void = typename void_unfix::Type; + +template constexpr bool always_false = false; + +} // namespace saw diff --git a/forstio/core/error.cpp b/forstio/core/error.cpp new file mode 100644 index 0000000..757e6a8 --- /dev/null +++ b/forstio/core/error.cpp @@ -0,0 +1,73 @@ +#include "error.h" + +namespace saw { +error::error() : error_{static_cast(0)} {} + +error::error(const std::string_view &msg, error::code code) + : error_message_{msg}, error_{static_cast(code)} {} + +error::error(std::string &&msg, error::code code) + : error_message_{std::move(msg)}, error_{static_cast(code)} {} + +error::error(error &&error) + : error_message_{std::move(error.error_message_)}, error_{std::move( + error.error_)} {} + +const std::string_view error::message() const { + + return std::visit( + [this](auto &&arg) -> const std::string_view { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + return std::string_view{arg}; + } else if constexpr (std::is_same_v) { + return arg; + } else { + return "Error in class Error. Good luck :)"; + } + }, + error_message_); +} + +bool error::failed() const { + return static_cast>(error_) != 0; +} + +bool error::is_critical() const { + return static_cast>(error_) < 0; +} + +bool error::is_recoverable() const { + return static_cast>(error_) > 0; +} + +error error::copy_error() const { + error error; + error.error_ = error_; + try { + error.error_message_ = error_message_; + } catch (const std::bad_alloc &) { + error.error_message_ = + std::string_view{"Error while copying Error string. Out of memory"}; + } + return error; +} + +error::code error::id() const { return static_cast(error_); } + +error make_error(const std::string_view &generic, error::code code) { + return error{generic, code}; +} + +error critical_error(const std::string_view &generic, error::code c) { + return make_error(generic, c); +} + +error recoverable_error(const std::string_view &generic, error::code c) { + return make_error(generic, c); +} + +error no_error() { return error{}; } + +} // namespace saw diff --git a/forstio/core/error.h b/forstio/core/error.h new file mode 100644 index 0000000..d8a5ae2 --- /dev/null +++ b/forstio/core/error.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include +#include + +#include + +#include "common.h" + +namespace saw { +/** + * Utility class for generating errors. Has a base distinction between + * critical and recoverable errors. Additional code ids can be provided to the + * constructor if additional distinctions are necessary. + */ +class error { +public: + enum class code : int16_t { + GenericCritical = -1, + GenericRecoverable = 1, + Disconnected = -99, + Exhausted = -98 + }; + +private: + std::variant error_message_; + code error_; + +public: + error(); + error(const std::string_view &msg, error::code id); + error(std::string &&msg, error::code id); + error(error &&error); + + SAW_FORBID_COPY(error); + + error &operator=(error &&) = default; + + const std::string_view message() const; + bool failed() const; + + bool is_critical() const; + bool is_recoverable() const; + + error copy_error() const; + + code id() const; +}; + +error make_error(const std::string_view &generic, error::code c); + +template +error make_error(const Formatter &formatter, error::code code, + const std::string_view &generic) { + try { + std::string error_msg = formatter(); + return error{std::move(error_msg), code}; + } catch (std::bad_alloc &) { + return error{generic, code}; + } +} + +error critical_error(const std::string_view &generic, + error::code c = error::code::GenericCritical); + +template +error critical_error(const Formatter &formatter, + const std::string_view &generic, + error::code c = error::code::GenericCritical) { + return make_error(formatter, c, generic); +} + +error recoverable_error(const std::string_view &generic, + error::code c = error::code::GenericRecoverable); + +template +error recoverable_error(const Formatter &formatter, + const std::string_view &generic, + error::code c = error::code::GenericRecoverable) { + return make_error(formatter, c, generic); +} + +error no_error(); + +/** + * Exception alternative. Since I code without exceptions this class is + * essentially a kind of exception replacement. + */ +template class error_or; + +class error_or_value { +public: + virtual ~error_or_value() = default; + + template error_or> &as() { + return static_cast> &>(*this); + } + + template const error_or> &as() const { + return static_cast> &>(*this); + } +}; + +template class error_or final : public error_or_value { +private: + std::variant> value_or_error_; + + static_assert(!std::is_same_v, + "Don't use internal private types"); + +public: + error_or() = default; + error_or(const fix_void &value) : value_or_error_{value} {} + + error_or(fix_void &&value) : value_or_error_{std::move(value)} {} + + error_or(const error &error) : value_or_error_{error} {} + error_or(error &&error) : value_or_error_{std::move(error)} {} + + bool is_value() const { + return std::holds_alternative>(value_or_error_); + } + + bool is_error() const { + return std::holds_alternative(value_or_error_); + } + + class error &error() { + return std::get(value_or_error_); + } + + const class error &error() const { + return std::get(value_or_error_); + } + + fix_void &value() { return std::get>(value_or_error_); } + + const fix_void &value() const { + return std::get>(value_or_error_); + } +}; + +template class error_or> { +private: + error_or() = delete; +}; + +} // namespace saw diff --git a/forstio/core/string_literal.h b/forstio/core/string_literal.h new file mode 100644 index 0000000..d530a54 --- /dev/null +++ b/forstio/core/string_literal.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace saw { +/** + * Helper object which creates a templated string from the provided string + * literal. It guarantees compile time uniqueness and thus allows using strings + * in template parameters. + */ +template class string_literal { +public: + constexpr string_literal(const CharT (&input)[N]) noexcept { + for (size_t i = 0; i < N; ++i) { + data[i] = input[i]; + } + } + + std::array data{}; + + constexpr std::string_view view() const noexcept { + return std::string_view{data.data()}; + } + + constexpr bool + operator==(const string_literal &) const noexcept = default; + + template + constexpr bool + operator==(const string_literal &) const noexcept { + return false; + } +}; + +template +constexpr string_literal operator""_key() { + return string_literal{Chars..., '\0'}; +} +} // namespace saw -- cgit v1.2.3