58 votes

C++ flux personnalisé manipulateur que les changements élément suivant sur le stream

En C++, pour imprimer un nombre en hexadécimal pour ce faire:

int num = 10;
std::cout << std::hex << num; // => 'a'

Je sais que je peux créer un manipulateur qui ajoute juste des trucs dans le flux de la sorte:

std::ostream& windows_feed(std::ostream& out)
{
    out << "\r\n";
    return out;
}

std::cout << "Hello" << windows_feed; // => "Hello\r\n"

Cependant, comment puis-je créer un manipulateur qui, comme 'hex', modifie les articles à venir sur le stream? Comme un exemple simple, comment pourrais-je créer l'plusone manipulateur ici?:

int num2 = 1;
std::cout << "1 + 1 = " << plusone << num2; // => "1 + 1 = 2"

// note that the value stored in num2 does not change, just its display above.
std::cout << num2; // => "1"

72voto

Johannes Schaub - litb Points 256113

Tout d'abord, vous avez à stocker de l'état dans chaque cours d'eau. Vous pouvez le faire avec la fonction iword et un indice vous passez à elle, donnée par xalloc:

inline int geti() { 
    static int i = ios_base::xalloc();
    return i;
}

ostream& add_one(ostream& os) { os.iword(geti()) = 1; return os; } 
ostream& add_none(ostream& os) { os.iword(geti()) = 0; return os; }

Ayant cela en place, vous pouvez déjà récupérer une partie de l'état dans tous les cours d'eau. Maintenant, vous avez juste à le brancher dans la sortie respective de l'opération. Sortie numérique est effectuée par une facette, parce qu'il est potentiellement locale dépendante. De sorte que vous pouvez faire

struct my_num_put : num_put<char> {
    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 

    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, unsigned long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 
}; 

Maintenant, vous pouvez tester le truc.

int main() {
    // outputs: 11121011
    cout.imbue(locale(locale(),new my_num_put));
    cout << add_one << 10 << 11 
         << add_none << 10 << 11;
}

Si vous voulez que la prochaine nombre est incrémenté, il suffit de mettre le mot d' 0 nouveau après chaque appel à l' do_put.

13voto

1800 INFORMATION Points 55907

Je suis totalement d'accord avec Neil Butterworth sur celui-ci, cependant, dans le cas précis que vous utilisez, vous pourriez faire ceci totalement horrible hack. Ne pas le faire dans toute la production de code. Il y a beaucoup de bugs. Pour une chose, il ne fonctionne que dans votre one-liner au-dessus, il ne change pas l'état du flux sous-jacent.

class plusone_stream : public std::ostream
{
  public:
    std::ostream operator<<(int i)
    {
      _out << i+1;
      return *this;
    }
};

std::ostream& plusone(std::ostream& out)
{
    return plusone_stream(out);
}

2voto

Dan Breslau Points 9217

J'ai créé une solution simple pour votre cas de test sans l'aide d' <iomanip>. Je ne peux pas promettre que la même approche dans la vraie vie.

L'approche de base est que l' cout << plusone renvoie un objet d'auxiliaire temporaire (PlusOnePlus), qui à son tour a surchargées operator << qui effectue l'addition.

Je l'ai testé sur Windows:

PlusOne plusone;
cout << plusone << 41

produit "42", comme prévu. Voici le code:

class PlusOnePlus {
public:
    PlusOnePlus(ostream& os) : m_os(os) {}
    // NOTE: This implementation relies on the default copy ctor,
    // assignment, etc.
private:
    friend ostream& operator << (PlusOnePlus& p, int n);
    ostream& m_os;
};

class PlusOne {
public:
    static void test(ostream& os);
};

PlusOnePlus operator << (ostream& os, const PlusOne p)
{
    return PlusOnePlus(os);
}

ostream& operator << (PlusOnePlus& p, int n)
{
    return p.m_os << n + 1;
}

void PlusOne::test(ostream& os)
{
    PlusOne plusone;
    os << plusone << 0 << endl;
    os << plusone << 41 << endl;
}

EDIT: a Commenté le code pour signaler que je suis en s'appuyant sur le constructeur de copie par défaut (etc.) pour PlusOnePlus. Une mise en œuvre robuste serait probablement définir ces

1voto

Luc Hermitte Points 14171

Vous aurez à jouer avec streamstates. J'ai un signet suivant des liens sur le sujet:

0voto

Dan Points 3171

litb l'approche est "le droit chemin" et nécessaires pour des choses compliquées, mais quelque chose comme cela peut être assez bon. Ajouter de la vie privée et de l'amitié au goût.

struct PlusOne
{
   PlusOne(int i) : i_(i) { }
   int i_;
};

std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
   return o << (po.i_ + 1);
}

std::cout << "1 + 1 = " << PlusOne(num2); // => "1 + 1 = 2"

Dans cet exemple simple de la création et de la diffusion d'un objet temporaire ne semble pas beaucoup plus utile que la définition d'une fonction plusOne() comme quelqu'un l'a déjà suggéré. Mais supposons que vous vouliez qu'il fonctionne comme ceci:

std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
   return o << po.i_ << " + 1 = " << (po.i_ + 1);
}

std::cout << PlusOne(num2); // => "1 + 1 = 2"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X