LoginSignup
0
0

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

Last updated at Posted at 2024-06-20

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;
}

0
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0