Skip to content

Commit 553c8c6

Browse files
committed
Language/CSV/Mapping
1 parent 7c4cfea commit 553c8c6

6 files changed

Lines changed: 333 additions & 75 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
### 🔣 Languages (Parsers, Compilers, VM)
5353
- Tools to create parsers and compilers (CSS, INI, HTML, JSON, Markdown, XML) (work in progress <img src="resources/loading.gif" width="12" height="12"/>)
54+
- [`CSV Mapping`](modules/Language/CSV/Mapping.mpp) - Type-safe CSV to struct mapping with support for custom conversion functions
5455
- [`CLikeCompiler`](modules/Language/CLikeCompiler.mpp) - Compiler for C-inspired language
5556
- [`GrammarParser`](modules/Language/GrammarParser.mpp) - A parser for defining and interpreting custom grammars, used for building language parsers
5657
- [`MetaEvaluator`](modules/Language/MetaEvaluator.mpp) - Homoiconic meta-circular evaluator with extensible reflexivity
@@ -107,7 +108,7 @@
107108
### 🏷️ Type
108109
- [`Concept`](modules/Type/Concept.mpp) - Extensions to `<type_traits>` and `<concepts>` providing additional compile-time checks and utilities
109110
- [`Enum`](modules/Type/Enum.mpp) - Generic enum-to-string conversion
110-
- [`Mapping`](modules/Type/ObjectMapping.mpp) - Static reflection on members (pre-C++26)
111+
- [`Mapping`](modules/Type/Mapping.mpp) - Generic compile-time mapping between values
111112
- [`Tuple`](modules/Type/Tuple.mpp) - Visitor for `std::tuple`
112113
- [`VariadicTemplate`](modules/Type/VariadicTemplate.mpp) - Metaprogramming on variadic parameters
113114
- [`Variant`](modules/Type/Variant.mpp) - Generic print and comparison operators for `std::variant`

modules/FileSystem/Directory.mpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ export namespace CppUtils::FileSystem
3939
std::filesystem::create_directory(m_tempDirectory);
4040
}
4141

42-
inline explicit TemporaryDirectory(auto&& function, [[maybe_unused]] auto&&... args):
42+
inline explicit TemporaryDirectory(std::invocable<std::filesystem::path> auto&& function, [[maybe_unused]] auto&&... args):
4343
TemporaryDirectory{}
4444
{
4545
static_assert(not Type::HasReturnValue<decltype(function)>);
4646
function(m_tempDirectory, std::forward<decltype(args)>(args)...);
4747
}
4848

49-
inline TemporaryDirectory(std::string_view directoryName, auto&& function, [[maybe_unused]] auto&&... args):
49+
inline TemporaryDirectory(std::string_view directoryName, std::invocable<std::filesystem::path> auto&& function, [[maybe_unused]] auto&&... args):
5050
TemporaryDirectory{directoryName}
5151
{
5252
static_assert(not Type::HasReturnValue<decltype(function)>);

modules/Language/CSV/Mapping.mpp

Lines changed: 115 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,139 @@
11
export module CppUtils.Language.CSV.Mapping;
2-
/*
2+
33
import std;
44

55
import CppUtils.String;
6+
import CppUtils.Type.Mapping;
7+
import CppUtils.FileSystem;
68

7-
export namespace CppUtils::Language::CSV::Mapping
9+
export namespace CppUtils::Language::CSV
810
{
9-
struct Column final
10-
{
11-
String::Token token;
12-
std::size_t position;
13-
};
14-
15-
template<class...>
16-
struct CSVSchema;
11+
using Line = std::vector<std::string_view>;
1712

18-
template<class... Columns>
19-
struct CSVSchema final
20-
{
21-
explicit CSVSchema(const Columns&... columns)
22-
{
23-
([&](const Column& column) -> void {
24-
columnsPositions[column.token] = column.position;
25-
}(columns), ...);
26-
}
27-
28-
std::unordered_map<String::Hash, std::size_t> columnsPositions;
29-
};
30-
31-
template<class>
32-
struct Mapping;
33-
34-
template<class Object>
35-
[[nodiscard]] inline auto toStruct(const Language::CSV::Line& csvLine) -> decltype(auto)
13+
template<class Object, class Mapping>
14+
[[nodiscard]] inline auto toStruct(const auto& csvLine) -> Object
3615
{
16+
using CSVMapping = typename Mapping::CSVMapping;
17+
using StructMapping = typename Mapping::StructMapping;
18+
using FunctionMapping = typename Mapping::FunctionMapping;
3719
static_assert(std::is_default_constructible_v<Object>);
38-
const auto& structSchema = Language::Mapping::Mapping<Object>::schema;
39-
const auto& csvSchema = Mapping<Object>::schema;
4020
auto object = Object{};
41-
std::apply([&](auto&&... attributes) -> void {
42-
([&](auto&& attribute) -> void {
43-
auto& objectAttribute = object.*attribute.address;
44-
using AttributeType = std::remove_cvref_t<decltype(objectAttribute)>;
45-
auto position = csvSchema.columnsPositions.at(attribute.token);
46-
if (position < std::size(csvLine))
21+
std::apply([&](auto&&... csvPairs) -> void {
22+
([&](auto&& csvPair) -> void {
23+
using CSVPair = std::remove_cvref_t<decltype(csvPair)>;
24+
constexpr auto token = CSVPair::lhs;
25+
constexpr auto csvColumnPosition = std::get<0>(CSVPair::rhs);
26+
if (csvColumnPosition < std::size(csvLine))
4727
{
48-
if (attribute.parsingFunction)
49-
objectAttribute = attribute.parsingFunction(csvLine[position]);
50-
else if constexpr (std::same_as<AttributeType, std::string>)
51-
objectAttribute = csvLine[position];
28+
constexpr auto memberPointer = std::get<0>(StructMapping::template toRhs<token>());
29+
auto& member = object.*memberPointer;
30+
using MemberType = std::remove_cvref_t<decltype(member)>;
31+
auto value = std::string_view{csvLine[csvColumnPosition]};
32+
if constexpr (FunctionMapping::template containsLhs<token>())
33+
FunctionMapping::template toSingleRhs<token>()(member, value);
34+
else if constexpr (std::same_as<MemberType, std::string_view>)
35+
member = value;
36+
else if constexpr (std::same_as<MemberType, std::string>)
37+
member = value;
38+
else if constexpr (std::integral<MemberType> or std::floating_point<MemberType>)
39+
std::from_chars(std::data(value), std::data(value) + std::size(value), member);
5240
}
53-
}(attributes, ...);
54-
}, structSchema.attributes);
41+
}(csvPairs), ...);
42+
}, typename CSVMapping::Values{});
5543
return object;
5644
}
5745

58-
using namespace String::Literals;
46+
template<class Object, class Mapping>
47+
[[nodiscard]] inline auto toStruct(std::string_view csvLine, std::string_view separator = ";") -> Object
48+
{
49+
const auto tokens = csvLine
50+
| std::views::split(separator)
51+
| std::views::transform([](auto&& range) { return std::string_view{range}; })
52+
| std::ranges::to<std::vector<std::string_view>>();
53+
return toStruct<Object, Mapping>(tokens);
54+
}
55+
56+
template<class Object, class Mapping>
57+
[[nodiscard]] inline auto toStructs(
58+
std::string_view csv,
59+
std::string_view columnSeparator = ";",
60+
std::string_view lineSeparator = "\n",
61+
std::size_t dropNFirstLines = 0,
62+
std::vector<Object>&& objects = {}) -> std::vector<Object>
63+
{
64+
const auto lines = csv
65+
| std::views::split(lineSeparator)
66+
| std::views::transform([](auto&& range) { return std::string_view{range}; })
67+
| std::ranges::to<std::vector<std::string_view>>();
68+
for (auto i = dropNFirstLines; i < std::size(lines); ++i)
69+
if (const auto trimmedLine = String::trimString(lines[i]); not std::empty(trimmedLine))
70+
objects.push_back(toStruct<Object, Mapping>(trimmedLine, columnSeparator));
71+
return std::move(objects);
72+
}
73+
74+
template<class Object, class Mapping>
75+
[[nodiscard]] inline auto toCSV(const Object& object, std::string_view separator = ";") -> std::string
76+
{
77+
using CSVMapping = typename Mapping::CSVMapping;
78+
using StructMapping = typename Mapping::StructMapping;
79+
constexpr auto maxPosition = []() -> std::size_t {
80+
auto max = 0uz;
81+
std::apply([&](auto&&... pairs) {
82+
((max = std::max(max, static_cast<std::size_t>(std::get<0>(std::remove_cvref_t<decltype(pairs)>::rhs)))), ...);
83+
}, typename CSVMapping::Values{});
84+
return max;
85+
}();
86+
auto columns = std::vector<std::string>(maxPosition + 1);
87+
std::apply([&](auto&&... csvPairs) -> void {
88+
([&](auto&& csvPair) -> void {
89+
using CSVPair = std::remove_cvref_t<decltype(csvPair)>;
90+
constexpr auto token = CSVPair::lhs;
91+
constexpr auto csvColumnPosition = std::get<0>(CSVPair::rhs);
92+
constexpr auto memberPointer = std::get<0>(StructMapping::template toRhs<token>());
93+
columns[csvColumnPosition] = String::formatValue(object.*memberPointer);
94+
}(csvPairs), ...);
95+
}, typename CSVMapping::Values{});
96+
return String::concatenateStringsWithSeparator(columns, separator);
97+
}
98+
99+
template<class Object, class Mapping>
100+
[[nodiscard]] inline auto toCSV(
101+
const std::vector<Object>& objects,
102+
std::string_view columnSeparator = ";",
103+
std::string_view lineSeparator = "\n") -> std::string
104+
{
105+
auto csv = std::string{};
106+
for (const auto& object : objects)
107+
{
108+
if (not std::empty(csv))
109+
csv += lineSeparator;
110+
csv += toCSV<Object, Mapping>(object, columnSeparator);
111+
}
112+
return csv;
113+
}
59114

60-
template<class T>
61-
inline auto loadFile(const std::filesystem::path& filePath, std::vector<T>&& elements = {}, std::size dropNFirstLines = 0) -> std::vector<T>
115+
template<class Object, class Mapping>
116+
[[nodiscard]] inline auto loadFile(
117+
const std::filesystem::path& filePath,
118+
std::string_view columnSeparator = ";",
119+
std::string_view lineSeparator = "\n",
120+
std::size_t dropNFirstLines = 0,
121+
std::vector<Object>&& elements = {}) -> std::vector<Object>
62122
{
63123
auto fileContent = FileSystem::String::read(filePath);
64-
auto csvTable = parse(fileContent, dropNFirstLines);
65-
for (const auto& csvLine : csvTable)
66-
elements.emplace_back(toStruct<T>(csvLine));
67-
return elements;
124+
return toStructs<Object, Mapping>(fileContent, columnSeparator, lineSeparator, dropNFirstLines, std::move(elements));
68125
}
69126

70-
template<class T>
71-
inline auto loadDirectory(const std::filesystem::path& directory, std::vector<T>&& elements = {}, std::size_t dropNFirstLines = 0) -> std::vector<T>
127+
template<class Object, class Mapping>
128+
[[nodiscard]] inline auto loadDirectory(const std::filesystem::path& directory,
129+
std::string_view columnSeparator = ";",
130+
std::string_view lineSeparator = "\n",
131+
std::size_t dropNFirstLines = 0,
132+
std::vector<Object>&& elements = {}) -> std::vector<Object>
72133
{
73-
FileSystem::forFilesWithExtension(directory, ".csv", [&elements, dropNFirstLines](const auto& filePath) {
74-
loadFile(filePath, std::move(elements), dropNFirstLines);
134+
FileSystem::forFilesWithExtension(directory, ".csv", [&](const auto& filePath) {
135+
elements = loadFile<Object, Mapping>(filePath, columnSeparator, lineSeparator, dropNFirstLines, std::move(elements));
75136
});
76-
return elements;
137+
return std::move(elements);
77138
}
78139
}
79-
*/

modules/Language/Language.mpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export import CppUtils.Language.ASTParser;
55
export import CppUtils.Language.GrammarParser;
66
export import CppUtils.Language.VirtualMachine;
77
export import CppUtils.Language.MetaEvaluator;
8+
export import CppUtils.Language.CSV.Mapping;

modules/Type/Mapping.mpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,40 @@ export namespace CppUtils::Type
1414
template<class... Pairs>
1515
struct Mapping final
1616
{
17-
static_assert(sizeof...(Pairs) > 0);
1817
using Values = std::tuple<Pairs...>;
1918

2019
Mapping() = delete;
2120

2221
template<auto Lhs>
2322
[[nodiscard]] static inline consteval auto toRhs() noexcept -> auto
2423
{
25-
return []<auto Target, class First, class... Rest>(this auto&& self) consteval {
26-
if constexpr (First::lhs == Target)
27-
return First::rhs;
28-
else if constexpr (sizeof...(Rest) == 0)
29-
static_assert([] { return false; }(), "Lhs not found");
30-
else
31-
return self.template operator()<Target, Rest...>();
32-
}.template operator()<Lhs, Pairs...>();
24+
if constexpr (sizeof...(Pairs) == 0)
25+
static_assert([] { return false; }(), "Lhs not found");
26+
else
27+
return []<auto Target, class First, class... Rest>(this auto&& self) consteval {
28+
if constexpr (First::lhs == Target)
29+
return First::rhs;
30+
else if constexpr (sizeof...(Rest) == 0)
31+
static_assert([] { return false; }(), "Lhs not found");
32+
else
33+
return self.template operator()<Target, Rest...>();
34+
}.template operator()<Lhs, Pairs...>();
3335
}
3436

3537
template<auto Rhs>
3638
[[nodiscard]] static inline consteval auto toLhs() noexcept -> auto
3739
{
38-
return []<auto Target, class First, class... Rest>(this auto&& self) consteval {
39-
if constexpr (contains<Target>(First::rhs))
40-
return First::lhs;
41-
else if constexpr (sizeof...(Rest) == 0)
42-
static_assert([] { return false; }(), "Rhs not found");
43-
else
44-
return self.template operator()<Target, Rest...>();
45-
}.template operator()<Rhs, Pairs...>();
40+
if constexpr (sizeof...(Pairs) == 0)
41+
static_assert([] { return false; }(), "Rhs not found");
42+
else
43+
return []<auto Target, class First, class... Rest>(this auto&& self) consteval {
44+
if constexpr (contains<Target>(First::rhs))
45+
return First::lhs;
46+
else if constexpr (sizeof...(Rest) == 0)
47+
static_assert([] { return false; }(), "Rhs not found");
48+
else
49+
return self.template operator()<Target, Rest...>();
50+
}.template operator()<Rhs, Pairs...>();
4651
}
4752

4853
template<auto Lhs>
@@ -53,6 +58,21 @@ export namespace CppUtils::Type
5358
return std::get<0>(tuple);
5459
}
5560

61+
template<auto Lhs>
62+
[[nodiscard]] static inline consteval auto containsLhs() noexcept -> bool
63+
{
64+
return ((Pairs::lhs == Lhs) or ...);
65+
}
66+
67+
template<auto Lhs>
68+
[[nodiscard]] static inline consteval auto hasSingleRhs() noexcept -> bool
69+
{
70+
if constexpr (containsLhs<Lhs>())
71+
return std::tuple_size_v<decltype(toRhs<Lhs>())> == 1;
72+
else
73+
return false;
74+
}
75+
5676
template<auto Value, class Tuple>
5777
[[nodiscard]] static inline consteval auto contains(const Tuple& tuple) noexcept -> bool
5878
{

0 commit comments

Comments
 (0)