Skip to content

Commit

Permalink
Add map
Browse files Browse the repository at this point in the history
  • Loading branch information
melton1968 committed Jun 26, 2023
1 parent 260984b commit a8cf3b6
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 31 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ target_include_directories(lexical_cast INTERFACE include)
target_link_libraries(lexical_cast INTERFACE fmt::fmt)

foreach(prog
lexical_cast_dictionary
lexical_cast0
lexical_cast1
)
Expand Down
34 changes: 15 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
[![Build](https://github.com/cpp-core/lexical_cast/actions/workflows/build.yaml/badge.svg)](https://github.com/cpp-core/lexical_cast/actions/workflows/build.yaml)

# Ergonomic, Type-Safe, and Extensible Conversions
Ergonomic, Type-Safe, and Extensible Conversions
================================================

## Motivation
# Motivation

Values often need to be converted from or to literal text,
e.g. converting between `std::vector<int>` and `std::string`. This is
a common task when reading command line arguments or a configuration
file or when logging output about the program state.
file or when logging output about program state.

The `lexical_cast` library is a header-only library that provides the
`lexical_cast` and `lexical_to_string` template functions for
convenient, type-safe, and extensible conversions between program
The `lexical_cast` library is a light-weight, header-only library that
provides for ergonomic, type-safe, and extensible conversion between
values and literal text. The `lexical` functions support the `C++`
standard library types such as `std::vector` and `std::map` out of the
box and are easily extensible to user-defined types as demonstrated by
the following code snippet.
composition of the builting and standard library types such as
`std::vector<int>` out of the box and are easily extensible to
user-defined types. The following snippet demonstrates converting
between `std::vector<int>` and `std::string`.

```c++
auto vec = lexical_cast<std::vector<int>>("[1, 2, 3]");
Expand All @@ -25,20 +26,15 @@ assert(lexical_to_string(vec) == "[1,2,3]");
## Non-Goals
The `lexical` functions are designed to work with value-types,
i.e. pointers are represented by the pointed to address and not the
pointed to object. There is no support for following a graph of
objects.
The `lexical` functions are not designed for the general
(de)serialization of program values. There is no support for following
pointers or marking objects that is necessary for general
(de)serialization functionality.
The `lexical` functions are designed to work with value-types and not
for the general (de)serialization of arbitrary values. There is no
support for following pointers or marking objects that is necessary
for general (de)serialization functionality.
## Other Options
The `C++` standard library has a number of facilities for performing
conversions for the builtin types that vary in their use and
extensibility.
## Tutorial
# Tutorial
4 changes: 3 additions & 1 deletion include/core/lexical_cast/bool.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
#pragma once
#include "impl.h"
#include "error.h"
#include "util.h"

namespace core::lexical_cast_detail {

template<>
struct lexical_cast_impl<bool> {
bool convert(std::string_view input) const {
bool convert(std::string_view raw_input) const {
std::string_view input = unwrap_ws(raw_input);
if (input == "0" or input == "f" or input == "F" or input == "false")
return false;
if (input == "1" or input == "t" or input == "T" or input == "true")
Expand Down
2 changes: 1 addition & 1 deletion include/core/lexical_cast/char.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace core::lexical_cast_detail {
template<>
struct lexical_cast_impl<char> {
char convert(std::string_view input) const {
if (input.size() == 0)
if (input.size() != 1)
throw lexical_cast_error(input, "char");
return input[0];
}
Expand Down
3 changes: 2 additions & 1 deletion include/core/lexical_cast/floating.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <charconv>
#include "impl.h"
#include "error.h"
#include "util.h"

namespace core::lexical_cast_detail {

Expand Down Expand Up @@ -46,7 +47,7 @@ T parse_floating_point(std::string_view input, std::string_view name) {
template<std::floating_point T>
struct lexical_cast_impl<T> {
T convert(std::string_view input) const {
return parse_floating_point<T>(input, "floating point");
return parse_floating_point<T>(unwrap_ws(input), "floating point");
}

std::string to_string(T input) const {
Expand Down
3 changes: 2 additions & 1 deletion include/core/lexical_cast/integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <charconv>
#include "impl.h"
#include "error.h"
#include "util.h"

namespace core::lexical_cast_detail {

Expand Down Expand Up @@ -37,7 +38,7 @@ T parse_integral(std::string_view input, std::string_view name) {
template<std::integral T>
struct lexical_cast_impl<T> {
T convert(std::string_view input) const {
return parse_integral<T>(input, "integral");
return parse_integral<T>(unwrap_ws(input), "integral");
}

std::string to_string(T input) const {
Expand Down
37 changes: 37 additions & 0 deletions include/core/lexical_cast/map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (C) 2017, 2018, 2019, 2021, 2022, 2023 by Mark Melton
//

#pragma once
#include <map>
#include "builtin.h"
#include "util.h"

namespace core::lexical_cast_detail {

template<class K, class V>
struct lexical_cast_impl<std::map<K, V>> {
std::map<K, V> convert(std::string_view s) const {
auto [iter, end] = unwrap(s.begin(), s.end());
std::map<K, V> m;
while (iter < end) {
auto delim = find_first(iter, end, ',');
m.emplace(lexical_cast<std::pair<K,V>>({iter, delim}));
iter = delim + 1;
}
return m;
}

std::string to_string(const std::map<K, V>& m) const {
std::string r = "{";
for (const auto& pair : m) {
if (r.size() > 1)
r += ",";
r += lexical_to_string(pair);
}
r += "}";
return r;
}
};

}; // core::lexical_cast_detail

28 changes: 20 additions & 8 deletions include/core/lexical_cast/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once
#include <utility>
#include <cctype>
#include <string_view>

namespace core::lexical_cast_detail
{
Expand Down Expand Up @@ -71,16 +72,27 @@ bool matching_delims(Iter iter, Iter end) {
}

template<class Iter>
std::pair<Iter,Iter> unwrap(Iter iter, Iter end) {
while (iter < end and std::isspace(*iter))
++iter;
while (end > iter and std::isspace(*(end - 1)))
--end;
if (iter < end and matching_delims(iter, end)) {
++iter;
std::pair<Iter,Iter> unwrap_ws(Iter begin, Iter end) {
while (begin < end and std::isspace(*begin))
++begin;
while (end > begin and std::isspace(*(end - 1)))
--end;
return {begin, end};
}

std::string_view unwrap_ws(std::string_view str) {
auto [b, e] = unwrap_ws(str.begin(), str.end());
return {b, e};
}

template<class Iter>
std::pair<Iter,Iter> unwrap(Iter begin, Iter end) {
auto [b, e] = unwrap_ws(begin, end);
if (b < e and matching_delims(b, e)) {
++b;
--e;
}
return {iter, end};
return {b, e};
}

}; // core::lexical_cast_detail
Expand Down
17 changes: 17 additions & 0 deletions src/tools/lexical_cast_dictionary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) 2023 by Mark Melton
//

#include <iostream>
#include "core/lexical_cast/string.h"
#include "core/lexical_cast/pair.h"
#include "core/lexical_cast/map.h"

using namespace core;
using std::cout, std::endl;

int main(int argc, const char *argv[]) {
auto text = R"({["a", 1], ["b", 2]})";
auto m = lexical_cast<std::map<std::string,int>>(text);
cout << lexical_to_string(m) << endl;
return 0;
}

0 comments on commit a8cf3b6

Please sign in to comment.