Besoin de contexte pour mettre à la disposition des opérateurs d'insertion de ostream de C

StackOverflow https://stackoverflow.com/questions/3114460

Question

Pour une API que je travaille, je veux permettre à l'utilisateur d'insérer des objets personnalisés dans un ostream, mais ces objets ont aucun sens eux-mêmes, et sont trop mémoire contraint d'inclure un pointeur supplémentaire ou une référence pour le contexte . (Pensez à des dizaines de millions de 16- / 32- / objets 48 bits dans un système embarqué avec une mémoire limitée.)

Supposons que l'utilisateur initialise le contexte sous-jacent, et regarde un de ces objets:

DDB ddb("xc5vlx330t");
Tilewire tw = ddb.lookUpTilewire("DSP_X34Y0", "DSP_IMUX_B5_3");
...
std::cout << complexDataStructure;

Dans un cadre tout à fait différent, peut-être niché loin du code explicite de l'utilisateur, nous pouvons avoir besoin d'insérer l'objet dans un ostream, avec ddb indisponible.

os << tw;

La valeur réelle est encapsulé par tw 97,594,974, mais la sortie souhaitée est la suivante:

DSP_IMUX_B5_3@[263,84] DSP "DSP_X34Y0" (1488@77406)

Pour que cela fonctionne, l'opérateur d'insertion approprié aurait besoin d'un accès à ddb, mais il ne peut pas compter sur des variables ou des fonctions statiques ou globales (pour des raisons multithreading). Ce que je comme à faire est de permettre à l'utilisateur de demander et d'utiliser un type d'emballage de flux de comme ceci:

ostream& wrappedCout = ddb.getWrappedOstream(std::cout);

La sous-classe retournée de ostream comprendrait une référence à ddb pour une utilisation par inséreuses de flux spéciaux qui en ont besoin, et une référence au flux-std::cout d'origine dans ce cas où il transmettrait toute sa production.

Malheureusement, les régimes d'héritage ou la composition que je suis venu avec sont en désordre au code vers le haut (pas une préoccupation énorme), et peut-être problématique pour l'utilisateur (une préoccupation beaucoup plus). Toutes les suggestions sur la façon de faire avec élégance ddb à la disposition des opérateurs d'insertion? Je suis au courant de façon marginale boost.Iostreams, mais pas sûr que cela va me aider.

Était-ce utile?

La solution

Ecrire un manipulateur de flux personnalisé qui stocke une référence à l'aide du mécanisme DdB iword / pword. Voici un exemple, vous devez ajouter le verrouillage autour de la carte iwork_indexes dans un programme multithread.

class dbb
{
public:
    explicit dbb(int value) : m_value(value) {}
    int value() const { return m_value; }
private:
    int m_value;
};

class dbb_reliant_type
{
public:
    dbb_reliant_type(const std::string& value) : m_value(value) {}
    const std::string& value() const { return m_value; }
private:
    std::string m_value;
};

typedef std::map<std::ostream*, int> iword_map;
iword_map iword_indexes;

inline int get_iword_index(std::ostream& os)
{
    iword_map::const_iterator index = iword_indexes.find(&os);

    if(index == iword_indexes.end())
    {
        std::pair<iword_map::iterator, bool> inserted = iword_indexes.insert(std::make_pair(&os, os.xalloc()));
        index = inserted.first;
    }

    return index->second;
}


inline std::ostream& operator<<(std::ostream& os, const dbb& value)
{
    const int index = get_iword_index(os);

    if(os.pword(index) == 0)
        os.pword(index) = &const_cast<dbb&>(value);

    return os;
}

std::ostream& operator<<(std::ostream& os, const dbb_reliant_type& value)
{
    const int index = get_iword_index(os);
    dbb* deebeebee = reinterpret_cast<dbb*>(os.pword(index));
    os << value.value() << "(" << deebeebee->value() << ")";
    return os;
}

int main(int, char**)
{
    dbb deebeebee(5);
    dbb_reliant_type variable("blah");
    std::cout << deebeebee << variable << std::endl;
    return 0;
}

Autres conseils

Je ne suis pas tout à fait sûr si je comprends ce qui peut être consulté à quel moment et ce qui peut et ne peut pas changer, mais .... pouvez-vous faire quelque chose comme ceci

struct TilewireFormatter {
    DDB *ddb;
    TilewireFormatter(DDB* d) : ddb(d) {}

    print(std::ostream& out, const Tilewire& obj) {
        // some formatting dependent on ddb
        out << obj;
    }
};

et remplacer out << tw; avec formatter.print(out, tw);

donc pas fournir toute sorte de << surcharge de l'opérateur pour Tilewire et passer une instance de TilewireFormatter autour qui est utilisé pour les formater en fonction de ce ddb est?

Au lieu de truquage autour et essayer de trouver un moyen de transmettre des informations contextuelles en utilisant l'opérateur d'insertion, je vous suggère de faire quelque chose comme une méthode d'impression comme choobablue suggère. C'est une belle et solution simple et tout colombophile est probablement plus d'ennuis que cela vaut la peine.

Je trouve aussi étrange que vous choisissez iostreams pour un système embarqué. Ils sont l'une des parties les plus pléthorique de la bibliothèque standard C ++ (non seulement par la mise en œuvre, mais par la conception) et si vous travaillez sur un système embarqué, vous pourriez tout aussi bien rouler votre propre solution de ce (toujours basé sur la conception de base de iostreams) et peut probablement le faire aussi rapidement que d'essayer d'utiliser de manière efficace et iostream sur plusieurs threads.

Je suis novice en la matière, donc en cas fournir ma propre réponse est dans la manière de me partager le crédit avec Gary, eh bien, Gary a ce que je venais trébuché sur quelques instants avant par la même référence: Stockage cours d'eau pour usage privé: iword, pword et xalloc

#include <iostream>

// statically request a storage spot that can be associated with any stream
const int iosDdbIndex = std::ios_base::xalloc();

class DDB {
public:
    // give the stream a pointer to ourselves
    void bless(std::ostream& os) { os.pword(iosDdbIndex) = this; }
    // provide a function that the insertion operator can access
    int getSomething(void) { return 50; }
};

class Tilewire {
    friend std::ostream& operator<< (std::ostream& os, Tilewire tilewire);
    // encapsulate a dummy value
    int m;
public:
    // construct the Tilewire
    Tilewire(int m) : m(m) {}
};

std::ostream& operator<< (std::ostream& os, Tilewire tilewire) {
    // look up the pointer to the DDB object
    DDB* ddbPtr = (DDB*) os.pword(iosDdbIndex);
    // insert normally, and prove that we can access the DDB object's methods
    return os << "Tilewire(" << tilewire.m << ") with DDB param " << ddbPtr->getSomething();
}

int main (int argc, char * const argv[]) {
    DDB ddb;
    ddb.bless(std::cout);
    std::cout << Tilewire(0) << std::endl;
    return 0;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top