Last active
April 4, 2020 11:45
-
-
Save DBJDBJ/064397449867b282123f4aba57ae58b3 to your computer and use it in GitHub Desktop.
nicely and quickly transform any std range made of std chars into std string of your choice -- only ASCII part that is
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _DBJ_META_CONVERTER_INC_ | |
#define _DBJ_META_CONVERTER_INC_ | |
/* | |
(c) 2019/202 by dbj@dbj.org | |
LICENCE -- CC BY SA 4.0 -- https://creativecommons.org/licenses/by-sa/4.0/ | |
This tool transforms any kind of char range into any required string kind. | |
By kind we mean "made of particular char type" | |
See the comprehensive testing in a separate header also here. | |
NOTE: be forewarned: in C++20 char8_t is actually crippled. | |
*/ | |
#include <type_traits> | |
#include <string> | |
namespace dbj { | |
enum class dbj_meta_converter_version { | |
major = 3, minor = 0, patch = 0 | |
}; | |
constexpr char const* dbj_meta_converter_version_string = "3.0.0 " __TIMESTAMP__; | |
namespace typetraits { | |
using namespace std; | |
template <typename T> struct remove_all_ptr { typedef T type; }; | |
template <typename T> struct remove_all_ptr<T*> { | |
using type = typename remove_all_ptr<::std::remove_cv_t<T>>::type; | |
}; | |
// reduce T***** to T, for any level of pointers to pointers | |
template <typename T> | |
using remove_all_ptr_t = typename remove_all_ptr<T>::type; | |
template< class T > | |
struct remove_cvref { | |
typedef ::std::remove_cv_t<::std::remove_reference_t<T>> type; | |
}; | |
template< class T > | |
using remove_cvref_t = typename remove_cvref<T>::type; | |
// reduce any compound type , to its base type | |
template <class T> | |
using to_base_t = | |
remove_all_ptr_t< ::std::remove_all_extents_t< remove_cvref_t < T > > >; | |
// dbj.org 2018-07-03 | |
// NOTE: pointers are not char's | |
// char *. wchar_t * .. are thus not chars | |
// take care of chars and their signed and unsigned forms | |
// where 'char' means one of the four ::std char types | |
// NOTE: u8string we will skip for now | |
// as it stands at 2019Q4 "char8_t is a mess" in C++20 | |
template<class _Ty> struct is_char : ::std::false_type { }; | |
template<> struct is_char<char> : ::std::true_type { }; | |
template<> struct is_char<signed char> : ::std::true_type { }; | |
template<> struct is_char<unsigned char> : ::std::true_type { }; | |
template<typename T> inline constexpr bool is_char_v = is_char<T>::value; | |
template<class _Ty> struct is_wchar : ::std::false_type { }; | |
template<> struct is_wchar<wchar_t> : ::std::true_type { }; | |
template<typename T> inline constexpr bool is_wchar_v = is_wchar<T>::value; | |
template<class _Ty> struct is_char16 : ::std::false_type { }; | |
template<> struct is_char16<char16_t> : ::std::true_type { }; | |
template<typename T> inline constexpr bool is_char16_v = is_char16<T>::value; | |
template<class _Ty> struct is_char32 : ::std::false_type { }; | |
template<> struct is_char32<char32_t> : ::std::true_type { }; | |
template<typename T> inline constexpr bool is_char32_v = is_char32<T>::value; | |
#if __cplusplus > 201703L | |
// C++20 code | |
template<class _Ty> struct is_char8 : ::std::false_type { }; | |
template<> struct is_char8<char8_t> : ::std::true_type { }; | |
template<typename T> inline constexpr bool is_char8_v = is_char8<T>::value; | |
#endif | |
/************************************************************************************/ | |
// and one for all | |
template<typename T, typename base_t = to_base_t<T> > | |
struct is_std_char : | |
::std::integral_constant | |
< | |
bool, | |
is_char_v< base_t > || is_wchar_v<base_t> || | |
is_char16_v<base_t> || is_char32_v<base_t> | |
#if __cplusplus > 201703L | |
|| is_char8_v<base_t> | |
#endif | |
> | |
{}; | |
template<typename T > | |
inline constexpr bool is_std_char_v = is_std_char<T>::value; | |
// is T, a standard string | |
template< class T > | |
struct is_std_string : integral_constant < bool, | |
is_same_v<T, string > || | |
is_same_v<T, wstring > || | |
is_same_v<T, u16string > || | |
is_same_v<T, u32string > | |
#if __cplusplus > 201703L | |
|| is_same_v<T, u8string > | |
#endif | |
> | |
{}; | |
template<typename T> | |
inline constexpr bool is_std_string_v = is_std_string<T>::value; | |
///<summary> | |
/// as per official definition circa 2019Q4 | |
/// range is anything that has | |
/// begin() and end() methods | |
/// But. We add yet another pre condition | |
/// T::value_type must be present | |
/// and then we give it a long and descriptive name | |
/// so that users do not use it wrongly | |
///</summary> | |
template <typename T, typename = void> | |
struct is_range_with_value_type final : ::std::false_type {}; | |
template <typename T> | |
struct is_range_with_value_type <T | |
, ::std::void_t | |
< | |
decltype(::std::declval<T>().begin()), | |
decltype(::std::declval<T>().end()), | |
typename T::value_type | |
> | |
> final : ::std::true_type{}; | |
template<typename T> | |
constexpr inline bool is_range_with_value_type_v = is_range_with_value_type<T>::value; | |
} // typetraits | |
namespace inner { | |
/// <summary> | |
/// Try to convert any range made of standard char types | |
/// return type is one of std strings | |
/// range is anything that has begin() and end(), and | |
/// value_type typedef as per std containers model | |
/// </summary> | |
template < typename return_type > | |
struct meta_converter final | |
{ | |
static_assert(typetraits::is_std_string_v<return_type>, "return type must be standard string type"); | |
template<typename T> | |
return_type operator () (T arg) const | |
{ | |
using namespace typetraits; | |
using actual_type = remove_cvref_t< T >; | |
if constexpr (std::is_same_v<actual_type, return_type >) { | |
return arg; // copy to output | |
} | |
else { | |
static_assert(is_range_with_value_type_v<actual_type>, | |
"can transform only ranges also having a T::value_type accessible"); | |
static_assert ( | |
// arg must have nested value_type | |
is_std_char_v< typename actual_type::value_type >, | |
"can not transform ranges **not** made out of standard char types" | |
); | |
#ifndef _MSC_VER | |
return { arg.begin(), arg.end() }; | |
#else | |
// currently a lot of warnings C4244 | |
// for MSVC, as of 2019-11-27 | |
#pragma warning( push ) | |
#pragma warning ( disable: 4244 ) | |
return return_type(arg.begin(), arg.end()); | |
#pragma warning( pop ) | |
#endif | |
} | |
} | |
// native pointers are now allowed | |
// thus this is comfortable API for everyone | |
// WARNING: zero terminated strings | |
// as char pointers | |
// are a safety risk | |
template<typename CHR > | |
return_type operator () (CHR* sv_) const | |
{ | |
using actual_type | |
= std::remove_cv_t< std::remove_pointer_t<CHR> >; | |
static_assert ( | |
typetraits::is_std_char_v< actual_type >, | |
"can transform only sequences made out of standard char types" | |
); | |
// make the string and send it to | |
// the range transformation method | |
return this->operator()( | |
std::basic_string<actual_type>{sv_} | |
); | |
} | |
}; // meta_converter | |
// explicit instantiations | |
template struct meta_converter<std::string >; | |
template struct meta_converter<std::wstring >; | |
template struct meta_converter<std::u16string>; | |
template struct meta_converter<std::u32string>; | |
#if __cplusplus > 201703L | |
// C++20 | |
template struct meta_converter<std::u8string>; | |
#endif | |
} // inner | |
// implicit instantiations | |
inline dbj::inner::meta_converter<std::string > range_to_string{}; | |
inline dbj::inner::meta_converter<std::wstring > range_to_wstring{}; | |
inline dbj::inner::meta_converter<std::u16string> range_to_u16string{}; | |
inline dbj::inner::meta_converter<std::u32string> range_to_u32string{}; | |
#if __cplusplus > 201703L | |
// C++20 | |
inline dbj::inner::meta_converter<std::u8string> range_to_u8string{}; | |
#endif | |
} // dbj | |
#endif // !_DBJ_META_CONVERTER_INC_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _DBJ_META_CONVERTER_TESTING_INC_ | |
#define _DBJ_META_CONVERTER_TESTING_INC_ | |
/* | |
(c) 2020 by dbj@dbj.org | |
LICENCE -- CC BY SA 4.0 -- https://creativecommons.org/licenses/by-sa/4.0/ | |
*/ | |
#include <deque> | |
#include <forward_list> | |
#include <string> | |
#include <array> | |
#include <vector> | |
#include <iomanip> | |
#include <sstream> | |
#include "dbj_meta_converter.h" | |
namespace meta_conversion_testing { | |
using namespace std; | |
using namespace std::string_literals; | |
/// the string meta converter testing | |
template<typename converter_type> | |
inline string test_conversion(converter_type&& the_converter) | |
{ | |
std::ostringstream rezult_{}; | |
// just show the type of the result ... | |
#define TEST_(x) do { \ | |
auto const & rez_of_x_ = (x); \ | |
rezult_ << "\n" << _CRT_STRINGIZE(x) << " -- result type: " << typeid(rez_of_x_).name() ; \ | |
} while(false) | |
// standard string literals | |
TEST_(the_converter("the\0\0standard string literal"s)); | |
TEST_(the_converter(L"the\0\0standard string literal"s)); | |
TEST_(the_converter(u"the\0\0standard string literal"s)); | |
TEST_(the_converter(U"the\0\0standard string literal"s)); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(u8"the\0\0standard string literal"s)); | |
#endif | |
// native string literals | |
TEST_(the_converter("Abra Ca Dabra Alhambra")); | |
TEST_(the_converter(L"Abra Ca Dabra Alhambra")); | |
TEST_(the_converter(u"Abra Ca Dabra Alhambra")); | |
TEST_(the_converter(U"Abra Ca Dabra Alhambra")); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(u8"Abra Ca Dabra Alhambra")); | |
#endif | |
// the five C++20 standard string types | |
TEST_(the_converter(std::string{ "Abra Ca Dabra Alhambra" })); | |
TEST_(the_converter(std::wstring{ L"Abra Ca Dabra Alhambra" })); | |
TEST_(the_converter(std::u16string{ u"Abra Ca Dabra Alhambra" })); | |
TEST_(the_converter(std::u32string{ U"Abra Ca Dabra Alhambra" })); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(std::u8string{ u8"Abra Ca Dabra Alhambra" })); | |
#endif | |
// now let's try the range-like containers | |
// in case you have missed it we can transform any range of chars | |
// into any required std string type | |
TEST_(the_converter(std::array{ 'H', 'e', 'l', 'l', 'o', '!' })); | |
TEST_(the_converter(std::array{ L'H', L'e', L'l', L'l', L'o', L'!' })); | |
TEST_(the_converter(std::array{ u'H', u'e', u'l', u'l', u'o', u'!' })); | |
TEST_(the_converter(std::array{ U'H', U'e', U'l', U'l', U'o', U'!' })); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(std::array{ u8'H', u8'e', u8'l', u8'l', u8'o', u8'!' })); | |
#endif | |
TEST_(the_converter(std::vector{ 'H', 'e', 'l', 'l', 'o', '!' })); | |
TEST_(the_converter(std::vector{ L'H', L'e', L'l', L'l', L'o', L'!' })); | |
TEST_(the_converter(std::vector{ u'H', u'e', u'l', u'l', u'o', u'!' })); | |
TEST_(the_converter(std::vector{ U'H', U'e', U'l', U'l', U'o', U'!' })); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(std::vector{ u8'H', u8'e', u8'l', u8'l', u8'o', u8'!' })); | |
#endif | |
TEST_(the_converter(std::deque{ 'H', 'e', 'l', 'l', 'o', '!' })); | |
TEST_(the_converter(std::deque{ L'H', L'e', L'l', L'l', L'o', L'!' })); | |
TEST_(the_converter(std::deque{ u'H', u'e', u'l', u'l', u'o', u'!' })); | |
TEST_(the_converter(std::deque{ U'H', U'e', U'l', U'l', U'o', U'!' })); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(std::deque{ u8'H', u8'e', u8'l', u8'l', u8'o', u8'!' })); | |
#endif | |
TEST_(the_converter(std::forward_list{ 'H', 'e', 'l', 'l', 'o', '!' })); | |
TEST_(the_converter(std::forward_list{ L'H', L'e', L'l', L'l', L'o', L'!' })); | |
TEST_(the_converter(std::forward_list{ u'H', u'e', u'l', u'l', u'o', u'!' })); | |
TEST_(the_converter(std::forward_list{ U'H', U'e', U'l', U'l', U'o', U'!' })); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(std::forward_list{ u8'H', u8'e', u8'l', u8'l', u8'o', u8'!' })); | |
#endif | |
// this can do | |
char const * char_const_ptr = "char const *"; | |
TEST_(the_converter(char_const_ptr)); | |
// this can do | |
TEST_(the_converter( "string literal")); | |
TEST_(the_converter(L"string literal")); | |
TEST_(the_converter(U"string literal")); | |
TEST_(the_converter(u"string literal")); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(u8"string literal")); | |
#endif | |
// this can do | |
TEST_(the_converter( "string literal"sv)); | |
TEST_(the_converter(L"string literal"sv)); | |
TEST_(the_converter(U"string literal"sv)); | |
TEST_(the_converter(u"string literal"sv)); | |
#if __cplusplus > 201703L | |
TEST_(the_converter(u8"string literal"sv)); | |
#endif | |
#undef TEST_ | |
return rezult_.str(); | |
} | |
/// | |
/// log callback signature: void (*)( const char * ) | |
/// log callback behaviour: each call means flush, after the output is done | |
/// | |
template<typename log_callback > | |
inline void metaconvertertest(log_callback log) { | |
log( test_conversion(dbj::range_to_string).c_str()); | |
log( test_conversion(dbj::range_to_wstring).c_str()); | |
log( test_conversion(dbj::range_to_u16string).c_str()); | |
log( test_conversion(dbj::range_to_u32string).c_str()); | |
#if __cplusplus > 201703L | |
log( test_conversion(dbj::range_to_u8string).c_str()); | |
#endif | |
} | |
} // meta_conversion_testing | |
#endif // DBJ_META_CONVERTER_TESTING_INC_ |
New version. Takes char pointers too, not just references to native arrays as the previous version did.
A safety risk. But this is what users want.
New Version works in C++20
DBJDBJ, thank you for this; this is very handy!
Thanks @SlowPokeInTexas ...
It really is incredibly useful. I include it almost by default on any
internal projects we use. Kudos!
Hope it gets more traction.
…On Thu, Mar 12, 2020 at 11:25 AM dbj ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
Thanks @SlowPokeInTexas <https://github.com/SlowPokeInTexas> ...
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://gist.github.com/064397449867b282123f4aba57ae58b3#gistcomment-3209868>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEVGLWA3ANWIDJIR4OC32S3RHEEIHANCNFSM4JSWKI7Q>
.
--
"The best time to plant a tree is 20 years ago. The second best time is
today."
Version 3.0.0
- Mechanism and testing are separated into two headers.
- All kinds of char ranges are being tested. Pointers to various char types too.
- Testing output is to the log callback:
void (*) ( const char * )
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
New version. Works with C++20 too.
char8_t
is being taken care of. also, see theis_range_with_value_type_v
...