Linux 🐧 | Mac | Windows |
---|---|---|
Converter is an easy-to-use C++ text ↔ type
conversion library. It supports C++20 (and
later), is header-only and comes with a basic test suite.
Converter is implemented using C++20 with code portable across OS and it's supported compiler's.
Ubit-Test results of last run:
🖥️ OS ➡️ 🤖 Compiler ⬇️ |
Ubuntu 24.04 | Ubuntu 22.04 | macOS-14 (ARM64) |
macOS-13 | macOS-12 | Windows 10 VS 17 - 2022 |
Windows 10 VS 16 - 2019 |
---|---|---|---|---|---|---|---|
g++ 14 | ✅ | - | ✅ | ✅ | ✅ | - | - |
g++ 13 | ✅ (default) | ⚠ | ✅ | ❌ | ❌ | - | - |
g++ 12 | ✅ | ✅ | ✅ | ✅ | ✅ | - | - |
g++ 11 | - | ✅ (default) | - | - | ⚠ | - | - |
clang++ (18,17,16) |
✅ (clang++:18) |
- | - | - | - | - | - |
clang++ (15,14,13) |
- | ✅ (clang++:14) |
- | - | - | - | - |
AppleClang (default) | - | - | ✅ (15) | ✅ (15) | ✅ (14) | - | - |
msvc 19 | - | - | - | - | - | ✅ (default) | ❌ (default) |
clangCL | - | - | - | - | - | ✅ (v17) | ✅ (v12) |
default Compiler | g++13 | g++11 | AppleClang 15 | AppleClang 15 | AppleClang 14 | msvc 19 | msvc 19 |
Previous successful Unit-test runs (now failing for some reasons) :
🖥️ OS ➡️ 🤖 Compiler ⬇️ |
Ubuntu 22.04 | macOS-13 | macOS-12 | ⚠ macOS-11 |
---|---|---|---|---|
g++ 13 | ☑️⚠ | - | ☑️❌ | NA |
g++ 12 | ^ | - | ^ | ☑️⚠ 12 |
g++ 11 | ^ | - | ☑️⚠ | ☑️⚠ 11 |
clang++ 12 | ☑️⚠ 12 | - | - | - |
☑️ : Was working earlier.
⚠ : Now Withdrawn.
❌ : Now Fails.
Converter may be included in a CMake project using FetchContent. Refer to the CMake FetchContent Example Project and in particular its CMakeLists.txt.
Converter may be included in a CMake project using Add-SubDirectory. Refer to the CMake Add-SubDirectory Example Project and in particular its CMakeLists.txt.
Floating-types, Integer-types, char-types, bool, std::chrono::year_month_day
are the various types supported.
Special types ci_string
(i.e case-insensitive string) and format_year_month_day<format-info>
(date-type with built-in format needed during string-conversion).
Use T ConvertFromStr<T>::ToVal(const std::string&)
for std::string
to T conversions.
Use std::string ConvertFromVal<T>::ToStr(const T&)
for T to std::string
conversions.
The Default convertion functions, maps to best available converter functions (for a type) provided by std library.
For floating point types, the converter S2T function calls std::from_chars()
and the converter T2S calls std::to_chars()
, if the compiler supports std::*_chars
functions. The table below, is based on test results of testFloatingPointPrecision.cpp.
Function Nomenclature:
⚔️ : uses std::from_chars()
and std::to_chars()
for data conversion.
🛠️ : uses std::from_string()
and std::operator<<
workarounds, for data conversion. As complier doesn't support std::*_chars()
functions. Precision is less compared to above approach.
Precision Nomenclature ( for roundtrip conversions :: string ↣ data-type ↣ string):
✅ : Good. Minimal precision loss, best of the lot.
☑️ : Average. Precision loss is non-uniform, loss can be high for some FP values.
🖥️ OS ➡️ 🤖 Compiler ⬇️ |
Ubuntu 24.04 | Ubuntu 22.04 | macOS-14 (ARM64) |
macOS-13 | macOS-12 | macOS-11 | Windows 10 VS 17 - 2022 |
Windows 10 VS 16 - 2019 |
---|---|---|---|---|---|---|---|---|
g++ 14 | ⚔️ ✅ | - | ⚔️ ☑️ | ⚔️ ✅ | ⚔️ ✅ | - | - | - |
g++ 13 | ⚔️ ✅ | ⚠ | ⚔️ ☑️ | ❌ | ❌ | - | - | - |
g++ 12 | ⚔️ ✅ | ⚔️ ✅ | ⚔️ ☑️ | ⚔️ ✅ | ⚔️ ✅ | ⚔️ ✅ | - | - |
g++ 11 | - | ⚔️ ✅ | - | - | ⚠ | ⚔️ ✅ | - | - |
clang++ (18,17,16) | ⚔️ ✅ | - | - | - | - | - | - | - |
clang++ (15,14,13) | - | ⚔️ ✅ | - | - | - | - | - | - |
AppleClang (default) | - | - | 🛠️ ☑️ (15) | 🛠️ ☑️ (15) | 🛠️ ☑️ (14) | - | - | - |
msvc 19 | - | - | - | - | - | - | ⚔️ ☑️ | ❌ |
clangCL | - | - | - | - | - | - | ⚔️ ☑️ (17) | ⚔️ ☑️ (12) |
For floating-point numbers, the default decimal precision is determined by the return value of the call to getDecimalPrecision<T>()
, this value is passed as template parameter to class-template T2S_Format_StreamDecimalPrecision
.
template<c_floating_point T, int decimalPrecision = getDecimalPrecision<T>()>
struct T2S_Format_StreamDecimalPrecision;
There is loss of data-precision at several places, for e.g:
float pi_val = 3.1415926535f; // (1)
std::ostringstream oss;
oss << pi_val; std::string pi_str = oss.str(); // (2)
std::istringstream iss(pi_str);
float pi_read;
iss >> pi_read; // (3)
Potential data-precision loss can happen at steps (1), (2) and (3).
The precision-loss at (1), where in rvalue-raw is captured to the nearest binary-bits equivalent
in lvalue-variable. This is system dependent.
It's better to write the value of lvalue-variable(pi_val) at a
higher precision digits for float(hence +3). As we need to eliminate the
precision loss happening at steps (2) and (3).
The repeatability or read-write accuracy can only be achieved by using
higher precision of that specified by precision accuracy for float.
Refer testFloatingPointPrecision.cpp for data-loss in text ↣ data-type ↣ text, conversion cycle. Refer testUserDefinedConverter_lowerPrecision.cpp, to see the effect of lowered Decimal-Precision when writing the data and then retriving it back.
For integer types, the converter S2T function calls std::from_chars()
and the converter T2S calls std::to_chars()
.
std::chrono::year_month_day
is used for date-type.
As of writing this code, std::chrono
is not fully supported by various OS's, in that case converter
lib, calls date lib for conversions.
Below table shows, the underlying conversion function(i.e between std::chrono::*_stream()
or date::*_stream()
) used across different OS's.
For string ↣ year_month_day conversion, is handled by function ...
std::chrono::year_month_day ConverterFromStr<std::chrono::year_month_day>::ToVal(const std::string&)
.
For year_month_day ↣ string conversion, is handled by function ...
std::string ConverterFromVal<std::chrono::year_month_day>::ToStr(const std::chrono::year_month_day&)
.
Available date conversions :
🤖 Algo/LIB ➡️ ⏱ conversion type ⬇️ |
std::chrono:: prefered |
date:: 3rd party lib |
jugaad work around |
---|---|---|---|
string ↣ year_month_day | ♔ std::chrono::from_stream() | ♘ date::from_stream() | ♙ hand-coded tokenizer |
year_month_day ↣ string | ♚ std::ostringstream << std::vformat() | ♞ date::to_stream() | ♟ hand-coded tokenizer |
Date-Conversions used across different OS/Compiler combinations :
🖥️ OS ➡️ 🤖 Compiler ⬇️ |
Ubuntu 24.04 | Ubuntu 22.04 | macOS-14 (ARM64) |
macOS-13 | macOS-12 | macOS-11 | Windows 10 VS 17 - 2022 |
Windows 10 VS 16 - 2019 |
---|---|---|---|---|---|---|---|---|
g++ 14 | ♔ ♚ | - | ♔ ♚ | ♔ ♚ | ♔ ♚ | - | - | - |
g++ 13 | ♘ ♚ | ⚠ | ♘ ♚ | ❌ | ❌ | - | - | - |
g++ 12 | ♘ ♞ | ♘ ♞ | ♘ ♞ | ♘ ♞ | ♘ ♞ | ♘ ♞ | - | - |
g++ 11 | - | ♘ ♞ | - | - | ⚠ | ♘ ♞ | - | - |
clang++ (18,17,16) | ♔ ♚ | - | - | - | - | - | - | - |
clang++ (15,14,13) | - | ♘ ♞ | - | - | - | - | - | - |
AppleClang (default) | - | - | ♘ ♚ (15) | ♘ ♞ (15) | ♘ ♞ (14) | - | - | - |
msvc 19 | - | - | - | - | - | - | ♔ ♚ | ❌ |
clangCL | - | - | - | - | - | - | ♔ ♚ (17) | ♔ ♚ (12) |
The default date format is "%F" (i.e "%Y-%m-%d"). For configuring a different date format refer testDateConversionFormat.cpp.
For char-types
, converter interprets the cell's (first)
byte as a character. For bool
, the expected integer values are 0
or 1
.
converter by default, uses std::from_chars()
conversion function(s) when parsing numeric-type values from string. std::from_chars()
donot use any locale for parsing. Though caveat observed for macos-13
and g++-11
combination, where in std::from_chars()
is dependent on C-locale, refer testUserDefinedConverter_locale.cpp for details.
Note that std::from_chars()
is not supported by 'AppleClang' compiler, refer testFloatingPointLocale.cpp for interplay between user-defined-locale, C-locale and limitations of 'AppleClang' compiler.
To use a particular locale for parsing numeric values, stream conversion can be used as shown below.
Refer testUserDefinedConverter_locale.cpp for full details.
// string literal object with static storage duration
constexpr char de_Loc[] = "de_DE.UTF-8"; // uses comma (,) as decimal separator
template<typename T>
using deLocal_iss = converter::S2T_Format_StreamUserLocale<T, converter::FailureS2Tprocess::THROW_ERROR, char, de_Loc>;
template<converter::c_floating_point T>
using ConvertFromStr_loc = converter::ConvertFromStr<T, deLocal_iss<T> >;
It is possible to configure converter to use locale dependent parsing by passing template-parameter
S2T_FORMAT=converter::S2T_Format_StreamAsIs<T, converter::FailureS2Tprocess::THROW_ERROR, char>
.
Refer testUserDefinedConverter_locale.cpp.
template<typename T>
using convertS2T_stream =
converter::ConvertFromStr<T,
converter::S2T_Format_StreamAsIs<T,
converter::FailureS2Tprocess::THROW_ERROR,
char>
>;
... or configure converter to use either classic-locale parsing by setting template-parameter
S2T_FORMAT=converter::S2T_Format_StreamUseClassicLocale<T, converter::FailureS2Tprocess::THROW_ERROR, char>
.
Refer testFloatingPointNaN.cpp and testIntegerNaN.cpp.
template <typename T, converter::FailureS2Tprocess EP>
using _ConvS2T_ISS = converter::ConvertFromStr<T, converter::S2T_Format_StreamUseClassicLocale<T, EP, char>>;
converter by default uses, std::to_chars()
conversion functions when converting numeric-type values to string.
To use a particular locale for converting numeric values, stream conversion can be used as shown below.
Refer testUserDefinedConverter_locale.cpp for full details.
template<typename T>
using deLocal_oss = converter::T2S_Format_StreamUserLocale<char, de_Loc>;
template<typename T>
using combine_deLocal_oss =
converter::T2S_Format_floating_StreamCombine< T,
converter::T2S_Format_StreamDecimalPrecision<T>,
deLocal_oss<T>
>;
It is possible to configure converter to use locale dependent parsing by changing template-parameter T2S_FORMAT=T2S_Format_StreamAsIs<char>
.
... or configure converter to use either classic-locale parsing by setting template-parameter T2S_FORMAT=T2S_Format_StreamUseClassicLocale<char>
. Refer testIntegerNaN.cpp.
template <typename T>
using _ConvT2S_OSS = converter::ConvertFromVal<T, converter::T2S_Format_StreamUseClassicLocale<char>>;
With floating point types std::to_string(...)
may yield unexpected results as the number of significant digits in the returned string can be zero, for e.g: pVal = 1e-09
. The return value may differ significantly from what std::cout
prints by default. This particular specialization is disabled by default. In case if this is needed, enable it by defining macro ENABLE_STD_TtoS
.
A different precision can be specified as below. Refer testUserDefinedConverter_lowerPrecision.cpp for more details.
template<converter::c_floating_point T>
constexpr int getLowerDecimalPrecision()
{
if constexpr (std::is_same_v<T, float>)
return FLT_DIG-1;
else
if constexpr (std::is_same_v<T, double>)
return DBL_DIG-1;
else
if constexpr (std::is_same_v<T, long double>)
return LDBL_DIG-1;
}
template<converter::c_floating_point T>
using T2S_Format_ldp = converter::T2S_Format_StreamDecimalPrecision<T, getLowerDecimalPrecision<T>() >;
template<converter::c_floating_point T>
using ConvertFromVal_lDP = converter::ConvertFromVal<T, T2S_Format_ldp<T> >;
There are four ways to handle error during string ↣ type conversion. This is achieved by enum FailureS2Tprocess
and template OnError
. Refer below.
enum FailureS2Tprocess { THROW_ERROR = 0, SIGNAL_NAN = 1, QUIET_NAN = 2, VARIANT_NAN = 3 };
template<typename T, FailureS2Tprocess PROCESS_ERR>
struct OnError;
Note: NaN means Not a Number
For usage examples refer testFloatingPointNaN.cpp and testIntegerNaN.cpp
- THROW_ERROR
This option throws an exception if one tries to convert non-numeric text to numeric data type, as it basically propagates the underlying conversion routine exceptions to the calling application.
THROW_ERROR is default behaviour for integer-types.
Both floating-point and integer type conversions support this option.
- SIGNAL_NAN
For floating-point types, with this option, the floating-point variable after conversion will have std::numeric_limits<T>::signaling_NaN()
as its value.
SIGNAL_NAN is default behaviour for floating-point numbers.
This is not applicable for integer types.
- QUIET_NAN
For floating-point types, with this option, the floating-point variable after conversion will have std::numeric_limits<T>::quite_NaN()
as its value.
This is applicable for floating-point numbers.
- VARIANT_NAN
Here instead of any type T
a variant type std::variant<T, std::string>
.
If the conversion is succes the vriant will of type T
, with the numeric-value that the string represents.
Else on conversion-error, the variant will of type std::string
, and value of the input-string that caused the error.
Both floating-point and integer type conversions support this option.
- For floating-point numbers
A NaN
value(both std::numeric_limits<T>::quiet_NaN()
and std::numeric_limits<T>::signaling_NaN()
) generally converts to a string value 'nan'.
Only exception is for Windows, where std::numeric_limits<T>::signaling_NaN()
will to convert to either of string values 'nan' or 'nan(snan)'.
- For values of type
std::variant<T, std::string>
(instead of typeT
)
By default, no convertion takes if data-type has error values, as it basically propagates the underlying conversion routines' exceptions to the calling application.
The reason for this is to ensure data correctness. If one wants to be able to identify variables with invalid values/numbers(for numeric data types), one can use the below class.
template< c_NOT_string T, typename T2S_FORMAT >
struct ConvertFromVal<std::variant<T, std::string>, T2S_FORMAT>
{
inline static std::string
ToStr(const std::variant<T, std::string>& val);
};
Above Template-specializaton can be used for all types(integer, floating, bool) to write the underlying valid-data(of type T
) or the string which raised the conversion-error.
- date-type
converter::format_year_month_day< const char* dateFMT, converter::FailureS2Tprocess PROCESS_ERR >
Use case for this type: For std::tuple<std::chrono::year_month_day,int,float,...>
conversion read-from string OR write-to string, the default conversion format for type std::chrono::year_month_day
is "%Y-%m-%d". If date-format "%d-%b-%Y" is needed, then read-from and write-to conversions would need different type-list as indicate in comments of code-block below. Also refer testTupleConversions.cpp for working code.
using t_tupleRowDD = std::tuple<std::chrono::year_month_day, // pure data type : 1/3
std::chrono::year_month_day>;
...
t_tupleRowDD convTupleDD =
converter::ConvertFromString< converter::ConvertFromStr_toDbY, // S2T-converter type : 2/3
std::chrono::year_month_day >::ToVal(rowDateStrInput);
std::string convTupleDD_toStr =
converter::ConvertFromTuple< converter::ConvertFromDbY_toStr, // T2S-converter type : 3/3
std::chrono::year_month_day >::ToStr(convTupleDD);
Instead same conversion-list across declarations and conversion is a better approach. See code below for recommended approach. (NOTE :: constexpr char dbY_fmt[] = "%d-%b-%Y";)
using t_fmtdbY = converter::format_year_month_day<converter::dbY_fmt, converter::FailureS2Tprocess::THROW_ERROR>;
#define TUPLE_TYPE_LIST t_fmtdbY, std::chrono::year_month_day
using t_tupleRowDD = std::tuple<TUPLE_TYPE_LIST>; // : same type list 1/3
...
t_tupleRowDD convTupleDD =
converter::ConvertFromString< TUPLE_TYPE_LIST >:: // : same type list 2/3
ToVal(rowDateStrInput);
std::string convTupleDD_toStr =
converter::ConvertFromTuple < TUPLE_TYPE_LIST >:: // : same type list 3/3
ToStr(convTupleDD);
For usage code, refer testDateYMD_format_dbY.cpp and testDateYMD_format_YMD.cpp
- Case-Insensitive String
ci_string
ci_string
type which does a case-insentive comparision. This is based on std::basic_string<...> with a different TypeTraits then that used by std::string
. Refer case_insensitive_string.h and testCIstring.cpp.
ConvertFromStr<T, S2T_FORMAT = S2T_DefaultFormat<T>::type>::ToVal(...)
function(s) convert string to T
data-type.
ConvertFromVal<T, T2S_FORMAT = T2S_DefaultFormat<T>::type>::ToStr(...)
function(s) convert T
data-type to string.
There are several specializations of ConvertFromStr
and ConvertFromVal
.
Each specialization is for certain types T
determined by concepts
and format type (S2T_FORMAT
or T2S_FORMAT
).
Format types S2T_FORMAT
and T2S_FORMAT
(if needed), provide additional information for formatting for types(such as Date
) or Locale
specific info/params or precision for floating-point types.
In addition if multiple conversion speicalization/algorithm is available for a particular type, the alternative specialization can be selected by passing the appropriate S2T_FORMAT
/T2S_FORMAT
. User could also define their own specialization of ConvertFromStr
or ConvertFromVal
(NOTE: new class type of S2T_FORMAT
or T2S_FORMAT
will also need to be defined).
- class converter::ConvertFromStr< CH, S2T_Format_WorkAround< CH, PROCESS_ERR > >
- class converter::ConvertFromStr< T, S2T_FORMAT_STREAM >
- class converter::ConvertFromStr< T, S2T_Format_std_CtoT< T, PROCESS_ERR > >
- class converter::ConvertFromStr< T, S2T_Format_std_StoT< T, PROCESS_ERR > >
- class converter::ConvertFromStr< bool, S2T_Format_WorkAround< bool, PROCESS_ERR > >
- class converter::ConvertFromStr< std::chrono::year_month_day, S2T_FORMAT_YMD >
- class converter::ConvertFromString< T_C >
- class converter::ConvertFromTuple< T_C >
- class converter::ConvertFromVal< CH, T2S_FORMAT_STREAM >
- class converter::ConvertFromVal< T, T2S_Format_WorkAround >
- class converter::ConvertFromVal< T, T2S_Format_std_TtoC >
- class converter::ConvertFromVal< T, T2S_Format_std_TtoS >
- class converter::ConvertFromVal< bool, T2S_Format_WorkAround >
- class converter::ConvertFromVal< std::chrono::year_month_day, T2S_FORMAT_YMD >
- class converter::format_year_month_day< dateFMT, PROCESS_ERR >
- class converter::ConvertFromVal< std::variant< T, std::string >, T2S_FORMAT >
- class converter::t_S2Tconv< T_C >
- class converter::t_T2Sconv< T_C >
Converter uses cmake for its tests. Commands to build and execute the test suite:
mkdir -p build && cd build
cmake -DCONVERTER_BUILD_TESTS=ON .. && cmake --build .
ctest -C unit --output-on-failure && ctest -C perf --verbose
cd -
Converter uses doxygenmd to generate its Markdown API documentation:
doxygenmd include/converter doc
Converter is distributed under the BSD 3-Clause license. See LICENSE file.
Bugs, PRs, etc are welcome on the GitHub project page https://github.com/panchaBhuta/converter/tree/master
c++, c++20, converter, string to type, type to string, single header library.