operator<<
を並べたくない
<<
に慣れればいいのでしょうけど
これを
#include <iostream>
int main(int argc, char** argv)
{
std::cout << "main(argc=" << argc << ", argv=" << argv << ")" << std::endl;
}
std::initializer_list
を使えば
こうしたい
std::cout << o { "main(argc=", argc, ", argv=", argv, ")", std::endl };
が出来そうな気がしたので作ってみた。
単純処理版
{ ... }
が一つの std::basic_ostringstream
へ出力されて文字列が生成されるので、マニピュレータでの設定は { ... }
の外には反映しません。
記述例
sample-1.cpp
#include <iomanip>
#include <iostream>
#include "sosblk.hpp" // 下にあります.
int main()
{
using coutblock = simple_ostringblock<std::ostringstream>;
std::cout << coutblock {
{
"boolalpha : ", std::boolalpha, true, std::endl,
"noboolalpha : ", std::noboolalpha, false, std::endl,
},
{
"showbase : ", std::showbase, std::hex, 0x2c, std::endl,
"noshowbase : ", std::noshowbase, std::hex, 0x2c, std::endl,
},
{
"showpoint : ", std::showpoint, 1., std::endl,
"noshowpoint : ", std::noshowpoint, 1., std::endl,
},
{
"showpos : ", std::showpos, 1, ", ", 0, ", ", -1, std::endl,
"noshowpos : ", std::noshowpos, 1, ", ", 0, ", ", -1, std::endl,
},
{ std::hex,
"uppercase : ", std::uppercase, std::setfill('0'), std::setw(6), 0x2c, std::endl,
"nouppercase : ", std::nouppercase, 0x2c, std::endl,
},
{
"left : ", '"', std::left, std::setw(6), -12, '"', std::endl,
"internal : ", '"', std::internal, std::setw(6), -12, '"', std::endl,
"right : ", '"', std::right, std::setw(6), -12, '"', std::endl,
},
{
"oct : ", std::oct, 0x2c, std::endl,
"dec : ", std::dec, 0x2c, std::endl,
"hex : ", std::hex, 0x2c, std::endl,
},
{
"fixed : ", std::fixed, 0.1, std::endl,
"scientific : ", std::scientific, 0.1, ", ", std::setprecision(8), 0.1, std::endl,
"hexfloat : ", std::hexfloat, 0.1, std::endl,
"defaultfloat: ", std::defaultfloat, 0.1, std::endl,
},
{
"setbase(8) : ", std::setbase(8), 0x2c, std::endl,
"setbase(10) : ", std::setbase(10), 0x2c, std::endl,
"setbase(16) : ", std::setbase(16), 0x2c, std::endl,
},
std::endl,
};
return 0;
}
実行結果
boolalpha : true
noboolalpha : 0
showbase : 0x2c
noshowbase : 2c
showpoint : 1.00000
noshowpoint : 1
showpos : +1, +0, -1
noshowpos : 1, 0, -1
uppercase : 00002C
nouppercase : 2c
left : "-12 "
internal : "- 12"
right : " -12"
oct : 54
dec : 44
hex : 2c
fixed : 0.100000
scientific : 1.000000e-01, 1.00000000e-01
hexfloat : 0x1.999999999999ap-4
defaultfloat: 0.1
setbase(8) : 54
setbase(10) : 44
setbase(16) : 2c
利用の容易さを考慮しています。
sosblk.hpp
#pragma once
#include <initializer_list>
#include <sstream>
template <typename OSStreamT>
class simple_ostringblock
{
public:
using char_type = typename OSStreamT::char_type;
using traits_type = typename OSStreamT::traits_type;
using allocator_type = typename OSStreamT::allocator_type;
using string_type = std::basic_string<char_type, traits_type, allocator_type>;
using ostream_type = std::basic_ostream<char_type, traits_type>;
protected:
using Self = simple_ostringblock<OSStreamT>;
using InitListT = std::initializer_list<Self>;
using IosF = std::ios_base& (*)(std::ios_base&);
using ManipF = ostream_type& (*)(ostream_type&);
// 型別の処理.
template <typename T> static void W(ostream_type& o, void* v) { o << *(T*)v; }
template <typename T> static void D(void* v) { delete (T*)v; }
bool bs; // 文字列を保持しているか?
void *vp; // データへのポインタ.
// 自前の vtable
void (*wp)(ostream_type& o, void* v); // virtual void wp(ostream_type& o, T* v);
void (*dp)(void*); // delete vp --> virtual ~T();
public:
operator const char_type*() const { return str().c_str(); }
const string_type& str() const { static string_type s; return bs ? *(const string_type*)vp : s; }
// データ保持用コンストラクタ.
template <typename T>
simple_ostringblock(const T& v) : bs(false), vp(new T(v)), wp(W<T>), dp(D<T>) {}
simple_ostringblock(IosF v) : bs(false), vp(new IosF(v)), wp(W<IosF>), dp(D<IosF>) {}
simple_ostringblock(ManipF v) : bs(false), vp(new ManipF(v)), wp(W<ManipF>), dp(D<ManipF>) {}
// 文字列保持用コンストラクタ.
simple_ostringblock(const string_type& s)
: bs(true), vp(new string_type(s)), wp(W<string_type>), dp(D<string_type>) {}
simple_ostringblock(const char_type* s) : simple_ostringblock(string_type(s)) {}
simple_ostringblock(const Self& s) : simple_ostringblock(s.str()) {}
// ブロック "{...}" を受け取るコンストラクタ.
simple_ostringblock(const InitListT& list)
: bs(true), wp(W<string_type>), dp(D<string_type>)
{
OSStreamT o;
for (const Self& s : list)
s.wp(o, s.vp);
vp = new string_type(std::move(o.str()));
}
// デストラクタ.
~simple_ostringblock() { dp(vp); }
};
コメント最後の "." が気になる人はこちら。
出力順可変版
ブロック {...}
の先頭に ::order{...}
を置くと、後続の要素の出力順が変更可能です。
sample-2.cpp
#include <iostream>
#include "bosblk.hpp" // 下にあります.
int main()
{
using coutblock = ostringblock;
std::cout << coutblock {
{
/*0*/ coutblock::order{ 1, 2, 3 },
/*1*/ "+1",
/*2*/ "+2",
/*3*/ "+3",
},
std::endl,
{
/*0*/ coutblock::order{ 2, 3, 1 },
/*1*/ "+1",
/*2*/ "+2",
/*3*/ "+3",
},
std::endl,
{
/*0*/ coutblock::order{ 3, 1, 2 },
/*1*/ "+1",
/*2*/ "+2",
/*3*/ "+3",
},
std::endl,
{
/*0*/ coutblock::order{ 1, 1, 1 },
/*1*/ "+1",
},
std::endl,
{
/*0*/ coutblock::order{ 1, 2, 2 },
/*1*/ "+1",
/*2*/ "+2",
},
std::endl,
{
/*0*/ coutblock::order{ 3, 2, 1 },
/*1*/ "+1",
/*2*/ "+2",
/*3*/ "+3",
},
std::endl,
};
return 0;
}
実行結果
+1+2+3
+2+3+1
+3+1+2
+1+1+1
+1+2+2
+3+2+1
実行効率を考慮しています。
bosblk.hpp
#pragma once
#include <cwchar>
#include <initializer_list>
#include <iomanip>
#include <sstream>
#include <vector>
template <typename OStringStream, typename Vector = std::vector<size_t> >
class basic_ostringblock
{
public:
using ostringstream_type = OStringStream;
using vector_type = Vector;
using char_type = typename ostringstream_type::char_type;
using traits_type = typename ostringstream_type::traits_type;
using string_allocator_type = typename ostringstream_type::allocator_type;
using string_type = std::basic_string<char_type, traits_type, string_allocator_type>;
using index_type = typename vector_type::value_type;
using vector_allocator_type = typename vector_type::allocator_type;
struct order : vector_type { using vector_type::vector_type; };
protected:
using ostream_type = std::basic_ostream<char_type, traits_type>;
using initializer_list_type = std::initializer_list<basic_ostringblock>;
using iosflag_function = std::ios_base& (*)(std::ios_base&);
using ostream_function = ostream_type& (*)(ostream_type&);
using setiosflags_type = decltype(std::setiosflags(std::ios_base::fmtflags(0)));
using resetiosflags_type = decltype(std::resetiosflags(std::ios_base::fmtflags(0)));
using setw_type = decltype(std::setw(0));
using setprecision_type = decltype(std::setprecision(0));
using setbase_type = decltype(std::setbase(10));
using op_lshift = bool(*)(const basic_ostringblock*, ostream_type&);
using destructor = void (*)(void*);
/**/
enum data_type {
trivial_data,
object_data,
index_data,
string_data,
};
/**/
data_type mode;
op_lshift lshift;
destructor finish;
inline static constexpr int size_max(size_t z)
{ return int(z); }
inline static constexpr int size_max(size_t z, size_t y)
{ return int(z > y ? z : y); }
template <typename... Args>
inline static constexpr int size_max(size_t z, Args... args)
{ return int(z) > size_max(args...) ? int(z) : size_max(args...); }
enum {
buffer_size = size_max(
sizeof(void*),
sizeof(int),
sizeof(long long),
sizeof(double),
sizeof(iosflag_function),
sizeof(ostream_function),
sizeof(string_type),
sizeof(vector_type),
sizeof(setiosflags_type),
sizeof(resetiosflags_type),
sizeof(setw_type),
sizeof(setprecision_type),
sizeof(setbase_type),
0),
buffer_align = size_max(
alignof(void*),
alignof(int),
alignof(long long),
alignof(double),
alignof(iosflag_function),
alignof(ostream_function),
alignof(string_type),
alignof(vector_type),
alignof(setiosflags_type),
alignof(resetiosflags_type),
alignof(setw_type),
alignof(setprecision_type),
alignof(setbase_type),
0),
};
alignas(buffer_align) std::uint8_t buffer[buffer_size];
template <typename T>
using is_small_object = typename std::conditional<
(sizeof(T) <= buffer_size && alignof(T) <= buffer_align),
std::true_type, std::false_type>::type;
/**/
struct trivial_tag { inline constexpr trivial_tag() noexcept {}; };
struct small_object_tag { inline constexpr small_object_tag() {}; };
struct large_object_tag { inline constexpr large_object_tag() {}; };
template <typename T>
using select_object_tag = typename std::conditional<
is_small_object<T>::value, small_object_tag, large_object_tag>::type;
template <typename T>
using select_trivial_tag = typename std::conditional<
is_small_object<T>::value && std::is_trivial<T>::value,
trivial_tag, select_object_tag<T>>::type;
template <typename T>
struct select_function_tag
{
template <bool B> struct funcref { using type = select_trivial_tag<T>; };
template <> struct funcref<true> { using type = trivial_tag; };
using type = typename funcref<std::is_function<T>::value>::type;
};
template <typename T>
using select_tag = typename select_function_tag<T>::type;
/**/
static bool lshift_none(const basic_ostringblock* s, ostream_type& o)
{ (void)s; (void)o; return false; }
template <typename T>
static bool lshift_data(const basic_ostringblock* s, ostream_type& o)
{ o << *(T*)s->buffer; return true; }
template <typename T>
static bool lshift_object(const basic_ostringblock* s, ostream_type& o)
{ o << **(T**)s->buffer; return true; }
inline bool write(ostringstream_type& o) const
{ return lshift(this, o); }
template <typename T> static void discard(void* p) noexcept { ((T*)p)->~T(); }
template <typename T> static void destroy(void* p) noexcept { delete *(T**)p; }
inline string_type& strbuf() noexcept { return *(string_type*)buffer; }
inline const string_type& strbuf() const noexcept { return *(string_type*)buffer; }
inline vector_type& vecbuf() noexcept { return *(vector_type*)buffer; }
inline const vector_type& vecbuf() const noexcept { return *(vector_type*)buffer; }
public:
inline const string_type& str() const noexcept
{
static string_type s;
return mode == string_data ? strbuf() : s;
}
inline const char_type* c_str() const noexcept { return str().c_str(); }
inline operator const string_type&() const noexcept { return str(); }
inline operator const char_type*() const noexcept { return c_str(); }
/**/
inline basic_ostringblock(ostream_function n) noexcept
: basic_ostringblock(n, trivial_tag()) {}
inline basic_ostringblock(const char_type* s)
: mode(string_data)
, lshift(lshift_data<string_type>)
, finish(discard<string_type>)
{ new (buffer) string_type(s); }
inline basic_ostringblock(const string_type& s)
: basic_ostringblock(s, string_data, small_object_tag()) {}
inline basic_ostringblock(const order& data)
: mode(index_data), lshift(lshift_none), finish(discard<order>)
{ new (buffer) order(data); }
template <typename T>
inline basic_ostringblock(const T& s) noexcept(noexcept(select_tag<T>()))
: basic_ostringblock(s, select_tag<T>()) {}
protected:
template <typename T>
inline basic_ostringblock(T n, trivial_tag) noexcept
: mode(trivial_data), lshift(lshift_data<T>), finish()
{ new (buffer) T(n); }
template <typename T>
inline basic_ostringblock(const T& s, small_object_tag)
: basic_ostringblock(s, object_data, small_object_tag()) {}
template <typename T>
inline basic_ostringblock(const T& s, data_type t, small_object_tag)
: mode(t), lshift(lshift_data<T>), finish(discard<T>)
{ new (buffer) T(s); }
template <typename T>
inline basic_ostringblock(const T& s, large_object_tag)
: mode(object_data), lshift(lshift_object<T>), finish(destroy<T>)
{ *(T**)buffer = new T(s); }
public:
basic_ostringblock(const initializer_list_type& args)
: mode(string_data)
, lshift(lshift_data<string_type>)
, finish(discard<string_type>)
{
using iterator = typename initializer_list_type::const_iterator;
if (!args.size())
{
new (buffer) string_type();
return;
}
ostringstream_type stream;
iterator top = args.begin();
if (top->mode != index_data)
for (auto& child : args)
child.write(stream);
else
for (auto n : top->vecbuf())
if (0 < n && size_t(n) < args.size())
(top + n)->write(stream);
new (buffer) string_type(std::move(stream.str()));
}
inline ~basic_ostringblock() { if (finish) finish(buffer); }
#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L
protected:
#endif /* C++17 */
const basic_ostringblock& operator =(const basic_ostringblock& s) = delete;
inline basic_ostringblock(const basic_ostringblock& s) noexcept // = delete
: basic_ostringblock(s.str()) {}
};
using ostringblock = basic_ostringblock<std::ostringstream>;
using wostringblock = basic_ostringblock<std::wostringstream>;
参考
C++17の初期版のプログラム(実行効率は度外視)
cofmt::order
を使用している部分で出力順序を入れ替えています。
sample.cpp
// ----------------------------------------------------------------------------
#include <cstdio>
#include <any>
#include <initializer_list>
#include <iomanip>
#include <sstream>
#include <type_traits>
#include <vector>
template <typename CharT, typename Traits = std::char_traits<CharT>>
class initializer_list_oss
{
protected:
using char_type = CharT;
using traits_type = Traits;
using ostream_type = std::basic_ostream<char_type, traits_type>;
using oss_type = std::basic_ostringstream<char_type, traits_type>;
using initializer_list_oss_type = initializer_list_oss<char_type>;
using initializer_list_type = std::initializer_list<initializer_list_oss_type>;
using setf_type = std::ios_base& (*)(std::ios_base&);
using manip_type = ostream_type& (*)(ostream_type&);
using any_type = std::any;
using string_type = std::basic_string<char_type, traits_type>;
public:
using order = std::vector<size_t>;
protected:
order index;
any_type value;
string_type output;
public:
const string_type& str() const { return output; }
const char_type* c_str()const { return str().c_str(); }
operator const char_type*() const { return c_str(); }
initializer_list_oss(const order& data) : index(data) {}
initializer_list_oss(const char_type* data) : value(string_type(data)) {}
initializer_list_oss(setf_type data) : value(data) {}
initializer_list_oss(manip_type data) : value(data) {}
template <typename T> initializer_list_oss(T data) : value(data) {}
template <typename T> initializer_list_oss(T* data) : value((void*)data) {}
initializer_list_oss(const initializer_list_type args)
{
using iterator = typename initializer_list_type::const_iterator;
if (!args.size())
return;
oss_type stream;
iterator top = args.begin();
if (!top->index.size())
for (auto& child : args)
child.put(stream);
else
for (auto n : top->index)
if (0 < n && n < args.size())
(top + n)->put(stream);
output = stream.str();
value = output;
}
void put(oss_type& o) const
{
using setiosflags_type = decltype(std::setiosflags(std::ios_base::fmtflags(0)));
using resetiosflags_type = decltype(std::resetiosflags(std::ios_base::fmtflags(0)));
using setw_type = decltype(std::setw(0));
using setfill_type = decltype(std::setfill('0'));
using setprecision_type = decltype(std::setprecision(0));
using setbase_type = decltype(std::setbase(10));
using put_time_type = decltype(std::put_time(nullptr, ""));
using quoted_type = decltype(std::quoted(""));
if (!value.has_value())
/*NOOP*/;
else if (value.type() == typeid(bool))
o << std::any_cast<bool>(value);
else if (value.type() == typeid(char))
o << std::any_cast<char>(value);
else if (value.type() == typeid(char16_t))
o << std::any_cast<char16_t>(value);
else if (value.type() == typeid(char32_t))
o << std::any_cast<char32_t>(value);
else if (value.type() == typeid(signed char))
o << std::any_cast<signed char>(value);
else if (value.type() == typeid(unsigned char))
o << std::any_cast<unsigned char>(value);
else if (value.type() == typeid(signed short))
o << std::any_cast<signed short>(value);
else if (value.type() == typeid(unsigned short))
o << std::any_cast<unsigned short>(value);
else if (value.type() == typeid(signed int))
o << std::any_cast<signed int>(value);
else if (value.type() == typeid(unsigned int))
o << std::any_cast<unsigned int>(value);
else if (value.type() == typeid(signed long))
o << std::any_cast<signed long>(value);
else if (value.type() == typeid(unsigned long))
o << std::any_cast<unsigned long>(value);
else if (value.type() == typeid(signed long long))
o << std::any_cast<signed long long>(value);
else if (value.type() == typeid(unsigned long long))
o << std::any_cast<unsigned long long>(value);
else if (value.type() == typeid(float))
o << std::any_cast<float>(value);
else if (value.type() == typeid(double))
o << std::any_cast<double>(value);
else if (value.type() == typeid(void*))
o << std::any_cast<void*>(value);
else if (value.type() == typeid(string_type))
o << std::any_cast<string_type>(value);
/**/
else if (value.type() == typeid(setf_type))
o << std::any_cast<setf_type>(value);
else if (value.type() == typeid(manip_type))
o << std::any_cast<manip_type>(value);
/**/
else if (value.type() == typeid(setiosflags_type))
o << std::any_cast<setiosflags_type>(value);
else if (value.type() == typeid(resetiosflags_type))
o << std::any_cast<resetiosflags_type>(value);
/**/
else if (value.type() == typeid(setw_type))
o << std::any_cast<setw_type>(value);
else if (value.type() == typeid(setfill_type))
o << std::any_cast<setfill_type>(value);
else if (value.type() == typeid(setprecision_type))
o << std::any_cast<setprecision_type>(value);
else if (value.type() == typeid(setbase_type))
o << std::any_cast<setbase_type>(value);
else if (value.type() == typeid(put_time_type))
o << std::any_cast<put_time_type>(value);
else if (value.type() == typeid(quoted_type))
o << std::any_cast<quoted_type>(value);
else // FIXME: if (...)
std::fprintf(stderr, "\nunkown type: %s\n", value.type().name());
}
};
// ----------------------------------------------------------------------------
#include <iostream>
int main(int argc, char** argv)
{
using cofmt = initializer_list_oss<char>;
bool swap = (argc > 1 && argv[1][0] == '-' && argv[1][1] == 's');
std::cout << cofmt { "main(argc=", argc, ", argv=", argv, ")", std::endl };
std::cout << cofmt {
"main(",
{
/*0*/ (swap ? cofmt::order {3, 2, 1} : cofmt::order {1, 2, 3}),
/*1*/ { "argc=", argc },
/*2*/ ", ",
/*3*/ { "argv=", argv },
},
")", std::endl,
std::endl,
{
"boolalpha : ", std::boolalpha, true, std::endl,
"noboolalpha : ", std::noboolalpha, false, std::endl,
},
{
"showbase : ", std::showbase, std::hex, 0x2c, std::endl,
"noshowbase : ", std::noshowbase, std::hex, 0x2c, std::endl,
},
{
"showpoint : ", std::showpoint, 1., std::endl,
"noshowpoint : ", std::noshowpoint, 1., std::endl,
},
{
"showpos : ", std::showpos, 1, ", ", 0, ", ", -1, std::endl,
"noshowpos : ", std::noshowpos, 1, ", ", 0, ", ", -1, std::endl,
},
{ std::hex,
"uppercase : ", std::uppercase, std::setfill('0'), std::setw(6), 0x2c, std::endl,
"nouppercase : ", std::nouppercase, 0x2c, std::endl,
},
{
"left : ", '"', std::left, std::setw(6), -12, '"', std::endl,
"internal : ", '"', std::internal, std::setw(6), -12, '"', std::endl,
"right : ", '"', std::right, std::setw(6), -12, '"', std::endl,
},
{
"oct : ", std::oct, 0x2c, std::endl,
"dec : ", std::dec, 0x2c, std::endl,
"hex : ", std::hex, 0x2c, std::endl,
},
{
"fixed : ", std::fixed, 0.1, std::endl,
"scientific : ", std::scientific, 0.1, ", ", std::setprecision(8), 0.1, std::endl,
"hexfloat : ", std::hexfloat, 0.1, std::endl,
"defaultfloat: ", std::defaultfloat, 0.1, std::endl,
},
{
"quoted : ", std::quoted(""), std::endl,
},
{
"setbase(8) : ", std::setbase(8), 0x2c, std::endl,
"setbase(10) : ", std::setbase(10), 0x2c, std::endl,
"setbase(16) : ", std::setbase(16), 0x2c, std::endl,
},
std::endl,
"setiosflags / resetiosflags:", std::endl,
{ " boolalpha: ",
"[set=", std::setiosflags(std::ios_base::boolalpha), true,
"], [reset=", std::resetiosflags(std::ios_base::boolalpha), true,
"]", std::endl
},
{ " showbase : ", std::hex,
"[set=", std::setiosflags(std::ios_base::showbase), 0x2c,
"], [reset=", std::resetiosflags(std::ios_base::showbase), 0x2c,
"]", std::endl
},
};
return 0;
}