split into multiple files, moved to string dir
This commit is contained in:
parent
aeba6ffc31
commit
b2e6958bb4
119
src/string/conversion.cpp
Normal file
119
src/string/conversion.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include "conversion.hpp"
|
||||||
|
#include "../exceptions.hpp"
|
||||||
|
#include "../regex.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gz {
|
||||||
|
|
||||||
|
bool isInt(const std::string& s) {
|
||||||
|
return std::regex_match(s, re::types::intT);
|
||||||
|
}
|
||||||
|
bool isInt(const std::string_view& s) {
|
||||||
|
return re::regex_match(s, re::types::intT);
|
||||||
|
}
|
||||||
|
bool isUInt(const std::string& s) {
|
||||||
|
return std::regex_match(s, re::types::uintT);
|
||||||
|
}
|
||||||
|
bool isUInt(const std::string_view& s) {
|
||||||
|
return re::regex_match(s, re::types::uintT);
|
||||||
|
}
|
||||||
|
bool isFloal(const std::string& s) {
|
||||||
|
return std::regex_match(s, re::types::floatT);
|
||||||
|
}
|
||||||
|
bool isFloat(const std::string_view& s) {
|
||||||
|
return re::regex_match(s, re::types::floatT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getIntOr(const std::string& s, int fallback) noexcept {
|
||||||
|
try {
|
||||||
|
fallback = std::stoi(s);
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int getUnsignedIntOr(const std::string& s, unsigned int fallback) noexcept {
|
||||||
|
try {
|
||||||
|
fallback = std::stoul(s);
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double getDoubleOr(const std::string& s, double fallback) noexcept {
|
||||||
|
try {
|
||||||
|
fallback = std::stod(s);
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float getFloatOr(const std::string& s, float fallback) noexcept {
|
||||||
|
try {
|
||||||
|
fallback = std::stof(s);
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool getBoolOr(const std::string& s, bool fallback) noexcept {
|
||||||
|
if (s == "true" or s == "True" or s == "1") {
|
||||||
|
fallback = true;
|
||||||
|
}
|
||||||
|
else if (s == "false" or s == "False" or s == "0") {
|
||||||
|
fallback = false;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string getStringOr(const std::string& s, const std::string& fallback) noexcept {
|
||||||
|
if (s == "") { return fallback; }
|
||||||
|
else { return s; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: remove?
|
||||||
|
/* // int = 0, double = 1, string = 2 */
|
||||||
|
/* std::variant<std::string, int, double, bool> getVariant(std::string value, GetVariantType type, bool bFallback, int iFallback, double dFallback, const char* sFallback) { */
|
||||||
|
/* std::variant<std::string, int, double, bool> val = value; */
|
||||||
|
/* /1* cout << "id-attr" << id << attr << "val: " << std::get<string>(val); *1/ */
|
||||||
|
/* switch (type) { */
|
||||||
|
/* // to integer */
|
||||||
|
/* case INT: */
|
||||||
|
/* val = getIntOr(value, iFallback); */
|
||||||
|
/* break; */
|
||||||
|
/* case DOUBLE: */
|
||||||
|
/* val = getDoubleOr(value, dFallback); */
|
||||||
|
/* break; */
|
||||||
|
/* case BOOL: */
|
||||||
|
/* val = getBoolOr(value, bFallback); */
|
||||||
|
/* break; */
|
||||||
|
/* case STRING: */
|
||||||
|
/* val = getStringOr(value, sFallback); */
|
||||||
|
/* break; */
|
||||||
|
/* } */
|
||||||
|
/* return val; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
//
|
||||||
|
// CONVERT FROM STRING
|
||||||
|
//
|
||||||
|
template<>
|
||||||
|
bool fromString<bool>(const std::string& s) {
|
||||||
|
if (s == "true" or s == "True" or s == "1") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (s == "false" or s == "False" or s == "0") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw InvalidArgument("s is not a bool: '" + s + "'", "fromString<bool>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace gz
|
377
src/string/conversion.hpp
Normal file
377
src/string/conversion.hpp
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "to_string.hpp"
|
||||||
|
#include "from_string.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
#ifdef FORMAT
|
||||||
|
#include <format>
|
||||||
|
#else
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gz {
|
||||||
|
/**
|
||||||
|
* @name Functions that determine if s is a string representation of a certain type, using regex.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
bool isInt(const std::string& s);
|
||||||
|
bool isInt(const std::string_view& s);
|
||||||
|
bool isUInt(const std::string& s);
|
||||||
|
bool isUInt(const std::string_view& s);
|
||||||
|
bool isFloat(const std::string& s);
|
||||||
|
bool isFloat(const std::string_view& s);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Convert to type or return fallback
|
||||||
|
* @todo Use string_views without constructing a std::string
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
int getIntOr(const std::string& s, int fallback=0) noexcept;
|
||||||
|
inline int getIntOr(const std::string_view& s, int fallback=0) noexcept { return getIntOr(std::string(s), fallback); }
|
||||||
|
|
||||||
|
unsigned int getUnsignedIntOr(const std::string& s, unsigned int fallback=0) noexcept;
|
||||||
|
/* inline unsigned int getUnsignedIntOr(const std::string&& s, unsigned int fallback=0) { return getUnsignedIntOr(s, fallback); } */
|
||||||
|
inline unsigned int getUnsignedIntOr(const std::string_view& s, unsigned int fallback=0) noexcept { return getUnsignedIntOr(std::string(s), fallback); }
|
||||||
|
|
||||||
|
double getDoubleOr(const std::string& s, double fallback=0) noexcept;
|
||||||
|
/* inline double getDoubleOr(const std::string&& s, double fallback=0) { return getDoubleOr(s, fallback); } */
|
||||||
|
inline double getDoubleOr(const std::string_view& s, double fallback=0) noexcept { return getDoubleOr(std::string(s), fallback); }
|
||||||
|
|
||||||
|
float getFloatOr(const std::string& s, float fallback=0) noexcept;
|
||||||
|
/* inline float getFloatOr(const std::string&& s, float fallback=0) { return getDoubleOr(s, fallback); } */
|
||||||
|
inline float getFloatOr(const std::string_view& s, float fallback=0) noexcept { return getDoubleOr(std::string(s), fallback); }
|
||||||
|
|
||||||
|
bool getBoolOr(const std::string& s, bool fallback=false) noexcept;
|
||||||
|
/* inline bool getBoolOr(const std::string&& s, bool fallback=false) { return getBoolOr(s, fallback); } */
|
||||||
|
inline bool getBoolOr(const std::string_view& s, bool fallback=false) noexcept { return getBoolOr(std::string(s), fallback); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the string or fallback if string is empty.
|
||||||
|
*/
|
||||||
|
std::string getStringOr(const std::string& s, const std::string& fallback="none") noexcept;
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
//
|
||||||
|
// HEX/OCT
|
||||||
|
//
|
||||||
|
/**
|
||||||
|
* @name Converting an integer to/from a hex/oct/bin string
|
||||||
|
* @todo Move to std::format, when P0645R10 is implemented in gcc libstd
|
||||||
|
* @note
|
||||||
|
* The fromHexString() and fromOctString() functions use `std::hex`, while fromBinString() uses `std::bitset`. Both can throw `std::invalid_argument` and the latter can also throw `std::out_of_range`.
|
||||||
|
* See https://en.cppreference.com/w/cpp/utility/bitset/bitset and https://en.cppreference.com/w/cpp/io/manip/hex
|
||||||
|
*/
|
||||||
|
/// @{
|
||||||
|
/**
|
||||||
|
* @brief Convert an integer to hexdecimal string (prefixed with 0x)
|
||||||
|
* @param digits
|
||||||
|
* Minimum number of digits. Defaults to the amount of digits required to represent the largest possible number of type T.
|
||||||
|
* If digits is smaller than the needed amount to represent t, the needed amount is used.
|
||||||
|
*/
|
||||||
|
template<std::integral T>
|
||||||
|
std::string toHexString(const T& t, char digits=sizeof(T)*2) {
|
||||||
|
#ifdef FORMAT
|
||||||
|
return std::format("{:#0" + std::to_string(digits) + "x}", t);
|
||||||
|
#endif
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "0x" << std::setfill('0') << std::setw(digits) << std::hex << t;
|
||||||
|
return std::string(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a hexadecimal string (may be prefixed with 0x) to integer
|
||||||
|
*/
|
||||||
|
template<std::integral T>
|
||||||
|
T fromHexString(const std::string& s) {
|
||||||
|
T t;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << s;
|
||||||
|
ss >> t;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert an integer to octal string (prefixed with 0)
|
||||||
|
* @param digits
|
||||||
|
* Minimum number of digits. Defaults to the amount of digits required to represent the largest possible number of type T.
|
||||||
|
* If digits is smaller than the needed amount to represent t, the needed amount is used.
|
||||||
|
*/
|
||||||
|
template<std::integral T>
|
||||||
|
std::string toOctString(const T& t, char digits=sizeof(T)*4) {
|
||||||
|
#ifdef FORMAT
|
||||||
|
// TODO test when possible
|
||||||
|
return std::format("{:#0" + std::to_string(digits) + "o}", t);
|
||||||
|
#endif
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "0" << std::setfill('0') << std::setw(digits) << std::oct << t;
|
||||||
|
return std::string(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert an octal string (may be prefixed with 0) to integer
|
||||||
|
*/
|
||||||
|
template<std::integral T>
|
||||||
|
T fromOctString(const std::string& s) {
|
||||||
|
T t;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::oct << s;
|
||||||
|
ss >> t;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert an integer to binary string (prefixed with 0b)
|
||||||
|
*/
|
||||||
|
template<std::integral T>
|
||||||
|
std::string toBinString(const T& t) {
|
||||||
|
#ifdef FORMAT
|
||||||
|
// TODO test when possible
|
||||||
|
return std::format("{:#0" + std::to_string(digits) + "b}", t);
|
||||||
|
#endif
|
||||||
|
return std::string("0b") + std::bitset<sizeof(T)*8>(t).to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert binary string (may be prefixed with 0b) to integer
|
||||||
|
*/
|
||||||
|
template<std::integral T>
|
||||||
|
T fromBinString(const std::string& s) {
|
||||||
|
if (s.starts_with("0b")) {
|
||||||
|
return static_cast<T>(std::bitset<sizeof(T)*8>(s, 2).to_ullong());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return static_cast<T>(std::bitset<sizeof(T)*8>(s).to_ullong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string where the elements from a forward range are in hexadecimal format
|
||||||
|
* @returns [ 0x0001, 0x0002, ... ]
|
||||||
|
*/
|
||||||
|
template<util::IntegralForwardRange T>
|
||||||
|
std::string toHexString(const T& t) {
|
||||||
|
std::string s = "[ ";
|
||||||
|
for (auto it = t.begin(); it != t.end(); it++) {
|
||||||
|
s += toHexString(*it) + ", ";
|
||||||
|
}
|
||||||
|
if (s.size() > 2) {
|
||||||
|
s.erase(s.size() - 2);
|
||||||
|
}
|
||||||
|
s += " ]";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string where the elements from a forward range are in octal format
|
||||||
|
* @returns [ 00001, 00002, ... ]
|
||||||
|
*/
|
||||||
|
template<util::IntegralForwardRange T>
|
||||||
|
std::string toOctString(const T& t) {
|
||||||
|
std::string s = "[ ";
|
||||||
|
for (auto it = t.begin(); it != t.end(); it++) {
|
||||||
|
s += toOctString(*it) + ", ";
|
||||||
|
}
|
||||||
|
if (s.size() > 2) {
|
||||||
|
s.erase(s.size() - 2);
|
||||||
|
}
|
||||||
|
s += " ]";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string where the elements from a forward range are in octal format
|
||||||
|
* @returns [ 0b0001, 0b0010, ... ]
|
||||||
|
*/
|
||||||
|
template<util::IntegralForwardRange T>
|
||||||
|
std::string toBinString(const T& t) {
|
||||||
|
std::string s = "[ ";
|
||||||
|
for (auto it = t.begin(); it != t.end(); it++) {
|
||||||
|
s += toBinString(*it) + ", ";
|
||||||
|
}
|
||||||
|
if (s.size() > 2) {
|
||||||
|
s.erase(s.size() - 2);
|
||||||
|
}
|
||||||
|
s += " ]";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// gz::toString and gz::fromString overloads exist
|
||||||
|
template<typename T>
|
||||||
|
concept StringConvertible = ConvertibleToString<T> and ConstructibleFromString<T>;
|
||||||
|
|
||||||
|
} // namespace gz
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Contains utilities for type conversions to and from string
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace gz {
|
||||||
|
/**
|
||||||
|
* @page string_conversion String conversion
|
||||||
|
* @section sc_about About
|
||||||
|
* This library provides utility for string conversion, from and to `std::string`.
|
||||||
|
* The string utility is split into 3 files:
|
||||||
|
* - @ref to_string.hpp "`<gz-util/string/to_string.hpp>`" for converting types to strings
|
||||||
|
* - @ref from_string.hpp "`<gz-util/string/from_string.hpp>`" for converting strings to other types
|
||||||
|
* - @ref conversion.hpp "`<gz-util/string/conversion.hpp>`" for other string conversion features like from/to bin/oct/hex numbers, also includes the two previous files
|
||||||
|
*
|
||||||
|
* There also three important concepts, ConvertibleToString, ConstructibleFromString and @ref StringConvertible which are
|
||||||
|
* used in the @ref Log "logger" and the @ref SettingsManager "settings manager" to log and store types.
|
||||||
|
*
|
||||||
|
* If you want to use your own types with one of these,
|
||||||
|
* you can easily @ref sc_overloads "write your own string conversion" function for your custom types.
|
||||||
|
*
|
||||||
|
* @section sc_toString Converting to string
|
||||||
|
* You can use the `gz::toString()` function to turn certain types into strings.
|
||||||
|
* Concepts are used to determine the correct overload of the function.
|
||||||
|
*
|
||||||
|
* The concept ConvertibleToString is satisfied for types that can be converted to string
|
||||||
|
* using `gz::toString()`.
|
||||||
|
* @subsection sc_toStringImplemented Types convertible to string
|
||||||
|
* The toString() function is implemented for these types (non-exhaustive list of examples in braces)
|
||||||
|
* -# Any @ref util::Stringy "string-like type": eg. std::string, std::string_view { std::string, std::string_view }
|
||||||
|
* -# Any @ref util::CanConstructString "type that is accepted by the constructor of std::string()" { char, const char* }
|
||||||
|
* -# Any @ref util::WorksWithStdToString "type that works with std::to_string()" { int, double, bool ... }
|
||||||
|
* -# Any @ref util::HasToStringMember "type that has a `toString() const` or `to_string() const` member that returns a string"
|
||||||
|
* -# Any @ref util::ConvertibleToStringGlobal "type where a `std::string toString(const T&)` overload exists in global namespace
|
||||||
|
* -# Any of the following (the mentioned members have to satisfy one of 1-5)
|
||||||
|
* - Any @ref util::Vector2ConvertibleToString "type with t.x and t.y"
|
||||||
|
* - Any @ref util::Vector3ConvertibleToString "type with t.x, t.y, t.z"
|
||||||
|
* - Any @ref util::Vector4ConvertibleToString "type with t.x, t.y, t.z and t.w"
|
||||||
|
* - Any @ref util::Extent2DConvertibleToString "type with t.width, t.height"
|
||||||
|
* - Any @ref util::Extent3DConvertibleToString "type with t.width, t.height, t.depth"
|
||||||
|
* -# Any of 1-6, but "behind" a pointer
|
||||||
|
* -# Any @ref util::ForwardRangeConvertibleToString "type that has a forward_iterator" which references any one of 1-7 { std::vector<int>, std::list<float> }
|
||||||
|
* -# Any @ref util::PairConvertibleToString "type with t.first and t.second" members which satisfy one of 1-8 { std::pair<int, const char*> }
|
||||||
|
* -# Any @ref util::MapConvertibleToString "type that has a forward_iterator" which references 9 { std::map<unsigned long, std::vector<bool> }
|
||||||
|
*
|
||||||
|
* @section sc_fromString Construct from string
|
||||||
|
* You can use the `gz::fromString()` function to create certain types from a string.
|
||||||
|
*
|
||||||
|
* The concept ConstructibleFromString is satisfied for types that can be constructed from a string with `gz::fromString()`.
|
||||||
|
*
|
||||||
|
* When constructing something from a string, there is of course the problem that errors occur at runtime when
|
||||||
|
* the string is unsuitable for construction.
|
||||||
|
* In this case, the fromString functions from this library throw an exception (either InvalidArgument, or std::invalid_argument / std::out_of_range).
|
||||||
|
*
|
||||||
|
* @section sc_overloads Overloading the string conversion functions
|
||||||
|
* @subsection sc_ov_toString Overload for toString
|
||||||
|
* If you want your custom type to be convertible to string, you have different options.
|
||||||
|
* You can either add an `toString() const` member to your class
|
||||||
|
* or overload `std::string toString(const T&)` in global or gz namespace.
|
||||||
|
* Example for a class called `Custom`:
|
||||||
|
* @code
|
||||||
|
* std::string toString(const Custom& c) {
|
||||||
|
* std::string s;
|
||||||
|
* ...
|
||||||
|
* return s;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @subsection sc_ov_fromString Overload for fromString
|
||||||
|
* Writing an overload for fromString needs a little bit more boiler plate.
|
||||||
|
* Since fromString is a templated function, you need to declare it as such.
|
||||||
|
* The function declaration in this library uses a concept so that it is only
|
||||||
|
* declared for the types that are actully implemented.
|
||||||
|
* Example for a class called `Custom`:
|
||||||
|
* @code
|
||||||
|
* // declare as template, but only for Custom
|
||||||
|
* template<std::same_as<Custom> T>
|
||||||
|
* Custom fromString(const std::string& s);
|
||||||
|
*
|
||||||
|
* // instantiation/definition, but only for Custom
|
||||||
|
* template<>
|
||||||
|
* Custom fromString<Custom>(const std::string& s) {
|
||||||
|
* ...
|
||||||
|
* return Custom(...);
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @subsection sc_notes Notes
|
||||||
|
* You should write your function so that the output of one can be used as input of the other.
|
||||||
|
* In other words:
|
||||||
|
* @code
|
||||||
|
* Custom c1;
|
||||||
|
* Custom c2 = fromString(toString(c1));
|
||||||
|
* assert(c1 == c2);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* SettingsManager expects the strings from toString() to be a single line.
|
||||||
|
* If it has multiple lines, it will not load it correctly when loading from a file.
|
||||||
|
*
|
||||||
|
* @subsection sc_troubleshooting Troubleshoting
|
||||||
|
* @subsubsection sc_t_1 My custom overloads are not recognized
|
||||||
|
* To satisfy the concept(s) ConvertibleToString/ConstructibleFromString/StringConvertible, any overload must be visible to the compiler when the concept is processed,
|
||||||
|
* so you have to declare it before including `string/to_string.hpp`/`string/from_string.hpp` (which may also be included by `string/conversion.hpp`, `settings_manager.hpp` and `log.hpp`).
|
||||||
|
* To ease troubleshooting the include order, `string/to_string.hpp`/`string_from_string.hpp` each define the macro `GZ_UTIL_STRING_CONCEPTS`,
|
||||||
|
* so you can place an assertion like this before declaring your overloads:
|
||||||
|
* @code
|
||||||
|
* #ifdef GZ_UTIL_STRING_CONCEPTS
|
||||||
|
* static_assert(false, "gz-util/string/conversion.hpp must not be included before this file!");
|
||||||
|
* #endif
|
||||||
|
* @endcode
|
||||||
|
* @subsubsection sc_t_2 I want to use the toString/fromString functions from this library in my custom overloads
|
||||||
|
* Separate the declaration from the definition:
|
||||||
|
* @code
|
||||||
|
* // file.hpp
|
||||||
|
* // do not include any file from <gz-util/string/...>
|
||||||
|
* // declare your overloads:
|
||||||
|
* class Custom {
|
||||||
|
* std::string toString() const;
|
||||||
|
* }
|
||||||
|
* // or
|
||||||
|
* std::string toString(const Custom& c);
|
||||||
|
*
|
||||||
|
* template<std::same_as<Custom> T>
|
||||||
|
* T fromString(const std::string&)
|
||||||
|
*
|
||||||
|
* // file.cpp
|
||||||
|
* #include "file.hpp"
|
||||||
|
* #include <gz-util/string/conversion.hpp>
|
||||||
|
*
|
||||||
|
* // define your functions
|
||||||
|
* std::string Custom::toString() const {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* // or
|
||||||
|
* std::string toString(const Custom& c) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* template<>
|
||||||
|
* Custom fromString(const std::string& s) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @subsubsection sc_t_3 I want to write the toString() for my Vector types myself
|
||||||
|
* Define the macro `GZ_TO_STRING_NO_VECTORS` before including `<gz-util/string/to_string.hpp>`.
|
||||||
|
* This disables all overloads for Vector and Extent classes.
|
||||||
|
*
|
||||||
|
* @todo Implement fromString for ranges that hold ConstructibleFromString types
|
||||||
|
*
|
||||||
|
* @section sc_int_types Converting integers to/from strings with different base
|
||||||
|
* The functions toHexString(), toOctString() and toBinString() can be used to get a
|
||||||
|
* string of an integers representation in 16 / 8 / 2 basis.
|
||||||
|
*
|
||||||
|
* The functions fromHexString<T>(), fromOctString<T>() and fromBinString<T>() can be used to get an integer
|
||||||
|
* from a string representation of an integer in 16 / 8 / 2 basis.
|
||||||
|
* These function can throw std::invalid_argument and std::out_of_range.
|
||||||
|
*
|
||||||
|
* @note toHexString() and toOctString() do not work well with `(unsigned) char` (`(u)int8_t`).
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
} // namespace gz
|
123
src/string/from_string.hpp
Normal file
123
src/string/from_string.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../concepts.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define GZ_UTIL_STRING_CONCEPTS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declaration of fromString in global namespace, so that concepts can use it
|
||||||
|
* @details
|
||||||
|
* This declaration only exists so that ::fromString can be used in concepts.
|
||||||
|
*/
|
||||||
|
template<gz::util::False T>
|
||||||
|
T fromString(const std::string& s);
|
||||||
|
|
||||||
|
namespace gz::util {
|
||||||
|
template<typename T>
|
||||||
|
concept ConstructibleFromStringGlobal = requires(const std::string& s) {
|
||||||
|
{ ::fromString<T>(s) } -> std::same_as<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief toString is implemented for these types
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
concept GetTypeFromStringImplemented =
|
||||||
|
std::same_as<T, int> or
|
||||||
|
std::same_as<T, long> or
|
||||||
|
std::same_as<T, long long> or
|
||||||
|
std::same_as<T, unsigned int> or
|
||||||
|
std::same_as<T, unsigned long> or
|
||||||
|
std::same_as<T, unsigned long long> or
|
||||||
|
std::same_as<T, float> or
|
||||||
|
std::same_as<T, double> or
|
||||||
|
std::same_as<T, long double> or
|
||||||
|
std::same_as<T, bool>;
|
||||||
|
} // namespace gz::util
|
||||||
|
|
||||||
|
namespace gz {
|
||||||
|
/**
|
||||||
|
* @name Construct a type from a string
|
||||||
|
* @note
|
||||||
|
* The fromString() functions (except for the bool one) simply return std::stoXX. These can throw std::invalid_argument and std::out_of_range.
|
||||||
|
* See https://en.cppreference.com/w/cpp/string/basic_string/stol
|
||||||
|
*/
|
||||||
|
/// @{
|
||||||
|
/**
|
||||||
|
* @brief Declaration of fromString, but only for the types for which it is implemented
|
||||||
|
*/
|
||||||
|
template<util::GetTypeFromStringImplemented T>
|
||||||
|
T fromString(const std::string& s);
|
||||||
|
|
||||||
|
/// @returns std::stoi(s)
|
||||||
|
template<> inline int fromString<int>(const std::string& s) {
|
||||||
|
return std::stoi(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stol(s)
|
||||||
|
template<> inline long fromString<long>(const std::string& s) {
|
||||||
|
return std::stol(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stoll(s)
|
||||||
|
template<> inline long long fromString<long long>(const std::string& s) {
|
||||||
|
return std::stoll(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stoul(s)
|
||||||
|
template<> inline unsigned int fromString<unsigned int>(const std::string& s) {
|
||||||
|
return std::stoul(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stoul(s)
|
||||||
|
template<> inline unsigned long fromString<unsigned long>(const std::string& s) {
|
||||||
|
return std::stoul(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stoull(s)
|
||||||
|
template<> inline unsigned long long fromString<unsigned long long>(const std::string& s) {
|
||||||
|
return std::stoull(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stof(s)
|
||||||
|
template<> inline float fromString<float>(const std::string& s) {
|
||||||
|
return std::stof(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stod(s)
|
||||||
|
template<> inline double fromString<double>(const std::string& s) {
|
||||||
|
return std::stod(s);
|
||||||
|
}
|
||||||
|
/// @returns std::stold(s)
|
||||||
|
template<> inline long double fromString<long double>(const std::string& s) {
|
||||||
|
return std::stold(s);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Convert a string to bool
|
||||||
|
* @details
|
||||||
|
* - returns true if s = "true" or "True" or "1"
|
||||||
|
* - returns false if s = "false" or "False" or "0"
|
||||||
|
* - throws InvalidArgument otherwise
|
||||||
|
*/
|
||||||
|
template<> bool fromString<bool>(const std::string& s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct T from a string using a fromString that is declared in global namespace
|
||||||
|
*/
|
||||||
|
template<util::ConstructibleFromStringGlobal T>
|
||||||
|
inline T fromString(const std::string& s) {
|
||||||
|
return ::fromString<T>(s);
|
||||||
|
}
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Any type where fromString(string) exists and returns T
|
||||||
|
* @note The function only has to exist, it does not have to be noexcept!
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
concept ConstructibleFromString = requires(const std::string& s) {
|
||||||
|
{ fromString<T>(s) } -> std::same_as<T>;
|
||||||
|
};
|
||||||
|
} // namespace gz
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Contains functions to construct types from string
|
||||||
|
* @todo Make these functions usable with std::string_view
|
||||||
|
*/
|
418
src/string/to_string.hpp
Normal file
418
src/string/to_string.hpp
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../concepts.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
#define GZ_UTIL_STRING_CONCEPTS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declaration of toString in global namespace, so that concepts can use it.
|
||||||
|
*/
|
||||||
|
template<gz::util::False T>
|
||||||
|
std::string toString(const T& s);
|
||||||
|
|
||||||
|
namespace gz::util {
|
||||||
|
/**
|
||||||
|
* @name 1) concepts for elementary types and custom toStrings()
|
||||||
|
* @details
|
||||||
|
* Definition order matters, the earlier the concept, the "stronger" because later concepts must not also satisfy the previous ones.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/// same as std::string, std::string_view or const char*
|
||||||
|
template<typename T>
|
||||||
|
concept Stringy = util::IsInPack<T, std::string, std::string_view, const char*>;
|
||||||
|
|
||||||
|
/// can construct std::string from T
|
||||||
|
template<typename T>
|
||||||
|
concept CanConstructString =
|
||||||
|
!Stringy<T> &&
|
||||||
|
requires(const T& t) {
|
||||||
|
{ std::string(t) } -> std::same_as<std::string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// has .toString() const member
|
||||||
|
template<typename T>
|
||||||
|
concept HasToStringMember =
|
||||||
|
!Stringy<T> &&
|
||||||
|
!CanConstructString<T> &&
|
||||||
|
requires(const T& t) { { t.toString() }-> Stringy; };
|
||||||
|
|
||||||
|
/// has .to_string() const member
|
||||||
|
template<typename T>
|
||||||
|
concept HasToStringMember2 =
|
||||||
|
!Stringy<T> &&
|
||||||
|
!CanConstructString<T> &&
|
||||||
|
!HasToStringMember<T> &&
|
||||||
|
requires(const T& t) { { t.to_string() }-> Stringy; };
|
||||||
|
|
||||||
|
/// works with std::to_string()
|
||||||
|
template<typename T>
|
||||||
|
concept WorksWithStdToString =
|
||||||
|
!Stringy<T> &&
|
||||||
|
!CanConstructString<T> &&
|
||||||
|
!HasToStringMember<T> &&
|
||||||
|
!HasToStringMember2<T> &&
|
||||||
|
requires(const T& t) { { std::to_string(t) } -> Stringy; };
|
||||||
|
|
||||||
|
/// toString function overload exists in global namespace and returns std::string
|
||||||
|
template<typename T>
|
||||||
|
concept ConvertibleToStringGlobal =
|
||||||
|
!Stringy<T> &&
|
||||||
|
!CanConstructString<T> &&
|
||||||
|
!HasToStringMember<T> &&
|
||||||
|
!HasToStringMember2<T> &&
|
||||||
|
!WorksWithStdToString<T> &&
|
||||||
|
requires(const T& t) {
|
||||||
|
{ ::toString(t) } -> std::same_as<std::string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept _ToStringBasicNoPtr =
|
||||||
|
Stringy<T> ||
|
||||||
|
CanConstructString<T> ||
|
||||||
|
HasToStringMember<T> ||
|
||||||
|
HasToStringMember2<T> ||
|
||||||
|
WorksWithStdToString<T> ||
|
||||||
|
ConvertibleToStringGlobal<T>;
|
||||||
|
|
||||||
|
#ifndef GZ_TO_STRING_NO_VECTORS
|
||||||
|
// VECTOR
|
||||||
|
/// Type having string-convertible x, y members and sizeof(T) == 2 * sizeof(x)
|
||||||
|
template<typename T>
|
||||||
|
concept Vector2ConvertibleToString = !_ToStringBasicNoPtr<T> &&
|
||||||
|
requires(T t) {
|
||||||
|
{ t.x } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.y } -> _ToStringBasicNoPtr;
|
||||||
|
requires sizeof(t.x) * 2 == sizeof(T);
|
||||||
|
};
|
||||||
|
/// Type having string-convertible x, y, z members and sizeof(T) == 3 * sizeof(x)
|
||||||
|
template<typename T>
|
||||||
|
concept Vector3ConvertibleToString = !_ToStringBasicNoPtr<T> &&
|
||||||
|
requires(T t) {
|
||||||
|
{ t.x } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.y } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.z } -> _ToStringBasicNoPtr;
|
||||||
|
requires sizeof(t.x) * 3 == sizeof(T);
|
||||||
|
};
|
||||||
|
/// Type having string-convertible x, y, z, w members and sizeof(T) == 4 * sizeof(x)
|
||||||
|
template<typename T>
|
||||||
|
concept Vector4ConvertibleToString = !_ToStringBasicNoPtr<T> &&
|
||||||
|
requires(T t) {
|
||||||
|
{ t.x } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.y } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.z } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.w } -> _ToStringBasicNoPtr;
|
||||||
|
requires sizeof(t.x) * 4 == sizeof(T);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Type having string-convertible width, height members and sizeof(T) == 2 * sizeof(width)
|
||||||
|
template<typename T>
|
||||||
|
concept Extent2DConvertibleToString = !_ToStringBasicNoPtr<T> &&
|
||||||
|
requires(T t) {
|
||||||
|
{ t.width } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.height } -> _ToStringBasicNoPtr;
|
||||||
|
requires sizeof(t.width) * 2 == sizeof(T);
|
||||||
|
};
|
||||||
|
/// Type having string-convertible width, height, depth members and sizeof(T) == 3 * sizeof(width)
|
||||||
|
template<typename T>
|
||||||
|
concept Extent3DConvertibleToString = !_ToStringBasicNoPtr<T> &&
|
||||||
|
requires(T t) {
|
||||||
|
{ t.width } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.height } -> _ToStringBasicNoPtr;
|
||||||
|
{ t.depth } -> _ToStringBasicNoPtr;
|
||||||
|
requires sizeof(t.width) * 3 == sizeof(T);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept _ToStringBasicOrVectorNoPtr =
|
||||||
|
_ToStringBasicNoPtr<T>
|
||||||
|
#ifndef GZ_TO_STRING_NO_VECTORS
|
||||||
|
|| Vector2ConvertibleToString<T> ||
|
||||||
|
Vector3ConvertibleToString<T> ||
|
||||||
|
Vector4ConvertibleToString<T> ||
|
||||||
|
Extent2DConvertibleToString<T> ||
|
||||||
|
Extent3DConvertibleToString<T>
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept PtrToToStringBasicOrVector =
|
||||||
|
!_ToStringBasicOrVectorNoPtr<T> &&
|
||||||
|
_ToStringBasicOrVectorNoPtr<std::remove_pointer_t<T>>;
|
||||||
|
/// @}
|
||||||
|
} // namespace gz::util
|
||||||
|
|
||||||
|
|
||||||
|
namespace gz {
|
||||||
|
/**
|
||||||
|
* @name 1) Converting a type to string
|
||||||
|
* @details
|
||||||
|
* All toString functions for types that satisfy util::ToStringBasicNoPtr or util::PtrToToStringBasicType
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @brief Return the string
|
||||||
|
* @returns static_cast<std::string>(t)
|
||||||
|
*/
|
||||||
|
template<util::Stringy T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return static_cast<std::string>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a string from a string like-type
|
||||||
|
* @returns std::string(t)
|
||||||
|
*/
|
||||||
|
template<util::CanConstructString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return std::string(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a type having a toString() const member function
|
||||||
|
* @returns t.toString()
|
||||||
|
*/
|
||||||
|
template<util::HasToStringMember T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return t.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a type having a to_string() const member function
|
||||||
|
* @returns t.to_string()
|
||||||
|
*/
|
||||||
|
template<util::HasToStringMember2 T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return t.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a number
|
||||||
|
* @returns std::to_string(t)
|
||||||
|
*/
|
||||||
|
template<util::WorksWithStdToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return std::to_string(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a string from a boolean
|
||||||
|
* @details
|
||||||
|
* Unlike std::to_string(bool), which returns 0 or 1, this function returns "true" or "false"
|
||||||
|
* @returns "true" or "false"
|
||||||
|
*/
|
||||||
|
template<std::same_as<bool> T>
|
||||||
|
inline std::string toString(const T& b) {
|
||||||
|
return b ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a type that has toString declared in global namespace
|
||||||
|
*/
|
||||||
|
template<util::ConvertibleToStringGlobal T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return ::toString(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef GZ_TO_STRING_NO_VECTORS
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a vector with x, y members
|
||||||
|
* @returns ( x, y )
|
||||||
|
*/
|
||||||
|
template<util::Vector2ConvertibleToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
std::string s = "( ";
|
||||||
|
s += toString(t.x) + ", ";
|
||||||
|
s += toString(t.y) + " )";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a vector with x, y members
|
||||||
|
* @returns ( x, y, z )
|
||||||
|
*/
|
||||||
|
template<util::Vector3ConvertibleToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
std::string s = "( ";
|
||||||
|
s += toString(t.x) + ", ";
|
||||||
|
s += toString(t.y) + ", ";
|
||||||
|
s += toString(t.z) + " )";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a vector with x, y, z, w members
|
||||||
|
* @returns ( x, y, z, w )
|
||||||
|
*/
|
||||||
|
template<util::Vector4ConvertibleToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
std::string s = "( ";
|
||||||
|
s += toString(t.x) + ", ";
|
||||||
|
s += toString(t.y) + ", ";
|
||||||
|
s += toString(t.z) + ", ";
|
||||||
|
s += toString(t.w) + " )";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a type having width and height members
|
||||||
|
* @returns ( width, height )
|
||||||
|
*/
|
||||||
|
template<util::Extent2DConvertibleToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
std::string s = "( ";
|
||||||
|
s += toString(t.width) + ", ";
|
||||||
|
s += toString(t.height) + " ) ";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a type having width and height members
|
||||||
|
* @returns ( width, height, depth )
|
||||||
|
*/
|
||||||
|
template<util::Extent3DConvertibleToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
std::string s = "( ";
|
||||||
|
s += toString(t.width) + ", ";
|
||||||
|
s += toString(t.height) + ", ";
|
||||||
|
s += toString(t.depth) + " ) ";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from the element the pointer points at
|
||||||
|
* @returns *t
|
||||||
|
*/
|
||||||
|
template<util::PtrToToStringBasicOrVector T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return toString(*t);
|
||||||
|
}
|
||||||
|
} // namespace gz
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace gz::util {
|
||||||
|
/**
|
||||||
|
* @name 2) concepts for containers that hold toString() compatible types
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/// Everything that is convertible to string with toString at this point
|
||||||
|
template<typename T>
|
||||||
|
concept _ConvertibleToString =
|
||||||
|
requires (const T& t) {
|
||||||
|
{ gz::toString(t) } -> std::same_as<std::string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// CONTAINER
|
||||||
|
/// Forward range having string-convertible elements
|
||||||
|
template<typename T>
|
||||||
|
concept ForwardRangeConvertibleToString = !_ConvertibleToString<T> and std::ranges::forward_range<T> and _ConvertibleToString<std::ranges::range_value_t<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept _TypeOrForwardRangeConvertibleToString = _ConvertibleToString<T> || ForwardRangeConvertibleToString<T>;
|
||||||
|
|
||||||
|
/// Pair having string-convertible elements
|
||||||
|
template<typename T>
|
||||||
|
concept PairConvertibleToString = !ForwardRangeConvertibleToString<T> and requires(const T& p) {
|
||||||
|
requires _TypeOrForwardRangeConvertibleToString<decltype(p.first)>;
|
||||||
|
requires _TypeOrForwardRangeConvertibleToString<decltype(p.second)>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Container having string-convertible pairs
|
||||||
|
template<typename T>
|
||||||
|
concept MapConvertibleToString = !PairConvertibleToString<T> and !_TypeOrForwardRangeConvertibleToString<T> and
|
||||||
|
std::ranges::forward_range<T> and PairConvertibleToString<std::ranges::range_reference_t<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept _ContainerTypeConvertibleToString = PairConvertibleToString<T> || ForwardRangeConvertibleToString<T> || MapConvertibleToString<T>;
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
} // namespace gz::util
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace gz {
|
||||||
|
/**
|
||||||
|
* @name 2) Converting a container to string
|
||||||
|
* @details
|
||||||
|
* All toString functions for types that satisfy util::_ContainerTypeConvertibleToString
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a forward range
|
||||||
|
* @returns [ x1, x2, ... ]
|
||||||
|
*/
|
||||||
|
template<util::ForwardRangeConvertibleToString T>
|
||||||
|
std::string toString(const T& t) {
|
||||||
|
std::string s = "[ ";
|
||||||
|
for (auto it = t.begin(); it != t.end(); it++) {
|
||||||
|
s += toString(*it) + ", ";
|
||||||
|
}
|
||||||
|
if (s.size() > 2) {
|
||||||
|
s.erase(s.size() - 2);
|
||||||
|
}
|
||||||
|
s += " ]";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a pair
|
||||||
|
* @returns ( first, second )
|
||||||
|
*/
|
||||||
|
template<util::PairConvertibleToString T>
|
||||||
|
inline std::string toString(const T& t) {
|
||||||
|
return "( " + toString(t.first) + ", " + toString(t.second) + " )";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload
|
||||||
|
* @brief Construct a string from a forward range holding a pair, eg a map
|
||||||
|
* @returns { first: second, first: second, ... }
|
||||||
|
*/
|
||||||
|
template<util::MapConvertibleToString T>
|
||||||
|
std::string toString(const T& t) {
|
||||||
|
std::string s = "{ ";
|
||||||
|
for (const auto& [k, v] : t) {
|
||||||
|
s += toString(k) + ": ";
|
||||||
|
s += toString(v) + ", ";
|
||||||
|
}
|
||||||
|
if (s.size() > 2) {
|
||||||
|
s.erase(s.size() - 2);
|
||||||
|
}
|
||||||
|
s += " }";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Any type where gz::toString(t) exists
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
concept ConvertibleToString =
|
||||||
|
requires (const T& t) {
|
||||||
|
{ gz::toString(t) } -> std::same_as<std::string>;
|
||||||
|
};
|
||||||
|
} // namespace gz
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Contains functions to convert types to string
|
||||||
|
*/
|
59
src/string/utility.cpp
Normal file
59
src/string/utility.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
namespace gz::util {
|
||||||
|
|
||||||
|
std::vector<std::string> splitStringInVector(std::string& s, char separator) {
|
||||||
|
// remove linebreaks from the end
|
||||||
|
if (*(s.end()) == '\n') { s.erase(s.end()); }
|
||||||
|
|
||||||
|
/* std::unique_ptr<std::unordered_map<Entity, std::string>> params (new std::unordered_map<Entity, std::string>); */
|
||||||
|
std::vector<std::string> v;
|
||||||
|
std::stringstream ss(s);
|
||||||
|
std::string temp;
|
||||||
|
|
||||||
|
while (std::getline(ss, temp, separator)) {
|
||||||
|
// if has "=": store latter part in vector
|
||||||
|
if (temp.find("=") != std::string::npos) {
|
||||||
|
int eqPos = temp.find("=");
|
||||||
|
v.emplace_back(temp.substr(eqPos + 1, temp.length()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
v.emplace_back(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<SplitStringInVectorImplemented T>
|
||||||
|
std::vector<T> splitStringInVector(const std::string_view& s, const std::string& separator, bool skipEmptyStrings) {
|
||||||
|
std::vector<T> v;
|
||||||
|
|
||||||
|
std::string::size_type posStart = 0;
|
||||||
|
std::string::size_type posEnd = s.find(separator, posStart);
|
||||||
|
while (posEnd != std::string::npos) {
|
||||||
|
if (!(skipEmptyStrings and posStart == posEnd)) {
|
||||||
|
v.emplace_back(T(s.begin() + posStart, s.begin() + posEnd));
|
||||||
|
}
|
||||||
|
posStart = posEnd + 1;
|
||||||
|
posEnd = s.find(separator, posStart);
|
||||||
|
}
|
||||||
|
// last element
|
||||||
|
if (posStart < s.size()) {
|
||||||
|
v.emplace_back(T(s.begin() + posStart, s.end()));
|
||||||
|
}
|
||||||
|
// if last char is separator, append empty string
|
||||||
|
else if (!skipEmptyStrings and posStart == s.size()) {
|
||||||
|
v.emplace_back(T(s.begin(), s.begin()));
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template std::vector<std::string_view> splitStringInVector<std::string_view>(const std::string_view&, const std::string&, bool);
|
||||||
|
template std::vector<std::string> splitStringInVector<std::string>(const std::string_view&, const std::string&, bool);
|
||||||
|
|
||||||
|
} // namespace gz::util
|
77
src/string/utility.hpp
Normal file
77
src/string/utility.hpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gz::util {
|
||||||
|
template<typename T>
|
||||||
|
concept SplitStringInVectorImplemented = std::same_as<T, std::string_view> || std::same_as<T, std::string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Split a string at a separator into a vector
|
||||||
|
* @details
|
||||||
|
* Splits a string at a separator.\n
|
||||||
|
* Two templates exist, one where the vector will hold std::string_views into s
|
||||||
|
* and one where it will hold std::strings.
|
||||||
|
* Behavior:
|
||||||
|
* - the elements of the returned vector will not contain the separator.
|
||||||
|
* - if the separator is not in s, the vector will contain s as only element.
|
||||||
|
* - empty elements will occur if:
|
||||||
|
* - separator is the first char
|
||||||
|
* - separator is the last char
|
||||||
|
* - two or more separator appear after each other\n
|
||||||
|
* You can turn this behaviour off by setting skipEmptyStrings to true
|
||||||
|
* @note
|
||||||
|
* The string_view variant has the advantage of being faster, however:\n
|
||||||
|
* The string_views will reference the original string, so it must not be changed or destroyed
|
||||||
|
* as long as the string_views are used!
|
||||||
|
*/
|
||||||
|
template<SplitStringInVectorImplemented T>
|
||||||
|
std::vector<T> splitStringInVector(const std::string_view& s, const std::string& separator, bool skipEmptyStrings=false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Map with string type as key, works with strings, string_view and char*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
struct string_hash
|
||||||
|
{
|
||||||
|
using hash_type = std::hash<std::string_view>;
|
||||||
|
using is_transparent = void;
|
||||||
|
|
||||||
|
size_t operator()(const char* str) const { return hash_type{}(str); }
|
||||||
|
size_t operator()(std::string_view str) const { return hash_type{}(str); }
|
||||||
|
size_t operator()(std::string const& str) const { return hash_type{}(str); }
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @brief A unordered_map where you can use string_views to access elements
|
||||||
|
* @details
|
||||||
|
* To retrieve an element using a string view, you need to do this:
|
||||||
|
* @code
|
||||||
|
* std::string_view sv = ...;
|
||||||
|
* gz::util::unordered_string_map<T> smap = { ... };
|
||||||
|
* ...
|
||||||
|
* if (smap.contains(sv)) {
|
||||||
|
* return smap.find(sv)->second;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* The at() member and [] operator do not work with the string_view.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
using unordered_string_map = std::unordered_map<std::string, T, util::string_hash, std::equal_to<>>;
|
||||||
|
/**
|
||||||
|
* @brief same as unordered_string_map, but using std::map instead of std::unordered_map
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
using string_map = std::map<std::string, T, util::string_hash, std::equal_to<>>;
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace gz::util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Contains utility for strings
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user