Skip to content

Instantly share code, notes, and snippets.

@DBJDBJ
Last active April 4, 2020 11:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DBJDBJ/064397449867b282123f4aba57ae58b3 to your computer and use it in GitHub Desktop.
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
#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_
#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_
@DBJDBJ
Copy link
Author

DBJDBJ commented Nov 28, 2019

New version. Works with C++20 too. char8_t is being taken care of. also, see the is_range_with_value_type_v...

@DBJDBJ
Copy link
Author

DBJDBJ commented Dec 8, 2019

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.

@DBJDBJ
Copy link
Author

DBJDBJ commented Dec 12, 2019

New Version works in C++20

@SlowPokeInTexas
Copy link

DBJDBJ, thank you for this; this is very handy!

@DBJDBJ
Copy link
Author

DBJDBJ commented Mar 12, 2020

Thanks @SlowPokeInTexas ...

@SlowPokeInTexas
Copy link

SlowPokeInTexas commented Mar 12, 2020 via email

@DBJDBJ
Copy link
Author

DBJDBJ commented Mar 15, 2020

Version 3.0.0

  1. Mechanism and testing are separated into two headers.
  2. All kinds of char ranges are being tested. Pointers to various char types too.
  3. 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