2008-11-23 7 views

Répondre

3

En effet, cela peut être fait, si la bibliothèque ou votre code fournit les surcharges pour operator<< et operator>> pour que cela fonctionne. Exemple simple sur la façon dont on pourrait le faire:

class transformer { 
    public: 
    virtual std::iostream& transform(std::iostream&) = 0; 
}; 

class noise : public transformer { 
    public: 
    virtual std::iostream& transform(std::iostream&) { 
     /* extract, change and put into again */ 
    } 
}; 


class echo : public transformer { 
    public: 
    virtual std::iostream& transform(std::iostream&) { 
     /* extract, change and put into again */ 
    } 
}; 

std::iostream& operator>>(std::iostream& io, transformer& ts) { 
    return ts.transform(io); 
} 

int main() { 
    std::stringstream data; 
    std::ifstream file("sound.wav"); 

    noise n; echo e; 

    data << file.rdbuf(); 
    data >> n >> e; 
    /* pipelined data now ready to be played back */ 
} 

Le problème avec l'aide d'un std::istream pur est que vous lire, mais vous n'auriez pas un moyen de mettre les données en arrière transformées pour l'étape suivante de la pipeline. Donc j'utilise std::iostream ici. Cette approche ne semble pas efficace, car chaque opérateur >> appel extrairait l'ensemble des données, et les réintégrerait.

Pour avoir un moyen plus performant de diffuser ce serait de créer un expression template. Cela signifie, alors que operator>> est appelé, vous ne faites pas transformantes encore, mais vous revenez types d'expression qui enregistrera la chaîne des opérations dans son type:

typedef transform< echo< noise<istream> > > pipeline; 
std::ifstream file("file.wav"); 
pipeline pipe(file); 
int byte = pipe.get(); 

serait un exemple de ce type. La structure des pipelines est décodée dans le type lui-même. Par conséquent, aucune fonction virtuelle n'est plus nécessaire dans le pipeline. Ce n'est pas construit à la demande, mais en utilisant typedef ici, pour montrer le principe. La programmation d'un tel système n'est pas facile. Donc, vous devriez probablement regarder dans les systèmes existants, comme Boost.Iostreams (voir ci-dessous). Pour vous donner une idée comment il ressemblerait, voici un exemple que je viens codé pour vous :):

#include <iostream> 

template<typename T> 
struct transformer { 
    int get() { 
     return static_cast<T*>(this)->read(); 
    } 
}; 

struct echot { 
    template<typename Chain> 
    struct chain : transformer< chain<Chain> > { 
     Chain c; 

     int read() { 
      return c.get() + 1; 
     } 

     chain(Chain const& c):c(c) { } 
    }; 
} echo; 

struct noiset { 
    template<typename Chain> 
    struct chain : transformer< chain<Chain> > { 
     Chain c; 

     int read() { 
      return c.get() * 2; 
     } 

     chain(Chain c):c(c) { } 
    }; 
} noise; 


template<typename T> 
typename T::template chain<std::istream&> operator>>(std::istream& is, T) { 
    return typename T::template chain<std::istream&>(is); 
} 

template<typename T, typename U> 
typename U::template chain<T> operator>>(T t, U u) { 
    return typename U::template chain<T>(t); 
} 

int main() { 
    std::cout << (std::cin >> echo >> noise).get() << std::endl; 
} 

Entrée 0 donne le code ASCII 48 ici, qui est ajouté 1, et multiplié par 2, donnant une valeur de 98, qui est également finalement sortie. Je pense que vous êtes d'accord que ce n'est pas un code qu'un débutant voudrait écrire. Alors peut-être regarder dans boost. Boost a une bibliothèque iostreams sophistiquée, qui peut faire beaucoup de choses. Je suis sûr que vous trouverez quelque chose de convenable à cela. Boost.Iostreams

+0

Le souci d'efficacité pourrait probablement être atténué par le passage d'une procuration d'une sorte plutôt que d'un flux de données lourdes. –

+0

il faut lire en utilisant paresseusement une expression template.that serait la façon de le faire i think.the modèle d'expression enregistrerait la structure de pipeline, et pourrait enchaîner les données à travers différents steps.but je pense qu'il vaut mieux pour lui montrer ce flux de base , au lieu de lui faire peur avec des modèles. –

+0

Les modèles sont bien :) En fait, j'ai déjà mis en œuvre quelque chose de très similaire. Ce qui m'a attiré à ces opérateurs était que vous pouviez utiliser le pipeline comme un modèle push ou pull (inversé pour la sortie). – Carl

2

Bien sûr que cela peut être fait. Il suffit de définir votre propre opérateur >> et l'opérateur < < ils font « la bonne chose » ...

Je le ferais donc j'avoir des méthodes dans la classe, comme toStream (ostream & os) et fromStream (istream &), puis définissez

istream& operator>> (istream& is, T& t) 
{ 
    t.fromStream(is); 
    return t; 
} 

ostream& operator<< (ostream& os, const T& t) 
{ 
    t.toStream(os); 
    return t; 
} 
2

Il n'y a pas besoin d'utiliser les flux pour déplacer les données. Vous pouvez créer vos propres classes pour le faire. Cela montre un exemple. Évidemment, les classes Decrypt et MetaDataParser peuvent être des classes de base abstraites avec des fonctions virtuelles permettant de brancher plusieurs fonctionnalités.

#include <iostream>               
#include <istream>               
using namespace std;               

class Data2                 
{                   
};                   

class Data3                 
{                   
};                   

class Decrypt                
{                   
};                   

class MetaDataParser               
{                   
};                   

Data2& operator>>(istream& in, Decrypt& decrypt)        
{                   
    return *new Data2;               
}                   

Data3& operator>>(Data2& d2, MetaDataParser& mdp)       
{                   
    return *new Data3;               
}                   

int main()                 
{                   
    Decrypt decrypt;               
    MetaDataParser mdp;              

    cin >> decrypt >> mdp;              
}                   
2

Juste pour être clair, avez-vous l'intention de dupliquer la sémantique de iostreams? Parce que vous semblez proposer quelque chose de différent. Dans l'exemple que vous donnez:

infile >> filedecrypter >> metadataparser >> audiodecoder >> effects >> soundplayer; 

En iostreams, le sens ici est de lire infile en filedecrypter jusqu'à ce que vous arrivez à des espaces, puis de infile en metadataparser jusqu'à plus des espaces, et ainsi de suite.

On dirait que vous proposez quelque chose de différent, où metadataparser lit de filedecrypter, audiodecoder de metadataparser, etc. Dans ce cas, je pense que la réponse à votre question doit être qualifié un peu.

Pouvez-vous utiliser l'opérateur >> pour exprimer cette construction? Probablement oui.

Pouvez-vous utiliser iostreams pour cela? Probablement pas.

Je vous suggère de préciser ce que cela signifie quand vous dites A >> B. exprimer Peut-être que les méthodes régulières plutôt que l'opérateur premier surcharge, et qui peut clarifier la question.

+0

OK. Je voulais dire quelque chose comme: Play (ApplyEffects (Decode (ExtractMetaData (Decrypt (BinaryFileRead ()))))); Je laisse de côté les types intermédiaires car ils ne sont pas importants. Pourrait être aussi simple qu'un tampon de caractères, ou des types d'objets totalement différents. – Carl

Questions connexes