2016-04-08 11 views
0

Ich bin dabei, an einer Klasse zu arbeiten, und ich habe 3 Arrays in meiner Klasse, wo sie alle die gleichen Daten aber in einem anderen Format darstellen. Ich habe den Operator << überladen, der außerhalb meiner Klasse erklärt wird, die eine const-Referenz zu ihm nimmt und nicht als ein Freund dieser Klasse.C++ Überladen der I/O-Operatoren: Getting Past Ambiguity

SomeClass { 
public: 
    // Nameless Union - All 3 Arrays Are of The Same Exact Data Type 
    // And All 3 Arrays Have The Same Exact Size. This Nameless Union 
    // Uses The Same Memory Address For All 3 Arrays And Their Elements. 
    // So An Element Is Changed By One Array Type, It Is Expected And 
    // Accepted For It To Change The Others. This Is Not 3 Different 
    // Arrays, This Is Still 1 Array Of Size 256, Just Different 
    // Representations Or Different Ways To Access Them. 
    union { 
     int m_256[256]; 
     int m_16[16][16]; 
     int m_4[4][4][4][4]; 
    }; 

    SomeClass() { std::fill(std::begin(m_256), std::end(m_256), 0); } 

}; // SomeClass 

std::ostream& operator<<(std::ostream& out, const SomeClass& c) { 
    out << std::endl; 

    for (unsigned box = 0; box < 4; box++) { 
     for (unsigned slice = 0; slice < 4; slice++) { 
      for (unsigned row = 0; row < 4; row++) { 
       for (unsigned col = 0; col < 4; col++) { 
        out << "(" << box << "," << slice << "," << row << "," << col << ") = " 
         << c.m_4[box][slice][row][col] << std::endl; 
       } 
      } 
     } 
    } 
    return out; 
} // operator<< 

Dies ist, was ich derzeit habe. Was ich gerne tun könnte ist, auch die operator<< mit dieser Klasse zu verwenden, aber in der Lage zu sein, einen Weg zu unterscheiden, dieselben Daten in einem anderen Format anzuzeigen.

Ich weiß, dass Sie dies nicht tun können: durch Hinzufügen eines zweiten

std::ostream& operator<<(std::ostream& out, const SomeClass& c) { 
    out << std::endl; 
    for (unsigned i = 0; i < 16; i++) { 
     for (unsigned j = 0; j < 16; j++) { 
      out << "(" << i << "," << j << ") = " << c.m_16[i][j] << std::endl; 
     } 
    } 
    return out; 
} // operator<< 

Und eine dritte

std::ostream& operator<<(std::ostream& out, const SomeClass& c) { 
    out << std::endl; 
    for (unsigned u = 0; u < 256; u++) { 
     out << u << " = " << m_256[u] << std::endl; 
    } 
    return out; 
} // operator<< 

Aufgrund der Tatsache, dass dies nicht eindeutig ist. Aber ich hätte gerne die Funktionalität, um es in einem der 3 verschiedenen Formate anzuzeigen.

Gibt es irgendwelche Arbeiten eine Runden oder Lösungen für dieses Problem? Ich möchte in der Lage sein, das Klassenobjekt einfach an den stream-Operator zu senden, und diese Typen von Operatoren können keine zusätzlichen Parameter akzeptieren, da sie binäre Operatoren und keine Funktion sind.

+1

Geben Sie Überlastungen für die Union Elementtypen anstelle der Klasse; Verwenden Sie Namespaces und 'using'; Fügen Sie Ihrer Vereinigung eine 'enum' hinzu, um anzuzeigen, welches Mitglied aktiv ist, verwenden Sie [xalloc] (http://en.cppreference.com/w/cpp/io/ios_base/xalloc) und co, um einen benutzerdefinierten Manipulator zu erstellen, machen Sie drei Wrapper-Klassen mit ihren eigenen Überladungen ... es gibt viele Lösungen, aber letztendlich hängt es davon ab, wie die Operatoren aufgerufen werden sollen. – user657267

Antwort

4

Sie könnten einfach eine Adapterklasse verwenden, um die Ausgabe zu schreiben. Sie könnten einen Formatspezifizierer in den Konstruktor übergeben oder nach Typ unterscheiden. Zum Beispiel (Differenzierung nach Typ):

struct SomeClassAs256 { 
    SomeClass const& x_; 

    explicit(SomeClass const& x) : x_(x) {} 
}; 

Und haben dann eine Operator < < Implementierung:

ostream& operator<<(ostream& os, SomeClassAs256 const& x) { 
    ... 
    return os; 
} 

Und dann verwenden Sie es:

SomeClass x; 
... 
cout << SomeClassAs256(x) << endl; 
+0

Dies erfordert, dass der Adapter über die Interna weiß, also entweder verschachtelt oder ein Freund ist. –

+0

Ja, ich habe versucht, den Standardbaustein innerhalb der Implementierung der Klasse zu verlassen, so dass dieser Benutzer sich nicht darum kümmern muss. –

+0

@FrancisCugler. Meine Antwort ist eine Art Erweiterung zu diesem. Ich denke, Sie können davon profitieren. Sieh es dir an und kritisiere es wenn nötig. –

1

Sie können dies tun, indem Rückkehr ein Proxy (um die Anpassung zu ermöglichen) aus Ihrer Klasse mit make - Funktionen und mit: operator < < (ostrea m, SomeClass :: Proxy) als Ausgabeoperator. Ich werde an einem Beispielcode arbeiten.

Auf diese Weise müssen Sie auch die Interna Ihrer Klasse nicht verfügbar machen. Nichts falsch mit Operator < < Freunde machen ...

Beispiel:

#include <iostream> 
class SomeClass { 
    union { 
     int m_256[256]; 
     int m_16[16][16]; 
     int m_4[4][4][4][4]; 
    }; 
public: 

    SomeClass() { std::fill(std::begin(m_256), std::end(m_256), 0); } 

    struct x256 
    { 
     const SomeClass& c_; 
     explicit x256(const SomeClass& c): c_(c) 
     { 
     } 
    }; 
    struct x16 
    { 
     const SomeClass& c_; 
     explicit x16(const SomeClass& c): c_(c) 
     { 
     } 
    }; 

    struct x4 
    { 
     const SomeClass& c_; 
     explicit x4(const SomeClass& c): c_(c) 
     { 
     } 
    }; 

    x256 output265() const 
    { 
     return x256(*this); 
    } 

    x16 output16() const 
    { 
     return x16(*this); 
    } 

    x4 output4() const 
    { 
     return x4(*this); 
    } 

    friend std::ostream& operator<<(std::ostream& out, const SomeClass::x256& c) { 
     out << std::endl; 
     for (unsigned u = 0; u < 256; u++) { 
      out << u << " = " << c.c_.m_256[u] << std::endl; 
     } 
     return out; 
    } // operator<< 
    friend std::ostream& operator<<(std::ostream& out, const SomeClass::x16& c) { 
     //... 
     return out; 
    } // operator<< 
    friend std::ostream& operator<<(std::ostream& out, const SomeClass::x4& c) { 
     //... 
     return out; 
    } // operator<< 
}; // SomeClass 

void testSomeClass() 
{ 
    SomeClass someClass; 

    std::cout << someClass.output265() << someClass.output16() << someClass.output4() << std::endl; 
} 
2

Sie können nicht wirklich tun, zumindest nicht so einfach.

Damit haben Sie zwei Möglichkeiten: Verwenden Sie zwei Funktionen, die eine Zeichenfolge erstellen und zurückgibt, oder erstellen Sie eine Stream-Manipulator-Struktur.

eine Reihe von Formatierungsfunktionen zu schaffen, die einen String zurückgibt, die dann für die Ausgabe zu verwenden, ist einfach mit std::ostringstream:

std::string format1(SomeClass const& c) 
{ 
    std::ostringstream os; 
    os << whatever you want ... 
    return os.str(); 
} 

Erstellen Manipulator Strukturen ein wenig komplizierter ist, kann aber auch flexibler und leistungsfähiger sein :

class format1 
{ 
public: 
    format1(SomeClass const& c) 
     : c_(c) 
    {} 

    friend std::ostream& operator<<(std::ostream& os, 
            format1 const& fmt) 
    { 
     os << some formated output here using `fmt.c_`...; 
     return os; 
    } 

private: 
    SomeClass const& c_; 
}; 

In beiden Fällen können Sie es auf die gleiche Weise verwenden:

SomeClass c(...); 
std::cout << format1(c) << '\n'; 
+0

Ich denke, Ihre Antwort war die naheliegendste Antwort auf den Ansatz und die Lösung, die ich gefunden habe und die ich in meiner eigenen Antwort veröffentlicht habe. –

1

irgendeiner Weise zum Beispiel Mitglied Ihrer Klasse hinzufügen Ausgabeformat zu entscheiden:

public: 
    enum OutputStyle 
    { 
     M_256, 
     M_16, 
     M_4, 
    }; 
    OutputStyle style() const {return style_;} 

private: 
    mutable OutputStyle style_ = M_256; 

einen Weg hinzufügen für Operator Funktionsaufruf Beispiel zu geben, dass:

public: 
    SomeClass const& operator()(OutputStyle s) const 
    { 
     style_ = s; 
     return *this; 
    } 

die < < Operator Machen Sie es sich zu prüfen, :

std::ostream& operator<<(std::ostream& out, const SomeClass& c) 
{ 
    switch(c.style()) 
    { 
    default: 
     assert(!"defective operator <<"); 
    case SomeClass::M_256: 
     // ... output like M_256 here 
     break; 
    case SomeClass::M_16: 
     // ... output like M_16 here 
     break; 
    case SomeClass::M_4: 
     // ... output like M_4 here 
     break; 
    } 
} 

Dann können Sie es vor oder während der Ausgabe ändern:

SomeClass x; // <- has style M_256 

    x(SomeClass::M_16); 

    std::cout << "current:" << x << std::endl 
       << "with M_4:" << x(SomeClass::M_4) << std::endl; 
1

Nachdem einige gute Antworten zu sehen und unter der Berücksichtigung, dass die ostream Objekt und operator<< nicht, welche Art zu verwenden und herauszufinden, dass sie vom Benutzer entschieden würde, um die Informationen für ihre Bedürfnisse zu zeigen, ging ich ein andere Route; Aber die Lösung, die ich für meine gegenwärtigen Bedürfnisse entwickelt habe, wurde von all jenen unterstützt und inspiriert, die große Antworten auf dieses Problem hinterlassen haben.

Die Richtung, die ich nahm, war dies; Ich habe meiner Klasse direkt einen enum mit den 3 Typen hinzugefügt. Ich habe eine öffentliche Funktion hinzugefügt, die eine Zeichenfolge ausgibt und den Typ enum als Parameter verwendet. Ich fügte die ostream operator<< meiner Klasse hinzu und der Parameter, den es nimmt, ist ein typename zu meinen Klassen enum. Ich verwende die Out-Funktion, um meine 3 verschiedenen Möglichkeiten zu verzweigen, um die Informationen anzuzeigen. Nun kann ich in einem anderen Codeabschnitt, der dieses Objekt verwendet, die Instanz übergeben, die die out-Funktion aufruft, die einen String zurückgibt, indem sie den gewünschten Typ übergibt. Meine Klasse sieht nun wie folgt aus:

class SomeClass { 
public: 
    enum OutputType { x256, x16, x4 }; 

    union { 
     int m_256[256]; 
     int m_16[16][16]; 
     int m_4[4][4][4][4]; 
    }; 

    std::string out(OutputType type) const; 

    std::ostream& operator<<(typename SomeClass::OutputType type); 

}; // SomeClass 


std::ostream& SomeClass::operator<<(typename SomeClass::OutputType type) { 
    return std::ostream << out(type); 
} // operator<< 

std::string SomeClass::out(OutputType type) const { 
    std::ostringstream out; 
    out << std::endl; 

    switch(type) { 
     case: x256: { 
      // Print Format Here 
      break; 
     } 
     case x16: { 
      // Print Format Here 
      break; 
     } 
     case x4: { 
      // Print Format Here 
      break; 
     } 
     default: { 
      // Error Message 
      return out.str(); 
     } 
    } 
    return out.str(); 
} // out 

Ich weiß nicht, ob es mit meiner Klasse in meinem Projekt zu tun hat, ist ein template oder die Art und Weise der operator<< implementiert ist, aber ich mit typename in der Funktion zu verwenden, hatte Erklärung/Definition, damit es funktioniert, so sieht es in meinem Code aus, mit Ausnahme des Namens der Klasse.

template< class T> 
std::ostringstream& SomeClass<T>::operator<<(typename SomeClass<T>::Type type) { 
    // Code Here 
} 

Ich schätze all die Hilfe und Anregungen, die alle von Ihnen angeboten haben, und ich nehme, dass die Beratung, die zu Herz gegeben wurde, ich danke Ihnen allen.

bearbeiten

Nun, wenn ich dies ein wenig leichter auf den Benutzer machen wollte: Ich konnte meine out-Funktion auf den privaten Bereich bewegen; Schreiben Sie 3 Wrapper-out- oder print-Funktionen, die keine Parameter annehmen, aber sie setzen die Variable auf die private Methode.

+0

Dies funktioniert auch für meine Bedürfnisse, weil ich eine Logger-Klasse zu tun haben, die entweder eine Ostringstream, String oder char * in seine Protokollfunktion nimmt, und es kann entweder auf der Konsole oder eine Textdatei, und verfügt über 4 Formate Protokoll schreiben Nachrichten {Info, Warnung, Fehler und Konsole}. Jetzt kann ich diese Klasseninstanzmethoden bei Bedarf direkt an meinen Logger weitergeben. –