Ein String für alle Fälle

Man kann std::basic_string für eigene Arten von Strings verwenden. Alles, was man dazu anpassen muss, ist der char_t und ggf. das dazugehörige std::char_traits.

Video

Quellcode

#include <iostream>
#include <string>

struct ColorChar
{
    char c;
    char color;

    ColorChar()
	: c{ -1 }, color { -1 }
    {
    }

    ColorChar(char c_, char color_)
        : c { c_ }, color { color_ }
    {
    }

    bool operator==(ColorChar const &other) const
    {
	return (c == other.c) && (color == other.color);
    }
 
    bool operator<(ColorChar const &other) const
    {
        return c < other.c || (c == other.c && color < other.color);
    }
};

namespace std
{
template <> struct char_traits<ColorChar>
{
    typedef ColorChar char_type;
    typedef ColorChar int_type;
    typedef streamoff off_type;
    typedef streampos pos_type;
    typedef mbstate_t state_type;

    static bool eq(const char_type &c, const char_type &d)
    {
        return c == d;
    }

    static bool lt(const char_type &c, const char_type &d)
    {
        return c < d;
    }

    static std::size_t length(const char_type *s)
    {
        std::size_t l = 0;
        while (s->color != -1) {
            ++l;
        }
        return l;
    }
  
    static void assign(char_type &r, const char_type &c)
    {
        r.c = c.c;
        r.color = c.color;
    }

    static char_type *assign(char_type *p, std::size_t n, char_type c)
    {
        char_type *tmp = p;
        for (std::size_t counter = 0; counter < n; ++counter)
        {
            assign(*p, c);
            ++p;
        }
        return tmp;
    }

    static int compare(const char_type *p, const char_type *q, std::size_t n)
    {
        for (std::size_t counter = 0; counter < n; ++counter) {
            if (!eq(*p, *q)) {
                if (lt(*p, *q)) {
                    return -1;
                }
                else {
                    return 1;
                }
            }
        }
        return 0;
    }

    static const char_type *find(const char_type *p, std::size_t n, const char_type &c)
    {
        for (std::size_t counter = 0; counter < n; ++counter) {
            if (eq(*p, c)) {
                return p;
            }
            ++p;
        }
        return nullptr;
    }

    static char_type *move(char_type *d, const char_type *s, std::size_t n)
    {
        char_type *tmp = d;
        if (d < s) {
            for (std::size_t c = 0; c < n; ++c) {
                assign(*d, *s);
                ++d;
                ++s;
            }
        }
        else {
            d += (n-1);
            s += (n-1);
            for (std::size_t c = 0; c < n; ++c) {
                assign(*d, *s);
                --d;
                --s;
            }
        }
        return tmp;
    }
  
    static char_type *copy(char_type *d, const char_type *s, size_t n)
    {
        return move(d, s, n);
    }

    static int_type eof()
    {
        return ColorChar();
    }

    static int_type not_eof(const int_type &c)
    {
        return eq(c, eof()) ? ColorChar(0,0) : c;
    }

    static char_type to_char_type(const int_type &c)
    {
        return not_eof(c);
    }

    static int_type to_int_type(const char_type &c)
    {
        return c;
    }

    static bool eq_int_type(const int_type &x, const int_type &y)
    {
        return x == y;
    }
};
} // namespace std

int main()
{
   std::basic_string<ColorChar> s { ColorChar('h', 2), ColorChar('e', 2), 
                                    ColorChar('l', 5), ColorChar('l', 5), 
                                    ColorChar('o', 2) };
   std::basic_string<ColorChar> pattern { ColorChar('l', 5), ColorChar('l', 5) };

   std::cout << "Pattern wurde an Index " << s.find(pattern) << " gefunden.\n";  
}

Erklärung

Ziel des Codes hier ist die Entwicklung eines Strings, der farbige Zeichen speichern kann. Dazu definieren wir einfach eine Struktur, die den entsprechenden Buchstaben und die dazugehörige Farbe hält (ColorChar). Um die nun erfolgreich in einem String zu verwenden, müssen wir noch eine passende Character-Traits-Klasse entwerfen. Alles, was zeichenspezifisch ist lagert std::basic_string nämlich an diese Klasse aus. Um einfach einen std::basic_string<ColorChar> definieren zu können, spezialisieren wir std::char_traits<ColorChar> - das ist nämlich der Defaultwert des Traits-Parameters von std::basic_string.

Der Standard schreibt die Inhalte der Traitsklasse vor. Es beginnt mit einigen typdefs, die manche Algorithmen brauchen, um feststellen zu können, mit was sie es zu tun haben. Außerdem enthält der Typ sämtliche char_t-spezifischen Operationen, aus denen std::basic_string dann seine Algorithmen zusammensetzt. So gibt es beispielsweise eine Methode zum Vergleichen zweier Zeichen. Statt auf == zu setzen ruft std::basic_string char_traits::eq auf. Damit kann man beliebig komplexe Vergleichsalgorithmen implementieren. Außerdem gibt es noch Methoden zum Suchen eines Zeichens, zum Zuweisen, zum Feststellen der Länge etc.

std::basic_string kann sich daraus dann seine Funktionalität zusammenbauen. Wenn bspw. str.size() aufgerufen wird, dann wird das untendrunter möglicherweise an char_traits::length weitergereicht, welches die eigentliche Länge feststellt. Ob dann der Rückgabewert zwischengespeichert wird oder jedesmal neu berechnet, ist Sache von std::basic_string

Hat man die notwendigen Dinge implementiert, dann kann man, wie in Zeile 158 zu sehen, (verhältnismäßig) einfach neue String-Instanzen anlegen und (Zeile 163) die üblichen Operationen, wie find auf ihnen ausführen.