Skip to content

Commit

Permalink
Additions to stdutils
Browse files Browse the repository at this point in the history
  • Loading branch information
pierre-dejoue committed Jul 22, 2024
1 parent 99aa7c9 commit fdfc2db
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 50 deletions.
6 changes: 3 additions & 3 deletions src/picross/src/line_alternatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,9 @@ namespace {
}

private:
LineSpanW m_all_reduced_lines;
stdutils::span<LineAlternatives::NbAlt> m_nb_alternatives;
stdutils::span<char> m_recorded;
LineSpanW m_all_reduced_lines;
stdutils::Span<LineAlternatives::NbAlt> m_nb_alternatives;
stdutils::Span<char> m_recorded;
unsigned int m_max_line_length;
};

Expand Down
9 changes: 9 additions & 0 deletions src/stdutils/include/stdutils/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
namespace stdutils {
namespace chrono {

// For example to measure a duration in milliseconds:
//
// std::chrono::duration<float, std::milli> duration;
// {
// stdutils::chrono::DurationMeas meas(duration);
// // Do something
// }
// float duration_ms = time.count();
//
template<typename Rep, typename Period>
class DurationMeas
{
Expand Down
158 changes: 153 additions & 5 deletions src/stdutils/include/stdutils/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
#include <exception>
#include <filesystem>
#include <functional>
#include <iomanip>
#include <istream>
#include <fstream>
#include <limits>
#include <sstream>
#include <string_view>
#include <vector>
Expand All @@ -31,6 +33,7 @@ struct Severity
static constexpr SeverityCode EXCPT = -1;
static constexpr SeverityCode ERR = 1;
static constexpr SeverityCode WARN = 2;
static constexpr SeverityCode INFO = 3;
};

std::string_view str_severity_code(SeverityCode code);
Expand All @@ -39,14 +42,61 @@ using ErrorMessage = std::string_view;

using ErrorHandler = std::function<void(SeverityCode, ErrorMessage)>;

/**
* Floating point IO precision
*
* Set the output stream precision so that the round-trip fp -> text -> fp is exact.
* Return the original precision of the stream.
*/
template <typename F, typename CharT>
int accurate_fp_precision(std::basic_ostream<CharT, std::char_traits<CharT>>& out);

/**
* Floating point IO precision
*
* Set the output stream precision. If passed a negative value, set to the same precision as stdutlls::accurate_fp_precision
* Return the original precision of the stream.
*/
template <typename F, typename CharT>
int fp_precision(std::basic_ostream<CharT, std::char_traits<CharT>>& out, int precision = -1);

/**
* RAII class to save/restore stream format for numerical values
*/
template <typename CharT = char>
class SaveNumericFormat
{
public:
SaveNumericFormat(std::basic_ostream<CharT, std::char_traits<CharT>>& out);
~SaveNumericFormat();
private:
std::basic_ostream<CharT, std::char_traits<CharT>>& m_out;
std::ios_base::fmtflags m_flags;
std::streamsize m_precision;
};

/**
* Pass a file to a parser of std::basic_istream
*/
template <typename Ret, typename CharT>
using StreamParser = std::function<Ret(std::basic_istream<CharT, std::char_traits<CharT>>&, const stdutils::io::ErrorHandler&)>;

template <typename Ret, typename CharT>
Ret open_and_parse_file(const std::filesystem::path& filepath, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept;
Ret open_and_parse_txt_file(const std::filesystem::path& filepath, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept;
template <typename Ret, typename CharT>
Ret open_and_parse_bin_file(const std::filesystem::path& filepath, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept;


/**
* Save a file with a writer to std::basic_ostream
*/
template <typename Obj, typename CharT>
using StreamWriter = std::function<void(std::basic_ostream<CharT, std::char_traits<CharT>>&, const Obj&, const stdutils::io::ErrorHandler&)>;

template <typename Obj, typename CharT>
void save_txt_file(const std::filesystem::path& filepath, const StreamWriter<Obj, CharT>& stream_writer, const Obj& obj, const stdutils::io::ErrorHandler& err_handler) noexcept;
template <typename Obj, typename CharT>
void save_bin_file(const std::filesystem::path& filepath, const StreamWriter<Obj, CharT>& stream_writer, const Obj& obj, const stdutils::io::ErrorHandler& err_handler) noexcept;

/**
* LineStream: A wrapper around std::getline to count line nb
Expand Down Expand Up @@ -114,19 +164,60 @@ using SkipLineStream = Basic_SkipLineStream<char>;
template <typename CharT>
std::size_t countlines(std::basic_istream<CharT, std::char_traits<CharT>>& istream);


//
//
// IMPLEMENTATION
// Implementation
//
//


template <typename F, typename CharT>
int accurate_fp_precision(std::basic_ostream<CharT, std::char_traits<CharT>>& out)
{
// The precision max_digits10 (9 for float, 17 for double) ensures the round-trip "fp -> text -> fp" will be exact.
constexpr int max_fp_digits = std::numeric_limits<F>::max_digits10;
const int initial_fp_digits = static_cast<int>(out.precision());
out << std::setprecision(max_fp_digits);
return initial_fp_digits;
}

template <typename F, typename CharT>
int fp_precision(std::basic_ostream<CharT, std::char_traits<CharT>>& out, int precision)
{
const int initial_fp_digits = static_cast<int>(out.precision());
if (precision < 0)
out << std::setprecision(std::numeric_limits<F>::max_digits10);
else
out << std::setprecision(precision);
return initial_fp_digits;
}

template <typename CharT>
SaveNumericFormat<CharT>::SaveNumericFormat(std::basic_ostream<CharT, std::char_traits<CharT>>& out)
: m_out(out)
, m_flags(out.flags())
, m_precision(out.precision())
{ }

template <typename CharT>
SaveNumericFormat<CharT>::~SaveNumericFormat()
{
m_out.flags(m_flags);
m_out.precision(m_precision);
}

namespace details {

template <typename Ret, typename CharT>
Ret open_and_parse_file(const std::filesystem::path& filepath, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept
Ret open_and_parse_file(const std::filesystem::path& filepath, bool binary, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept
{
static_assert(std::is_nothrow_default_constructible_v<Ret>);
try
{
std::basic_ifstream<CharT> inputstream(filepath);
std::ios_base::openmode mode = std::ios_base::in;
if (binary) { mode |= std::ios_base::binary; }
std::basic_ifstream<CharT> inputstream(filepath, mode);
if (inputstream.is_open())
{
return stream_parser(inputstream, err_handler);
Expand All @@ -141,12 +232,69 @@ Ret open_and_parse_file(const std::filesystem::path& filepath, const StreamParse
catch(const std::exception& e)
{
std::stringstream oss;
oss << "Exception: " << e.what();
oss << "stdutils::io::details::open_and_parse_file(" << filepath << ", " << (binary ? "BIN" : "TXT") << "): " << e.what();
err_handler(stdutils::io::Severity::EXCPT, oss.str());
}
return Ret();
}

template <typename Obj, typename CharT>
void save_file(const std::filesystem::path& filepath, bool binary, const StreamWriter<Obj, CharT>& stream_writer, const Obj& obj, const stdutils::io::ErrorHandler& err_handler) noexcept
{
try
{
std::ios_base::openmode mode = std::ios_base::out;
if (binary) { mode |= std::ios_base::binary; }
std::basic_ofstream<CharT> outputstream(filepath, mode);
if (outputstream.is_open())
{
stream_writer(outputstream, obj, err_handler);
}
else
{
std::stringstream oss;
oss << "Cannot open file " << filepath;
err_handler(stdutils::io::Severity::FATAL, oss.str());
}
}
catch(const std::exception& e)
{
std::stringstream oss;
oss << "stdutils::io::details::save_file(" << filepath << ", " << (binary ? "BIN" : "TXT") << "): " << e.what();
err_handler(stdutils::io::Severity::EXCPT, oss.str());
}
}

} // namespace details

template <typename Ret, typename CharT>
Ret open_and_parse_txt_file(const std::filesystem::path& filepath, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept
{
const bool binary_file = false;
return details::open_and_parse_file(filepath, binary_file, stream_parser, err_handler);
}

template <typename Ret, typename CharT>
Ret open_and_parse_bin_file(const std::filesystem::path& filepath, const StreamParser<Ret, CharT>& stream_parser, const stdutils::io::ErrorHandler& err_handler) noexcept
{
const bool binary_file = true;
return details::open_and_parse_file(filepath, binary_file, stream_parser, err_handler);
}

template <typename Obj, typename CharT>
void save_txt_file(const std::filesystem::path& filepath, const StreamWriter<Obj, CharT>& stream_writer, const Obj& obj, const stdutils::io::ErrorHandler& err_handler) noexcept
{
const bool binary_file = false;
details::save_file(filepath, binary_file, stream_writer, obj, err_handler);
}

template <typename Obj, typename CharT>
void save_bin_file(const std::filesystem::path& filepath, const StreamWriter<Obj, CharT>& stream_writer, const Obj& obj, const stdutils::io::ErrorHandler& err_handler) noexcept
{
const bool binary_file = true;
details::save_file(filepath, binary_file, stream_writer, obj, err_handler);
}

template <typename CharT>
Basic_LineStream<CharT>::Basic_LineStream(stream_type& source)
: m_stream(source)
Expand Down
1 change: 1 addition & 0 deletions src/stdutils/include/stdutils/macros.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#pragma once

#define UNUSED(x) (void)(x)
#define IGNORE_RETURN (void)
101 changes: 69 additions & 32 deletions src/stdutils/include/stdutils/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This code is distributed under the terms of the MIT License
#pragma once

#include <array>
#include <cassert>
#include <cstddef>
#include <limits>
Expand All @@ -14,65 +15,101 @@ inline constexpr std::size_t dyn_extent = std::numeric_limits<std::size_t>::max(
// Imitation of std::span in C++20

template <typename T, std::size_t Sz = dyn_extent, class Enable = void>
class span {};
class Span {};

template <typename T>
class span<T, dyn_extent, void>
class Span<T, dyn_extent, void>
{
public:
span(T* ptr, std::size_t size) noexcept : m_ptr(ptr), m_size(size) { assert(m_ptr); }
span(const span<T>&) noexcept = default;
span(span<T>&&) noexcept = default;
span<T>& operator=(const span<T>&) noexcept = default;
span<T>& operator=(span<T>&&) noexcept = default;
using element_type = T;
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;

public:
Span() noexcept : m_ptr(nullptr), m_size(0) { }
Span(T* ptr, std::size_t size) noexcept : m_ptr(ptr), m_size(size) { assert(m_ptr); }
Span(const Span<T>&) noexcept = default;
Span(Span<T>&&) noexcept = default;
Span<T>& operator=(const Span<T>&) noexcept = default;
Span<T>& operator=(Span<T>&&) noexcept = default;

// For qualification conversions (e.g. non-const T to const T)
template <typename R>
span(const span<R>& other) noexcept : m_ptr(other.begin()), m_size(other.size()) { assert(m_ptr); }
Span(const Span<R>& other) noexcept : m_ptr(other.data()), m_size(other.size()) { assert(m_ptr); }

std::size_t size() const { return m_size; }
std::size_t size() const noexcept { return m_size; }
bool empty() const noexcept { return m_size == 0 || m_ptr == nullptr; }

T* data() { return m_ptr; }
T* begin() { return m_ptr; }
T* end() { return m_ptr + m_size; }
const T* data() const { return m_ptr; }
const T* begin() const { return m_ptr; }
const T* end() const { return m_ptr + m_size; }
pointer data() noexcept { return m_ptr; }
pointer begin() noexcept { return m_ptr; }
pointer end() noexcept { return m_ptr + m_size; }
const_pointer data() const noexcept { return m_ptr; }
const_pointer begin() const noexcept { return m_ptr; }
const_pointer end() const noexcept { return m_ptr + m_size; }

T& operator[](std::size_t idx) { assert(idx < m_size); return *(m_ptr + idx); }
const T& operator[](std::size_t idx) const { assert(idx < m_size); return *(m_ptr + idx); }
reference operator[](std::size_t idx) { assert(idx < m_size); return *(m_ptr + idx); }
const_reference operator[](std::size_t idx) const { assert(idx < m_size); return *(m_ptr + idx); }

private:
T* m_ptr;
std::size_t m_size;
};

template <typename C>
Span<typename C::value_type, dyn_extent> make_span(C& container)
{
using T = typename C::value_type;
return Span<T, dyn_extent>(container.data(), container.size());
}

template <typename C>
Span<const typename C::value_type, dyn_extent> make_const_span(const C& container)
{
using T = typename C::value_type;
return Span<const T, dyn_extent>(container.data(), container.size());
}

template <typename T, std::size_t Sz>
class span<T, Sz, std::enable_if_t<Sz != dyn_extent>>
class Span<T, Sz, std::enable_if_t<Sz != dyn_extent>>
{
using this_type = span<T, Sz, std::enable_if_t<Sz != dyn_extent>>;
using this_type = Span<T, Sz, std::enable_if_t<Sz != dyn_extent>>;
static_assert(Sz > 0);

public:
using element_type = T;
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;

public:
explicit span(T* ptr) noexcept : m_ptr(ptr) { assert(m_ptr); }
span(const this_type&) noexcept = default;
span(this_type&&) noexcept = default;
explicit Span(T* ptr) noexcept : m_ptr(ptr) { assert(m_ptr); }
explicit Span(std::array<T, Sz>& arr) noexcept : m_ptr(arr.data()) { assert(m_ptr); }
explicit Span(const std::array<std::remove_const_t<T>, Sz>& arr) noexcept : m_ptr(arr.data()) { assert(m_ptr); }
Span(const this_type&) noexcept = default;
Span(this_type&&) noexcept = default;
this_type& operator=(const this_type&) noexcept = default;
this_type& operator=(this_type&&) noexcept = default;

// For qualification conversions (e.g. non-const T to const T)
template <typename R>
span(const span<R, Sz, void>& other) noexcept : m_ptr(other.begin()) { assert(m_ptr); }
Span(const Span<R, Sz, void>& other) noexcept : m_ptr(other.begin()) { assert(m_ptr); }

constexpr std::size_t size() const { return Sz; }
constexpr std::size_t size() const noexcept { return Sz; }

T* data() { return m_ptr; }
T* begin() { return m_ptr; }
T* end() { return m_ptr + Sz; }
const T* data() const { return m_ptr; }
const T* begin() const { return m_ptr; }
const T* end() const { return m_ptr + Sz; }
pointer data() noexcept { return m_ptr; }
pointer begin() noexcept { return m_ptr; }
pointer end() noexcept { return m_ptr + Sz; }
const_pointer data() const noexcept { return m_ptr; }
const_pointer begin() const noexcept { return m_ptr; }
const_pointer end() const noexcept { return m_ptr + Sz; }

T& operator[](std::size_t idx) { assert(idx < Sz); return *(m_ptr + idx); }
const T& operator[](std::size_t idx) const { assert(idx < Sz); return *(m_ptr + idx); }
reference operator[](std::size_t idx) { assert(idx < Sz); return *(m_ptr + idx); }
const_reference operator[](std::size_t idx) const { assert(idx < Sz); return *(m_ptr + idx); }

private:
T* m_ptr;
Expand Down
Loading

0 comments on commit fdfc2db

Please sign in to comment.