// demo_string.h

#ifndef INCLUDED_STRING
#define INCLUDED_STRING

#include <iosfwd>
#include <string>

namespace demo
{
    // The class string is merely defined to have something which can be used
    // to overload the output operator.
    struct string
        : std::string {
        using std::string::string;
    };
    std::ostream& operator<< (std::ostream&, string const&);

    std::ostream& noquote(std::ostream&);
    std::ostream& squote(std::ostream&);
    std::ostream& dquote(std::ostream&);

    struct prefix
        : std::string {
        using std::string::string;
    };
    std::ostream& operator<< (std::ostream&, prefix const&);
}

#endif

// demo_string.cpp
// #include "demo/string.h"
#include <ostream>

namespace
{
    int stringFormatIndex() {
        static int rc = std::ios_base::xalloc();
        return rc;
    }

    void callback(std::ios_base::event ev, std::ios_base& s, int index) {
        void* pword = s.pword(index);
        switch (ev) {
        case std::ios_base::erase_event:
            delete static_cast<std::string*>(pword);
            pword = 0;
            break;
        case std::ios_base::copyfmt_event:
            pword = new std::string(*static_cast<std::string*>(pword));
            break;
        default:
            break;
        }
    }
}

std::ostream& demo::noquote(std::ostream& out) {
    out.iword(stringFormatIndex()) = 0;
    return out;
}
std::ostream& demo::squote(std::ostream& out) {
    out.iword(stringFormatIndex()) = '\'';
    return out;
}
std::ostream& demo::dquote(std::ostream& out) {
    out.iword(stringFormatIndex()) = '"';
    return out;
}

std::ostream& demo::operator<< (std::ostream& out, demo::string const& str) {
    int index{stringFormatIndex()};
    if (out.pword(index)) {
        out << *static_cast<std::string const*>(out.pword(index));
    }
    char quote(out.iword(index));
    return quote? out << quote << str.c_str() << quote: out << str.c_str();
}

std::ostream& demo::operator<< (std::ostream& out, demo::prefix const& p) {
    void*& pword(out.pword(stringFormatIndex()));
    if (pword) {
        *static_cast<std::string*>(pword) = p;
    }
    else {
        out.register_callback(&callback, stringFormatIndex());
        pword = new std::string(p);
    }
    return out;
}

// main.cpp
// #include "demo/string.h"
#include <iostream>

int main()
{
    demo::string value("whatever");
    std::cout << demo::prefix("value=")
              << value << ' '
              << demo::squote  << value << ' '
              << demo::dquote  << value << ' '
              << demo::noquote << value << ' '
              << '\n';
        ;
}
