10
votes

Pouvez-vous passer un manipulateur à une fonction?

J'aimerais transmettre une liste de manipulateurs à une fonction, quelque chose comme ceci: xxx pré>

qui serait idéalement appelé par code quelque chose comme ceci: p>

error: ‘const _Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> 
>::address(__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference)
const [with _Tp = std::ios_base&(std::ios_base&); __gnu_cxx::new_allocator<
<template-parameter-1-1> >::const_pointer = std::ios_base& (*)(std::ios_base&);
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference =
std::ios_base& (&)(std::ios_base&)]’ cannot be overloaded


8 commentaires

Les manipulateurs sont souvent des fonctions. Il n'y a donc pas de classe de base commune pour eux.


Vous pouvez simplement définir vos propres cours d'emballage avec un flux opérateur qui applique le manipulateur et une base commune et l'utiliser pour votre paramètre?


Si vous voulez vraiment faire cela, vous devez probablement écrire votre fonction sous forme de modèle variadique (car différents manipulateurs ont des types différents, dont la plupart que vous ne connaissez pas). Même avec cela, cela ne sera pas trivial (par exemple, vous devrez probablement utiliser std :: lid pour gérer des manipulateurs qui prennent des arguments).


@Jerrycoffin Je pense qu'un modèle de fonction variadique fonctionnerait également.


@Angew: Je pensais que c'était ce que j'ai dit. C'est ce que je voulais dire, de toute façon ...


@Jerrycoffin Je crois que votre commentaire original a déclaré "Macro".


@Angew: hmm ... j'espère que non. Je suppose qu'une macro pourrait fonctionner aussi, mais ce n'est certainement pas ce que je conseillerais.


@JmetCalfe, après avoir écrit ma propre réponse, je vois maintenant que votre commentaire mentionne déjà les principaux ingrédients que j'ai utilisés. Alors oui, cela peut être fait pour travailler.


4 Réponses :


1
votes

Étant donné que les manipulateurs sont des fonctions, cela dépend de leur signature. Cela signifie que vous pouvez créer du vecteur de manipulateurs avec la même signature.

Par exemple: P>

#include <iomanip>
#include <vector>
#include <iostream>
#include <functional>

typedef std::function< std::ios_base& ( std::ios_base& ) > manipF;

std::vector< manipF > getManipulators()
{
    std::vector< manipF > m =
    {
        std::showpos,
        std::boolalpha,
        [] ( std::ios_base& )->std::ios_base&
        {
          std::cout << std::setprecision( 2 );
          return std::cout;
        }
    };
    return m;
}

int main()
{
  auto m = getManipulators();

  for ( auto& it : m )
  {
    it(std::cout);
  }
  std::cout<<"hi " << true << 5.55555f << std::endl;
}


1 commentaires

Votre SetPrecision Lambda utilise STD :: Cout, mais pas le paramètre donné. Je suppose que c'est parce que SetPreCision est défini en termes de flux basic_ [io] . Sinon, j'aime votre approche +1



6
votes

Vos manipulateurs peuvent avoir à peu près des types arbitraires. Vous devez donc utiliser des modèles pour les gérer. Afin d'y accéder en utilisant un pointeur ou une référence de type fixe, vous devrez utiliser une classe de base commune pour tous ces modèles. Ce type de polymorphisme ne fonctionne que pour les pointeurs et les références, mais vous voulez probablement une sémantique de valeur, en particulier pour les stocker dans des conteneurs. Donc, le moyen le plus simple consiste à laisser un partagé_ptr prendre en charge la gestion de la mémoire et en utilisant une autre classe pour masquer tous les détails laids de l'utilisateur.

Le résultat pourrait ressembler à ceci: < Pré> xxx

avec ceci, votre code fonctionne après de légères modifications apportées aux espaces de noms: xxx


1 commentaires

J'ai essayé à la fois la réponse acceptée et cela. Celui-ci fonctionne mieux, IMHO: les manipulateurs peuvent être stockés dans des vecteurs et les vecteurs de manipulateurs peuvent être stockés et transmis. Pour une raison quelconque, la réponse acceptée ne le permet pas.



7
votes

Un manipulateur de sortie est simplement n'importe quel type pour lequel os << m est défini pour certains basic_ostream instanciation. Un manipulateur peut être une fonction (sous réserve de l'opérateur <<< / code> des surcharges de basic_ostream ) mais il peut également être n'importe quel type qui définit son propre opérateur <<< / code>. En tant que tel, nous avons besoin d'effectuer une effacement de type pour capturer l'opérateur <<< / code> pour un approprié (code> basic_ostream instanciation; Le moyen le plus simple de le faire est avec std :: fonction et une lambda: xxx


4 commentaires

Approche intéressante, mais ne compile pas avec G ++ - 4.7.


@Oolafdietsche Clang-3.2 va bien; On dirait un bug avec la manipulation de G ++ de références universelles aux fonctions. Je vais voir si je peux trouver une solution de contournement.


@Oolafdietsche J'ai ajouté une surcharge pour g ++; Cela fonctionne maintenant avec G ++ - 4.7.2.


Merci! Merveilleusement simple et simple de le faire.



0
votes

Solution C ++ Standard 17, pourrait être basée sur STD :: Tuple. Voici le POC

int main()
{
// quick p.o.c.
auto ios_mask_keeper = [&](auto mask) {
    // keep tuple here
    static auto mask_ = mask;
    return mask_;
};

// make required ios mask and keep it
auto the_tuple = ios_mask_keeper(
    // please note we can mix ios formaters and iomanip-ulators
    std::make_tuple(std::boolalpha, std::fixed, std::setprecision(2) ) 
);

// apply iomanip's stored in a tuple
std::apply([&](auto & ...x) 
{
    // c++17 fold 
    (std::cout << ... << x);
}, the_tuple);

return 0;


0 commentaires