開発中のソースを現在貼り付けて公開中・・・。
総じて、v1系とのソース互換の向上を目指した。
- Windows/Linux環境下で、strconv.h (v1系) であった全ての文字コード変換関数をサポート。
(ansi_to_~()、~_to_ansi() を除く) -
strconv.h (v1系) であった formatA()関数は廃止、format()関数がインテリジェントになった。chcpに関係なく韓国語等のユニコード文字を出力できる。formatA() を復活させた。 - strconv.h (v1系) で
unicode_ostream uout(std::cout, GetConsoleOutputCP());
と指定していたのをマルチプラットフォーム対応のため、。ダミーの第二引数を受け付けるようにして、Linux環境では GetConsoleOutputCP() および GetConsoleCP() が (0) に展開されるようなマクロを用意して v1系とのソース互換を目指した。unicode_ostream uout(std::cout);
とした(コードページを指定する第二引数を廃止) - wchar_t * または std::wstring を直接出力できない仕様とした。utf8(std::string)またはchar8(std::u8string)に変換してから出力する。
- utf8/char8 と char16(std::u16string)/char32(std::u32string) との間の変換関数を用意した。
- unicode_ostream が char16(char16_t *, std::u16string) および char32(char32_t *, std::u32string) を受け付けるようにした。
デモソースコード
#include "strconvxp.h"
#include <iomanip>
#include <cmath>
using namespace std;
class CMyClass
{
time_t t;
int count;
public:
CMyClass()
{
this->t = std::time(nullptr);
this->count = 0;
}
int CountUp()
{
return ++this->count;
}
friend unicode_ostream &operator<<(unicode_ostream &stream, const CMyClass &value);
};
unicode_ostream &operator<<(unicode_ostream &stream, const CMyClass &value)
{
struct tm *ptm = std::localtime(&value.t);
stream << u8"[作成日時=" << std::put_time(ptm, "%Y-%m-%d %H:%M:%S");
stream << u8"、カウント=" << value.count << "]";
return stream;
};
int main(int argc, char *argv[])
{
// demo 1
std::string utf8_str = U8("あいう");
format(cout, "utf8_str.length()=%u\n", utf8_str.length());
std::wstring wide_str = utf8_to_wide(utf8_str);
format(cout, "wide_str.length()=%u\n", wide_str.length());
std::string sjis_str = wide_to_sjis(wide_str);
format(cout, "sjis_str.length()=%u\n", sjis_str.length());
// demo 2
std::string nameUtf8 = U8("太郎");
int age = 15;
format(std::cout, u8"ハロー、私の名前は %s。 年は %d だ!\n", nameUtf8.c_str(), age);
format(std::cout, u8"漢字=한자\n");
// demo 3
unicode_ostream uout(cout);
CMyClass mc;
mc.CountUp();
mc.CountUp();
uout << mc << endl;
double pi = 4 * atan(1.0);
uout << u8"π(1)=" << pi << endl;
uout << u8"π(2)=" << format("%.2f", pi) << endl;
uout << 1 << u8" char*漢字=한자 " << std::string(U8(" string漢字=한자 ")) << 1.2345 << endl;
double A = 100;
double B = 2001.5251;
// 書式指定(A) hex の代わりに setbase(16) を使うこともできます
uout << hex << left << showbase << nouppercase;
// 実際の印字処理(A)
uout << (long long)A << endl;
// 書式指定(B) setbase(10) の代わりに dec を使うこともできます
uout << setbase(10) << right << setw(15)
<< setfill('_') << showpos
<< fixed << setprecision(2);
// 実際の印字処理(B)
uout << B << endl;
// demo4 (v1系との互換性)
//#define formatA format
formatA(std::cout, u8"ハロー、私の名前は %s。 年は %d だ!\n", nameUtf8.c_str(), age);
//#define GetConsoleOutputCP() (0)
unicode_ostream uout2(cout, GetConsoleOutputCP());
uout2 << "GetConsoleOutputCP()マクロを用意してダミー対応。" << endl;
//#define GetConsoleCP() (0)
unicode_ostream uout3(cout, GetConsoleCP());
uout2 << "GetConsoleCP()マクロを用意してダミー対応。" << endl;
// demo5
uout << u"ゆーてぃーえふ16" << endl;
uout << U"ゆーてぃーえふ32" << endl;
std::string s = U8("abc");
std::vector<unsigned int> v = {
437,//OEM 米国
737,//ギリシャ語
775,//バルト言語
850,//西ヨーロッパ言語
852,//中央ヨーロッパ言語
855,//OEM キリル
857,//トルコ語
858,//OEM マルチリンガル ラテン I
860,//ポルトガル語
861,//アイスランド語
862,//ヘブライ語
863,//フランス語 (カナダ)
864,//アラビア語
865,//北欧
866,//キリル言語
869,//ギリシャ語, Modern
874,//タイ語
932,//日本語 (シフト JIS)
936,//簡体字中国語 (GB2312)
949,//韓国語
950,//繁体字中国語 (Big5)
1250,//中央ヨーロッパ言語
1251,//キリル言語
1252,//西ヨーロッパ言語
1253,//ギリシャ語
1254,//トルコ語
1255,//ヘブライ語
1256,//アラビア語
1257,//バルト言語
1258,//ベトナム語
1361,//韓国語 (Johab)
65001,//Unicode (UTF-8)
};
int error_count = 0;
for (std::size_t i=0; i<v.size(); i++)
{
std::string s2 = utf8_to_cp(s, v[i]);
if(s2 == "abc") continue;
uout << "ERROR: " << v[i] << ":" << utf8_to_cp(s, v[i]) << std::endl;
error_count++;
}
format(std::cout, u8"変換エラー: %d個\n", error_count);
return 0;
}
strconvxp.h
/* strconvxp.h v2.0.8 */
/* Last Modified: 2022/05/20 02:17 */
#ifndef STRCONVXP_H
#define STRCONVXP_H
#if defined(_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <stdarg.h>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <codecvt>
#include <locale>
#if !defined(_WIN32) || defined(STRCONV_USE_ICONV)
#include <iconv.h>
template <typename X, typename Y>
static inline std::basic_string<X> iconv_translit(
std::string to_code,
std::string from_code,
const std::basic_string<Y> &from_str)
{
#if defined(_WIN32)
to_code += "//IGNORE";
#else
to_code += "//TRANSLIT";
#endif
void *from_begin = (void *)from_str.data();
void *from_end = (void *)(from_str.data() + from_str.size());
size_t buff_len = ((char *)from_end) - ((char *)from_begin);
iconv_t icd = iconv_open(to_code.c_str(), from_code.c_str());
if (icd == ((iconv_t) -1))
{
std::basic_string<X> empty;
return empty;
}
while (true)
{
std::vector<char> buff(buff_len + 1);
char *src_top = (char *)from_begin;
size_t src_len = ((char *)from_end) - ((char *)from_begin);
char *dest_top = &buff[0];
size_t dest_len = buff_len;
size_t ret = iconv(icd, &src_top, &src_len, &dest_top, &dest_len);
if (ret == ((size_t) -1))
{
if (errno == E2BIG)
{
buff_len *= 2;
continue;
}
else
{
//std::cerr << errno << std::endl;
//iconv_close(icd);
//std::basic_string<X> empty;
//return empty;
}
}
iconv_close(icd);
buff.resize(buff_len - dest_len);
std::basic_string<X> result((X *)buff.data(), (X *)(buff.data() + buff.size()));
return result;
}
}
#endif // !defined(_WIN32) || defined(STRCONV_USE_ICONV)
#if defined(_WIN32) && !defined(STRCONV_USE_ICONV)
static inline std::wstring cp_to_wide(const std::string &s, unsigned int codepage)
{
int in_length = (int)s.length();
int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0);
std::wstring result(out_length, L'\0');
if (out_length)
MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &result[0], out_length);
return result;
}
static inline std::string wide_to_cp(const std::wstring &s, unsigned int codepage)
{
int in_length = (int)s.length();
int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0);
std::string result(out_length, '\0');
if (out_length)
WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &result[0], out_length, 0, 0);
return result;
}
#else // !defined(_WIN32)
static inline std::wstring cp_to_wide(const std::string &s, unsigned int codepage)
{
switch(codepage)
{
case 65001:
return iconv_translit<wchar_t>("WCHAR_T", "UTF-8", s);
default:
std::string from_code = "CP" + std::to_string(codepage);
return iconv_translit<wchar_t>("WCHAR_T", from_code, s);
}
}
static inline std::string wide_to_cp(const std::wstring &s, unsigned int codepage)
{
switch(codepage)
{
case 65001:
return iconv_translit<char>("UTF-8", "WCHAR_T", s);
default:
std::string to_code = "CP" + std::to_string(codepage);
return iconv_translit<char>(to_code, "WCHAR_T", s);
}
}
#endif // _WIN32
static inline std::string cp_to_utf8(const std::string &s, unsigned int codepage)
{
if (codepage == 65001)
return s;
std::wstring wide = cp_to_wide(s, codepage);
return wide_to_cp(wide, 65001);
}
static inline std::string utf8_to_cp(const std::string &s, unsigned int codepage)
{
if (codepage == 65001)
return s;
std::wstring wide = cp_to_wide(s, 65001);
return wide_to_cp(wide, codepage);
}
static inline std::wstring ansi_to_wide(const std::string &s)
{
#if defined(_WIN32)
return cp_to_wide(s, CP_ACP);
#else
return cp_to_wide(s, 65001);
#endif
}
static inline std::string wide_to_ansi(const std::wstring &s)
{
#if defined(_WIN32)
return wide_to_cp(s, CP_ACP);
#else
return wide_to_cp(s, 65001);
#endif
}
static inline std::wstring sjis_to_wide(const std::string &s)
{
return cp_to_wide(s, 932);
}
static inline std::string wide_to_sjis(const std::wstring &s)
{
return wide_to_cp(s, 932);
}
static inline std::string ansi_to_utf8(const std::string &s)
{
#if defined(_WIN32)
return cp_to_utf8(s, CP_ACP);
#else
//return cp_to_utf8(s, 65001);
return s;
#endif
}
static inline std::string utf8_to_ansi(const std::string &s)
{
#if defined(_WIN32)
return utf8_to_cp(s, CP_ACP);
#else
//return utf8_to_cp(s, 65001);
return s;
#endif
}
static inline std::string sjis_to_utf8(const std::string &s)
{
return cp_to_utf8(s, 932);
}
static inline std::string utf8_to_sjis(const std::string &s)
{
return utf8_to_cp(s, 932);
}
static inline std::wstring utf8_to_wide(const std::string &s)
{
return cp_to_wide(s, 65001);
}
static inline std::string wide_to_utf8(const std::wstring &s)
{
return wide_to_cp(s, 65001);
}
#ifdef __cpp_char8_t
static inline std::u8string utf8_to_char8(const std::string &s)
{
return std::u8string(s.begin(), s.end());
}
static inline std::string char8_to_utf8(const std::u8string &s)
{
return std::string(s.begin(), s.end());
}
static inline std::wstring char8_to_wide(const std::u8string &s)
{
return cp_to_wide(char8_to_utf8(s), 65001);
}
static inline std::u8string wide_to_char8(const std::wstring &s)
{
return utf8_to_char8(wide_to_cp(s, 65001));
}
static inline std::u8string cp_to_char8(const std::string &s, unsigned int codepage)
{
return utf8_to_char8(cp_to_utf8(s, codepage));
}
static inline std::string char8_to_cp(const std::u8string &s, unsigned int codepage)
{
return utf8_to_cp(char8_to_utf8(s), codepage);
}
static inline std::u8string ansi_to_char8(const std::string &s)
{
#if defined(_WIN32)
return cp_to_char8(s, CP_ACP);
#else
//return cp_to_char8(s, 65001);
return utf8_to_char8(s);
#endif
}
static inline std::string char8_to_ansi(const std::u8string &s)
{
#if defined(_WIN32)
return char8_to_cp(s, CP_ACP);
#else
//return char8_to_cp(s, 65001);
return char8_to_utf8(s);
#endif
}
static inline std::u8string sjis_to_char8(const std::string &s)
{
return cp_to_char8(s, 932);
}
static inline std::string char8_to_sjis(const std::u8string &s)
{
return char8_to_cp(s, 932);
}
#endif
static inline std::string char16_to_utf8(const std::u16string &s)
{
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> convert;
return convert.to_bytes(s);
}
static inline std::u16string utf8_to_char16(const std::string &s)
{
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> convert;
return convert.from_bytes(s);
}
static inline std::string char16_to_cp(const std::u16string &s, unsigned int codepage)
{
return utf8_to_cp(char16_to_utf8(s), codepage);
}
static inline std::string char32_to_utf8(const std::u32string &s)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return convert.to_bytes(s);
}
static inline std::u32string utf8_to_char32(const std::string &s)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return convert.from_bytes(s);
}
static inline std::string char32_to_cp(const std::u32string &s, unsigned int codepage)
{
return utf8_to_cp(char32_to_utf8(s), codepage);
}
#ifdef __cpp_char8_t
static inline std::u8string char16_to_char8(const std::u16string &s)
{
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> convert;
return utf8_to_char8(convert.to_bytes(s));
}
static inline std::u16string char8_to_char16(const std::u8string &s)
{
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> convert;
return convert.from_bytes(char8_to_utf8(s));
}
static inline std::u8string char32_to_char8(const std::u32string &s)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return utf8_to_char8(convert.to_bytes(s));
}
static inline std::u32string char8_to_char32(const std::u8string &s)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return convert.from_bytes(char8_to_utf8(s));
}
#endif
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
static inline std::string vformat(const char *format, va_list args)
{
va_list args_copy;
va_copy(args_copy, args);
int len = vsnprintf(0, 0, format, args);
if (len < 0)
{
va_end(args_copy);
return "";
}
std::vector<char> buffer(len + 1);
len = vsnprintf(&buffer[0], len + 1, format, args_copy);
va_end(args_copy);
if (len < 0)
{
return "";
}
buffer[len] = '\0';
return &buffer[0];
}
#ifdef __cpp_char8_t
static inline std::u8string vformat(const char8_t *format, va_list args)
{
va_list args_copy;
va_copy(args_copy, args);
int len = vsnprintf(0, 0, (const char *)format, args);
if (len < 0)
{
va_end(args_copy);
return u8"";
}
std::vector<char> buffer(len + 1);
len = vsnprintf(&buffer[0], len + 1, (const char *)format, args_copy);
va_end(args_copy);
if (len < 0)
{
return u8"";
}
buffer[len] = '\0';
return (char8_t *)&buffer[0];
}
#endif
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
static inline std::string format(const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
return s;
}
#ifdef __cpp_char8_t
static inline std::u8string format(const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
return s;
}
#endif
#if defined(_WIN32)
static inline HANDLE handle_for_ostream(std::ostream &ostrm)
{
if (&ostrm == &std::cout)
{
return GetStdHandle(STD_OUTPUT_HANDLE);
}
else if (&ostrm == &std::cerr)
{
return GetStdHandle(STD_ERROR_HANDLE);
}
return INVALID_HANDLE_VALUE;
}
template <typename X>
static inline void output_to_ostream(std::ostream &ostrm, const std::basic_string<X> &s, unsigned int target_cp)
{
static_assert(sizeof(X)==1);
HANDLE h = handle_for_ostream(ostrm);
if (h == INVALID_HANDLE_VALUE)
{
ostrm << utf8_to_cp((const char *)s.c_str(), target_cp==((unsigned int) -1) ? 65001 : target_cp) << std::flush;
}
DWORD dwNumberOfCharsWrite;
if (GetFileType(h) != FILE_TYPE_CHAR)
{
std::string s2 = utf8_to_cp((const char *)s.c_str(), target_cp==((unsigned int) -1) ? GetConsoleOutputCP() : target_cp);
WriteFile(h, s2.c_str(), (DWORD)s2.size(), &dwNumberOfCharsWrite, NULL);
}
else
{
std::wstring ws = utf8_to_wide((const char *)s.c_str());
WriteConsoleW(h,
ws.c_str(),
(DWORD)ws.size(),
&dwNumberOfCharsWrite,
NULL);
}
}
static inline void format(std::ostream &ostrm, const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
output_to_ostream(ostrm, s, ((unsigned int) -1));
}
#ifdef __cpp_char8_t
static inline void format(std::ostream &ostrm, const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
output_to_ostream(ostrm, s, ((unsigned int) -1));
}
#endif
#else // !defined(_WIN32)
static inline void format(std::ostream &ostrm, const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
ostrm << s << std::flush;
}
#ifdef __cpp_char8_t
static inline void format(std::ostream &ostrm, const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
ostrm << char8_to_utf8(s) << std::flush;
}
#endif
#endif // _WIN32
static inline std::string formatA(const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
#if defined(_WIN32)
return utf8_to_ansi(s);
#else
return s;
#endif
}
#ifdef __cpp_char8_t
static inline std::string formatA(const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
#if defined(_WIN32)
return char8_to_ansi(s);
#else
return char8_to_utf8(s);
#endif
}
#endif
#if defined(_WIN32)
static inline void formatA(std::ostream &ostrm, const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
output_to_ostream(ostrm, s, CP_ACP);
}
#ifdef __cpp_char8_t
static inline void formatA(std::ostream &ostrm, const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
output_to_ostream(ostrm, s, CP_ACP);
}
#endif
#else // !defined(_WIN32)
static inline void formatA(std::ostream &ostrm, const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
ostrm << s << std::flush;
}
#ifdef __cpp_char8_t
static inline void formatA(std::ostream &ostrm, const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
ostrm << char8_to_utf8(s) << std::flush;
}
#endif
#endif // _WIN32
static inline std::string formatUtf8(const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
return s;
}
#ifdef __cpp_char8_t
static inline std::string formatUtf8(const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
return char8_to_utf8(s);
}
#endif
#if defined(_WIN32)
static inline void formatUtf8(std::ostream &ostrm, const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
output_to_ostream(ostrm, s, 65001);
}
#ifdef __cpp_char8_t
static inline void formatUtf8(std::ostream &ostrm, const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
output_to_ostream(ostrm, s, 65001);
}
#endif
#else // !defined(_WIN32)
static inline void formatUtf8(std::ostream &ostrm, const char *format, ...)
{
va_list args;
va_start(args, format);
std::string s = vformat(format, args);
va_end(args);
ostrm << s << std::flush;
}
#ifdef __cpp_char8_t
static inline void formatUtf8(std::ostream &ostrm, const char8_t *format, ...)
{
va_list args;
va_start(args, format);
std::u8string s = vformat(format, args);
va_end(args);
ostrm << char8_to_utf8(s) << std::flush;
}
#endif
#endif // _WIN32
class unicode_ostream
{
private:
std::ostream *m_ostrm;
#if defined(_WIN32)
unsigned int m_target_cp;
bool is_ascii(const std::string &s)
{
for (std::size_t i = 0; i < s.size(); i++)
{
unsigned char c = (unsigned char)s[i];
if (c > 0x7f)
return false;
}
return true;
}
#endif
public:
unicode_ostream(std::ostream &ostrm, unsigned int dummy = 0) : m_ostrm(&ostrm)
{
(void)dummy;
#if defined(_WIN32)
m_target_cp = GetConsoleOutputCP();
#endif
}
template <typename T>
unicode_ostream &operator<<(const T &x)
{
#if defined(_WIN32)
std::ostringstream oss;
oss << x;
std::string output = oss.str();
if (is_ascii(output))
{
(*m_ostrm) << x;
}
else
{
(*m_ostrm) << utf8_to_cp(output, m_target_cp);
}
#else
(*m_ostrm) << x;
#endif
return *this;
}
unicode_ostream &operator<<(const std::string &x)
{
#if defined(_WIN32)
(*m_ostrm) << utf8_to_cp(x, m_target_cp);
#else
(*m_ostrm) << x;
#endif
return *this;
}
unicode_ostream &operator<<(const char *x)
{
#if defined(_WIN32)
(*m_ostrm) << utf8_to_cp(x, m_target_cp);
#else
(*m_ostrm) << x;
#endif
return *this;
}
#ifdef __cpp_char8_t
unicode_ostream &operator<<(const std::u8string &x)
{
#if defined(_WIN32)
(*m_ostrm) << char8_to_cp(x, m_target_cp);
#else
(*m_ostrm) << char8_to_utf8(x);
#endif
return *this;
}
unicode_ostream &operator<<(const char8_t *x)
{
#if defined(_WIN32)
(*m_ostrm) << char8_to_cp(x, m_target_cp);
#else
(*m_ostrm) << char8_to_utf8(x);
#endif
return *this;
}
#endif
unicode_ostream &operator<<(const std::u16string &x)
{
#if defined(_WIN32)
(*m_ostrm) << char16_to_cp(x, m_target_cp);
#else
(*m_ostrm) << char16_to_utf8(x);
#endif
return *this;
}
unicode_ostream &operator<<(const char16_t *x)
{
#if defined(_WIN32)
(*m_ostrm) << char16_to_cp(x, m_target_cp);
#else
(*m_ostrm) << char16_to_utf8(x);
#endif
return *this;
}
unicode_ostream &operator<<(const std::u32string &x)
{
#if defined(_WIN32)
(*m_ostrm) << char32_to_cp(x, m_target_cp);
#else
(*m_ostrm) << char32_to_utf8(x);
#endif
return *this;
}
unicode_ostream &operator<<(const char32_t *x)
{
#if defined(_WIN32)
(*m_ostrm) << char32_to_cp(x, m_target_cp);
#else
(*m_ostrm) << char32_to_utf8(x);
#endif
return *this;
}
unicode_ostream &operator<<(std::ostream &(*pf)(std::ostream &)) // For manipulators...
{
(*m_ostrm) << pf;
return *this;
}
unicode_ostream &operator<<(std::basic_ios<char> &(*pf)(std::basic_ios<char> &)) // For manipulators...
{
(*m_ostrm) << pf;
return *this;
}
};
#if !defined(_WIN32)
#define GetConsoleCP() (0)
#define GetConsoleOutputCP() (0)
#endif
#define U8(X) ((const char *)u8##X)
#define WIDE(X) (L##X)
#endif /* STRCONVXP_H */