-
Notifications
You must be signed in to change notification settings - Fork 490
Expand file tree
/
Copy pathRangeTokenizer.h
More file actions
103 lines (97 loc) · 3.92 KB
/
RangeTokenizer.h
File metadata and controls
103 lines (97 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.
#ifndef RANGE_TOKENIZER_H
#define RANGE_TOKENIZER_H
/// @file RangeTokenizer.h
/// @author Matthias Richter
/// @since 2018-09-18
/// @brief Helper function to tokenize sequences and ranges of integral numbers
#include <vector>
#include <string>
#include <sstream>
#include <utility> // std::move
#include <functional> // std::function
namespace o2
{
/// @class RangeTokenizer
/// @brief Tokenize a string according to delimiter ',' and extract values of type T
///
/// Extract a sequence of elements of specified type T from a string argument. Elements are
/// separated by comma. If T is an integral type, also ranges are supported using '-'.
///
/// The default conversion from token to type is using std stringstream operator>> which
/// supports a variety of built-in conversions.
/// A custom handler function of type std::function<T(std::string const&)> can be provided
/// to convert string tokens to the specified output type.
///
/// @return std::vector of type T
///
/// Usage:
/// // the simple case using integral type
/// std::vector<int> tokens = RangeTokenizer::tokenize<int>("0-5,10,13");
///
/// // simple case using string type
/// std::vector<std::string> tokens = RangeTokenizer::tokenize<std::string>("apple,strawberry,tomato");
///
/// // process a custom type according to a map
/// // use a custom mapper function, this evetually throws an exception if the token is not in the map
/// enum struct Food { Apple,
/// Strawberry,
/// Tomato };
/// const std::map<std::string, Food> FoodMap {
/// { "apple", Food::Apple },
/// { "strawberry", Food::Strawberry },
/// { "tomato", Food::Tomato },
/// };
/// std::vector<Food> tokens = RangeTokenizer::tokenize<Food>("apple,tomato",
/// [FoodMap](auto const& token) {
/// return FoodMap.at(token);
/// } );
struct RangeTokenizer {
template <typename T>
static std::vector<T> tokenize(
std::string input, std::function<T(std::string const&)> convert = [](std::string const& token) {T value; std::istringstream(token) >> value; return value; })
{
std::istringstream stream(input);
std::string token;
std::vector<T> res;
while (std::getline(stream, token, ',')) {
if (std::is_integral<T>::value && token.find('-') != token.npos) {
// extract range
if constexpr (std::is_integral<T>::value) { // c++17 compile time
insertRange(res, token, convert);
}
} else {
res.emplace_back(convert(token));
}
}
return std::move(res);
}
/// extract a range of an integral type from a token string and add to vector
template <typename T, typename std::enable_if_t<std::is_integral<T>::value == true, int> = 0>
static void insertRange(std::vector<T>& res, std::string token, std::function<T(std::string const&)> convert)
{
std::istringstream tokenstream(token);
std::string bound;
T lowerBound, upperBound;
if (std::getline(tokenstream, bound, '-')) {
lowerBound = convert(bound);
if (std::getline(tokenstream, bound, '-')) {
upperBound = convert(bound);
for (T index = lowerBound; index <= upperBound; index++) {
res.emplace_back(index);
}
}
}
}
};
}; // namespace o2
#endif