Initial commit

This commit is contained in:
gaztin committed 2026-07-11 19:53:00 +02:00
commit d1890b6e18
16 files changed
+702

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
#include "file-stream.h"
#include "core-library/io/io-helper.h"
#include <system_error>
core::file_stream::~file_stream()
{
close();
}
core::file_stream::file_stream(file_stream&& other) noexcept
: m_file_handle(std::exchange(other.m_file_handle, nullptr))
{
}
core::file_stream& core::file_stream::operator=(file_stream&& other)
{
if (this != &other)
{
core::close_file(m_file_handle);
m_file_handle = std::exchange(other.m_file_handle, nullptr);
}
return *this;
}
core::file_stream core::file_stream::open_for_reading(const std::filesystem::path& file_path)
{
file_stream stream{};
stream.m_file_handle = core::open_file(file_path.string().c_str(), "rb");
return stream;
}
std::string core::file_stream::file_contents_as_string(const std::filesystem::path& file_path)
{
file_stream stream = open_for_reading(file_path);
return stream.read_all_as_string();
}
std::string core::file_stream::read_all_as_string()
{
if (!m_file_handle)
return {};
core::seek_file_to_end(m_file_handle);
const std::size_t file_size = static_cast<std::size_t>(core::get_file_offset(m_file_handle));
core::seek_file_to_beginning(m_file_handle);
if (file_size == 0)
return {};
std::string result(file_size, '\0');
core::read_from_file(&result[0], 1, file_size, m_file_handle);
return result;
}
void core::file_stream::close()
{
if (m_file_handle)
{
if (int result = std::fclose(m_file_handle); result != 0)
throw std::system_error(std::error_code{result, std::system_category()});
m_file_handle = nullptr;
}
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <concepts>
#include <cstdio>
#include <filesystem>
#include <string>
#include <type_traits>
namespace core {
class file_stream
{
public:
file_stream(const file_stream&) = delete;
file_stream& operator=(const file_stream&) = delete;
file_stream(file_stream&&) noexcept;
file_stream& operator=(file_stream&&);
~file_stream();
static file_stream open_for_reading(const std::filesystem::path& file_path);
static std::string file_contents_as_string(const std::filesystem::path& file_path);
std::string read_all_as_string();
void close();
private:
file_stream() = default;
FILE* m_file_handle{};
};
}
+72
View File
@@ -0,0 +1,72 @@
#include "io-helper.h"
#include <system_error>
std::FILE* core::open_file(const char* file_path, const char* mode)
{
std::FILE* file = std::fopen(file_path, mode);
if (!file)
throw std::system_error(errno, std::system_category());
return file;
}
void core::seek_file_to_beginning(std::FILE* file, long offset)
{
const int result = std::fseek(file, offset, SEEK_SET);
if (result != 0)
throw std::system_error(errno, std::system_category());
}
void core::seek_file_relatively(std::FILE* file, long offset)
{
const int result = std::fseek(file, offset, SEEK_CUR);
if (result != 0)
throw std::system_error(errno, std::system_category());
}
void core::seek_file_to_end(std::FILE* file, long offset)
{
const int result = std::fseek(file, offset, SEEK_END);
if (result != 0)
throw std::system_error(errno, std::system_category());
}
long core::get_file_offset(std::FILE* file)
{
const long offset = std::ftell(file);
if (offset < 0)
throw std::system_error(errno, std::system_category());
return offset;
}
std::size_t core::read_from_file(void* buffer, std::size_t size, std::size_t count, std::FILE* file)
{
const std::size_t objects_read = std::fread(buffer, size, count, file);
if (objects_read < count)
{
if (std::feof(file))
throw std::runtime_error("Failed to read from file: End-of-file reached.");
else
throw std::runtime_error("Failed to read from file: Unknown error.");
}
return objects_read;
}
std::size_t core::write_to_file(const void* buffer, std::size_t size, std::size_t count, std::FILE* file)
{
const std::size_t objects_written = std::fwrite(buffer, size, count, file);
if (objects_written < count)
throw std::runtime_error("Failed to write to file.");
return objects_written;
}
void core::close_file(std::FILE* file)
{
int result = std::fclose(file);
if (result != 0)
throw std::runtime_error("Failed to close file.");
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <cstdio>
#include <string_view>
namespace core {
extern std::FILE* open_file(const char* file_path, const char* mode);
extern void seek_file_to_beginning(std::FILE* file, long offset = 0);
extern void seek_file_relatively(std::FILE* file, long offset);
extern void seek_file_to_end(std::FILE* file, long offset = 0);
extern long get_file_offset(std::FILE* file);
extern std::size_t read_from_file(void* buffer, std::size_t size, std::size_t count, std::FILE* file);
extern std::size_t write_to_file(const void* buffer, std::size_t size, std::size_t count, std::FILE* file);
extern void close_file(std::FILE* file);
}
+17
View File
@@ -0,0 +1,17 @@
project "cpp-core-library"
do
kind "StaticLib"
location "%{_MAIN_SCRIPT_DIR}/.build"
includedirs {"%{_MAIN_SCRIPT_DIR}"}
files {
"**.cpp",
"**.h",
}
filter "toolset:msc*"
do
defines {"_CRT_SECURE_NO_WARNINGS"}
end
filter ""
end
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <concepts>
namespace core {
template<typename T>
concept callable = requires { &T::operator(); };
template<typename T>
struct function_traits
{
using return_type = void;
using first_argument_type = void;
};
template<typename T>
requires callable<T>
struct function_traits<T>
{
using return_type = typename function_traits<decltype(&T::operator())>::return_type;
using first_argument_type = typename function_traits<decltype(&T::operator())>::first_argument_type;
};
template<typename Ret, typename Arg0, typename... Args>
struct function_traits<Ret(Arg0, Args...)>
{
using return_type = typename Ret;
using first_argument_type = typename Arg0;
};
template<typename Ret, typename Arg0, typename... Args>
struct function_traits<Ret (*)(Arg0, Args...)>
{
using return_type = typename Ret;
using first_argument_type = typename Arg0;
};
template<typename Ret, typename Class, typename Arg0, typename... Args>
struct function_traits<Ret (Class::*)(Arg0, Args...)>
{
using return_type = typename Ret;
using first_argument_type = typename Arg0;
};
template<typename Ret, typename Class, typename Arg0, typename... Args>
struct function_traits<Ret (Class::*)(Arg0, Args...) const>
{
using return_type = typename Ret;
using first_argument_type = typename Arg0;
};
template<typename T>
using return_type_t = typename function_traits<T>::return_type;
template<typename T>
using first_argument_type_t = typename function_traits<T>::first_argument_type;
template<typename T, typename Arg>
concept callable_with_converted_argument = requires(T&& callable, Arg&& arg) { callable(typename traits::first_argument_type_t<T>{arg}); };
}
@@ -0,0 +1,95 @@
#pragma once
#include <ranges>
#include <span>
#include <string_view>
namespace core {
class command_line_argument
{
public:
constexpr command_line_argument(char* arg, std::span<char*> modifiers)
: m_arg_view{arg}
, m_modifiers{modifiers}
{
}
template<typename F>
requires std::invocable<F>
void with(std::string_view arg_to_match_view, F&& function) const
{
if (arg_to_match_view == m_arg_view)
std::invoke(std::forward<F>(function));
}
private:
std::string_view m_arg_view;
std::span<char*> m_modifiers;
};
class command_line_args_view : public std::ranges::view_interface<command_line_args_view>
{
public:
constexpr command_line_args_view() = default;
constexpr command_line_args_view(int argc, char* argv[])
: m_argc{--argc}
, m_application_path{argv++[0]}
, m_argv{argv}
{
}
class iterator
{
public:
constexpr iterator(int argc, char** argv, int index)
: m_argv{argv}
, m_argc{argc}
, m_index{index}
{
}
constexpr iterator& operator++()
{
++m_index;
while (m_index < m_argc)
{
const std::string_view arg_view{m_argv[m_index]};
if (arg_view.starts_with("--"))
m_modifier_start_index = m_index++;
}
return *this;
}
constexpr command_line_argument operator*() const
{
if (m_modifier_start_index != -1)
return command_line_argument{m_argv[m_index], std::span<char*>(&m_argv[m_modifier_start_index], &m_argv[m_index])};
else
return command_line_argument{m_argv[m_index], {}};
}
constexpr bool operator!=(const iterator& other) const { return m_argv != other.m_argv || m_index != other.m_index; }
private:
char** m_argv;
int m_argc;
int m_index;
int m_modifier_start_index{-1};
};
constexpr auto&& appliation_path() const { return m_application_path; }
constexpr iterator begin() const { return iterator{m_argc, m_argv, 0}; }
constexpr iterator end() const { return iterator{m_argc, m_argv, m_argc}; }
constexpr std::size_t size() const { return static_cast<std::size_t>(m_argc); }
private:
int m_argc{};
char* m_application_path{};
char** m_argv{};
};
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "core-library/views/trim-view.h"
namespace core {
template<std::ranges::forward_range Range>
requires requires(Range&& range) {
(void)range.empty();
(void)trim_view{(*std::begin(range))};
}
class trim_elements_view : public std::ranges::view_interface<trim_elements_view<Range>>
{
public:
constexpr trim_elements_view(Range range)
: m_range(std::move(range))
{
}
class iterator
{
public:
using base_t = std::ranges::iterator_t<Range>;
constexpr iterator(base_t base, base_t end)
: m_base{base}
, m_end{end}
{
}
constexpr bool operator!=(iterator other) const { return m_base != other.m_base; }
constexpr auto operator*() const { return trim_view{*m_base}; }
constexpr iterator& operator++()
{
// Skip empty elements
while (++m_base != m_end && std::ranges::empty(**this)) {}
return *this;
}
private:
base_t m_base;
base_t m_end;
};
constexpr auto begin() { return iterator{std::begin(m_range), std::end(m_range)}; }
constexpr auto end() { return iterator{std::end(m_range), std::end(m_range)}; }
private:
Range m_range{};
};
constexpr view_closure<trim_elements_view> trim_elements{};
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include "core-library/views/view-closure.h"
#include <ranges>
namespace core {
template<std::ranges::forward_range Range>
requires requires(Range&& range) {
*std::begin(range) == ' '; // Comparable with char literals.
}
class trim_view : public std::ranges::view_interface<trim_view<Range>>
{
public:
constexpr explicit trim_view(Range range)
: m_range(std::move(range))
{
}
static constexpr bool is_space(auto&& c)
{
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0';
}
constexpr auto begin()
{
auto it = std::begin(m_range);
while (it != std::end(m_range) && is_space(*it))
++it;
return it;
}
constexpr auto end()
{
auto nonspace_end = std::end(m_range);
for (auto it = std::begin(m_range); it != std::end(m_range);)
{
if (!is_space(*it++))
nonspace_end = it;
}
return nonspace_end;
}
private:
Range m_range{};
};
constexpr view_closure<trim_view> trim{};
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <ranges>
namespace core {
template<template<typename> typename ViewTemplate>
class view_closure : public std::ranges::range_adaptor_closure<view_closure<ViewTemplate>>
{
public:
template<std::ranges::viewable_range Range>
constexpr auto operator()(Range&& range) const
requires requires { ViewTemplate<std::views::all_t<Range>>{std::forward<Range>(range)}; }
{
return ViewTemplate<std::views::all_t<Range>>{std::forward<Range>(range)};
}
};
}