std::cout << o { "sample: ", hex, 321, endl }; としたい

# <code>operator&lt;&lt;</code> を並べたくない

<code>&lt;&lt;</code> に慣れればいいのでしょうけど

```C++:これを
#include <iostream>
int main(int argc, char** argv)
{
    std::cout << "main(argc=" << argc << ", argv=" << argv << ")" << std::endl;
}
```

<code>std::initializer_list</code> を使えば

```C++:こうしたい
    std::cout << o { "main(argc=", argc, ", argv=", argv, ")", std::endl };
```

が出来そうな気がしたので作ってみた。


# 単純処理版

<code>{ ... }</code> が一つの <code>std::basic_ostringstream</code> へ出力されて文字列が生成されるので、マニピュレータでの設定は <code>{ ... }</code> の外には反映しません。

**記述例**

```C++: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;
}
```

```console:実行結果
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
```

利用の容易さを考慮しています。

```C++: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); }
};
```

コメント最後の "." が気になる人は[こちら](https://qiita.com/ikiuo/items/a08d3dca2db3ab11e392)。


# 出力順可変版

ブロック <code>{...}</code> の先頭に <code>::order{...}</code> を置くと、後続の要素の出力順が変更可能です。

```C++: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;
}
```

```console:実行結果
+1+2+3
+2+3+1
+3+1+2
+1+1+1
+1+2+2
+3+2+1
```

実行効率を考慮しています。

```C++: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>;
```

# 参考

<details><summary>C++17の初期版のプログラム(実行効率は度外視)</summary><div>

<code>cofmt::order</code> を使用している部分で出力順序を入れ替えています。

```C++: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;
}
```

</div></details>

https://qiita.com/ikiuo/items/ed4b4fb170ffe81a017d