-2

I am trying to implement my own basic_string, but came across a problem with printing my strings. I am not going to use std::char_traits and other traits from std, as I've implemented ones myself. How can I create a direct analogue of std::cout that could be used for my strings and use std::basic_ostream for that? (not gonna create basic_ostream myself).

I tried some approaches to the problem. I created the following operator:

template<typename CharType, typename CharTraits>
std::basic_ostream<CharType, CharTraits>& 
operator<<(std::basic_ostream<CharType, CharTraits>& o, const AnyString<CharType, CharTraits>& str)
{
    using size_type = AnyString<CharType, CharTraits>::size_type;
    for (size_type i = 0u; i < str.size(); ++i)
    {
        o << str[i];
    }

    return o;
}

Then I tried using it with std::cout like this:

cout << str;

but the problem was: "no operator<< matches these operands". The reason is std::cout uses std::char_traits<char> but not CharTraits<char, int> that I developed.

I decided to create my own version of std::cout:

using Ostream = std::basic_ostream<char, CharTraits<char, int> >;
Ostream Cout;

But it doesn't compile for this reason:

std::basic_ostream<char,CharTraits<char,int>>': no appropriate default constructor available

I need to understand what is the most appropriate way to initialize my version of std::cout.

Capy Maths
  • 79
  • 6
  • 2
    Can't you just create overloads for `std::ostream& operator<<(std::ostream&, const YourOwnStringType>&)`? – Ted Lyngmo Nov 16 '22 at 20:48
  • 3
    https://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream because it does not in fact... wait for it... has a default constructor! – Sergey Kolesnik Nov 16 '22 at 20:48
  • @TedLyngmo no, that doen't work for reason `std::ostream = basic_ostream>;` but I need to use exactly CharTraits I created. – Capy Maths Nov 16 '22 at 21:05
  • 1
    @CapyMaths I did not understand that reason at all. You want to create your own `basic_string` and be able to stream it out to `ostream`s, right? If so, adding the overloads I suggested is how it's usually done. ... or with `N` added to the string-type too. [example](https://godbolt.org/z/bq8bhaYhG) – Ted Lyngmo Nov 16 '22 at 21:06
  • @TedLyngmo I edited my question. Now the problem must be more clear. – Capy Maths Nov 16 '22 at 23:15

1 Answers1

4

How can I create a direct analogue of std::cout that could be used for my strings and use std::basic_ostream for that?

You don't need to create a custom ostream at all. All you need is an overload of operator<< for the standard std::ostream, eg:

template<typename CharType, typename CharTraits>
std::ostream& operator<<(std::ostream& o, const AnyString<CharType, CharTraits>& str)
{
    // print the contents of str to out as needed...
    using size_type = AnyString<CharType, CharTraits>::size_type;
    for (size_type i = 0u; i < str.size(); ++i)
    {
        o << (char) str[i];
    }

    return o;
}

Or, if you want the ostream to match the CharType of your string (ie, using std::cout for char strings, std::wcout for wchar_t strings, etc), you can use this instead:

template<typename CharType, typename CharTraits>
std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& o, const AnyString<CharType, CharTraits>& str)
{
    // print the contents of str to out as needed...
    using size_type = AnyString<CharType, CharTraits>::size_type;
    for (size_type i = 0u; i < str.size(); ++i)
    {
        o << str[i];
    }

    return o;
}

For example, the following code: ... doesn't compile for this reason:

That is because you are trying to create a default-constructed instance of std::basic_ostream, which doesn't have a default constructor. That has nothing to do with your custom string class.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • But that doesn't work. I need to use my `CharTraits`, which must not anyhow depend on stl `std::char_traits`. `std::ostream` can be only used with strings, whose traits are exactly `std::char_traits`. But that doesn't suit me at all. – Capy Maths Nov 16 '22 at 22:55
  • It works just fine. Your assertion is wrong. Your `CharTraits` can be applied to your custom string class, it does not need to be applied to `basic_ostream` itself. A custom `operator<<` overload can write your custom string to standard `basic_ostream` however you want it to, but `basic_ostream` must use its own standard traits, which can be deduced from a specified character type, if you want to use `std::(w)cout` specifically. I have updated my answer to show this. – Remy Lebeau Nov 17 '22 at 00:20
  • Oh, excuse me, you've been right, that really works. But still I doubt whether your solution is general enough. `basic_ostream` has char-trait template parameter, therefore I assume it is used in `basic_ostream` somehow. Don't we lose some necessary behaviour of `CharTraits` if `char_traits` are inserted in `basic_ostream` instead, as you suggest? I mean, is there any considerable difference between `basic_ostream>` and `basic_ostream>`? – Capy Maths Nov 17 '22 at 00:42
  • If you use custom traits with `basic_ostream`, you can't use `std::(w)cout` as its type won't match. Template arguments are part of a template class's type. Also, you would have to implement everything that `basic_ostream` uses from `char_traits` – Remy Lebeau Nov 17 '22 at 01:00
  • Yep, that is exactly what I was struggling with. I want to understant how to use basic_ostream with custom traits and thus - how to implement custom cout. Probably I have to refactor my question to make its point clear. – Capy Maths Nov 17 '22 at 01:29
  • 2
    Why are you so dead-set on avoiding `char_traits`? You are going to fight an uphill battle trying to make it work with standard streams. If you absolute need to, then you also need to deal with `std::basic_streambuf`, too which also uses traits. So, to implement a custom `Cout`, you would likely end up having to implement a custom `streambuf` to wrap `stdout`. So, what is your real end goal here? Why do all of this work? – Remy Lebeau Nov 17 '22 at 01:56
  • Indeed, that's the way I usually study. If I don't understand the inner structure of some essence, I am extreemly displeased with using it. So while I have enough time, I'm trying to implement myself as much things as I can. And you're right, I now cannot avoid deeping into std::basic_streambuf. Thank you ;) – Capy Maths Nov 17 '22 at 02:05
  • @CapyMaths you will very quicky run out of your time if you keep digging. What is your goal? To apply to a maintainer's position for std implementation? You do not need to know how things are implemented, rather you need to be familiar with the specification. – Sergey Kolesnik Nov 17 '22 at 08:25
  • @SergeyKolesnik It's just of special interest for me to get to know how stl is implemented. I will inevitably change the direction of my progress in some time for lack of time. But I'd keep it up while I have enough time. – Capy Maths Nov 17 '22 at 17:02