Question

J'ai été googler autour et je ne peux pas trouver une réponse simple à cette question. Et il devrait être simple, comme le TSL est en général.

Je veux définir MyOStream qui hérite publiquement de std :: ostream. Disons que je veux appeler foo () chaque fois que quelque chose est écrit dans mon flux.

class MyOStream : public ostream {
public:
  ...
private:
   void foo() { ... }
}

Je comprends que l'interface publique de ostream est non-virtuelle, alors comment peut-il être fait? Je veux que les clients de pouvoir utiliser à la fois opérateur << et écrire () et de mettre () sur MyOStream et ont la capacité d'utiliser prolongée de ma classe.

Était-ce utile?

La solution

Il est pas une question simple, malheureusement. Les classes que vous devriez tirer de sont les classes basic_, comme basic_ostream. Cependant, la dérivation d'un cours d'eau peut ne pas être ce que vous voulez, vous pouvez tirer d'un lieu tampon de flux, puis utiliser cette classe pour instancier une classe de flux existant.

La région est complexe, mais il est un excellent livre sur le sujet standard C ++ et iostreams Paramètres régionaux , que je vous suggère de jeter un oeil à avant d'aller plus loin.

Autres conseils

Je filais ma tête autour de la façon de faire la même chose et j'ai découvert c'est en fait pas si difficile. Fondamentalement, la sous-classe juste le ostream et les objets streambuf, et construire le ostream avec lui-même en tant que tampon. le débordement virtuel () std :: streambuf sera appelé pour chaque caractère envoyé au flux. Pour adapter votre exemple, je viens de faire une fonction foo () et l'a appelé.

struct Bar : std::ostream, std::streambuf
{
    Bar() : std::ostream(this) {}

    int overflow(int c)
    {
        foo(c);
        return 0;
    }


    void foo(char c)
    {
        std::cout.put(c);

    }
};

void main()
{
    Bar b;
    b<<"Look a number: "<<std::hex<<29<<std::endl;
}

oh et ne pas tenir compte du fait que la fonction principale est pas une véritable fonction principale. Il est dans un espace de noms appelé d'ailleurs; p

Une autre astuce de travail pour obtenir un effet similaire est d'utiliser le modèle et la composition

class LoggedStream {
public:
  LoggedStream(ostream& _out):out(_out){}
  template<typename T>
  const LoggedStream& operator<<(const T& v) const {log();out << v;return *this;}
protected:
  virtual void log() = 0;
  ostream& out;
};

class Logger : LoggedStream {
  void log() { std::cerr << "Printing" << std::endl;}
};

int main(int,char**) {LoggedStream(std::cout) << "log" << "Three" << "times";}

Je ne sais pas si cela est une solution correcte, mais je hérité de std :: ostream cette façon. Il utilise une méthode héritée de tampon std :: basic_streambuf et obtient 64 caractères à la fois (ou moins si rincée) et les envoie à un putChars générique () où le traitement réel des données se fait. Il montre également comment donner des données utilisateur.

en direct Exemple

#include <streambuf>
#include <ostream>
#include <iostream>

//#define DEBUG

class MyData
{
    //example data class, not used
};

class MyBuffer : public std::basic_streambuf<char, std::char_traits<char> >
{

public:

    inline MyBuffer(MyData data) :
    data(data)
    {
        setp(buf, buf + BUF_SIZE);
    }

protected:

    // This is called when buffer becomes full. If
    // buffer is not used, then this is called every
    // time when characters are put to stream.
    inline virtual int overflow(int c = Traits::eof())
    {
#ifdef DEBUG
        std::cout << "(over)";
#endif
        // Handle output
        putChars(pbase(), pptr());
        if (c != Traits::eof()) {
            char c2 = c;
            // Handle the one character that didn't fit to buffer
            putChars(&c2, &c2 + 1);
        }
        // This tells that buffer is empty again
        setp(buf, buf + BUF_SIZE);

        return c;
    }

    // This function is called when stream is flushed,
    // for example when std::endl is put to stream.
    inline virtual int sync(void)
    {
        // Handle output
        putChars(pbase(), pptr());
        // This tells that buffer is empty again
        setp(buf, buf + BUF_SIZE);
        return 0;
    }

private:

    // For EOF detection
    typedef std::char_traits<char> Traits;

    // Work in buffer mode. It is also possible to work without buffer.
    static const size_t BUF_SIZE = 64;
    char buf[BUF_SIZE];

    // This is the example userdata
    MyData data;

    // In this function, the characters are parsed.
    inline void putChars(const char* begin, const char* end){
#ifdef DEBUG
        std::cout << "(putChars(" << static_cast<const void*>(begin) <<
            "," << static_cast<const void*>(end) << "))";
#endif
        //just print to stdout for now
        for (const char* c = begin; c < end; c++){
            std::cout << *c;
        }
    }

};

class MyOStream : public std::basic_ostream< char, std::char_traits< char > >
{

public:

    inline MyOStream(MyData data) :
    std::basic_ostream< char, std::char_traits< char > >(&buf),
    buf(data)
    {
    }

private:

    MyBuffer buf;

};

int main(void)
{
    MyData data;
    MyOStream o(data);

    for (int i = 0; i < 8; i++)
        o << "hello world! ";

    o << std::endl;

    return 0;
}

Composition, pas l'héritage. Votre classe contient, "enveloppe" un ostream & et avant lui (après avoir appelé foo ()).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top