UnaryFunction in c++ 17



  • Hallo

    In meiner Klasse habe ich eine Add Methode deren ich einen Iterator auf begin und end übergebe:

    template<class _Iter>
    void Add(_Iter begin, _Iter end)
    {
        if( std::distance(begin, end) <= 0 )
            THROW_TRACEABLE_EXCEPTION("no elements between begin and end");
    
        Add(begin, end, iterator_traits<_Iter>::iterator_category(),
                            std::_Iter_cat_t<_Iter>(), *begin);
    }
    

    Und eine Add Methode die zusätlich eine UnaryFunction entgegen nimmt.

    template<class _Iter, class UnaryFunction>
    void Add(_Iter begin, _Iter end, UnaryFunction op)
    {
        if( std::distance(begin, end) <= 0 )
            THROW_TRACEABLE_EXCEPTION("no elements between begin and end");
    
        Add(begin, end, op, std::_Iter_cat_t<_Iter>(), op(*begin));
    }
    

    Beide werden dann an die Add Methode weitergeleitet bei der dann der explizite datentyp angegeben ist

    template<class _Iter, class UnaryFunction>
    void Add(_Iter begin, _Iter end, UnaryFunction uFunc, input_iterator_tag,
                     shared_ptr<MyVariable>)
    
    {
       ...
    }
    

    Möchte ich nun aus einer anderen map hier variablen über eine projektion hinzufügen habe ich das z.B. wie folgt getan:

    groupvar->Add(varMap.begin(), varMap.end(), stl::select2nd<VarMap::value_type>())
    

    seit c++ 11 kann ich die select2nd Methode auch über einen Lambda realisieren.

    groupVar->Add(varMap.begin(), varMap.end(), [](pair<string, shared_ptr<MyVariable>> pair) { return pair.second; });
    

    Nun habe ich auf c++ 17 umgestellt und die UnaryFunction gibt es nicht mehr.

    Hier gibt es nun andere Techniken bei denen ich aber noch nicht ganz durchgestiegen bin.
    z.b. enable_if im template

    Könnte mir jemand sagen wie die Add Methode nun elegant in c++ 17 implementiert werden könnte?



  • UnaryFunction? Das hat es ziemlich sicher nie gegeben.



  • Nein. Das ist ja nur der Name.

    Würde auch funktionieren mit

    template<class _Iter, class Irgendwas>
    void Add(_Iter begin, _Iter end, Irgendwas op)
    

  • Mod

    booster schrieb:

    template<class _Iter, class UnaryFunction>
    void Add(_Iter begin, _Iter end, UnaryFunction op)
    

    ...
    Nun habe ich auf c++ 17 umgestellt und die UnaryFunction gibt es nicht mehr.

    was?

    Nebenbei bemerkt sind Bezeichner, mit einem Unterstrich beginnen, gefolgt von einem Großbuchstaben reserviert.
    Mit std::_Iter_cat_t fasst du irgendetwas Internes der Standardbibliotheksimplementation an, davon solltest du die Finger lassen.



  • booster schrieb:

    Würde auch funktionieren mit

    angeblich funktioniert es ja nicht.



  • @manni66

    angeblich funktioniert es ja nicht.

    in c++ 11 hat es noch funktioniert in c++ 17 nicht mehr

    @camper

    Nun habe ich auf c++ 17 umgestellt und die UnaryFunction gibt es nicht mehr
    

    sorry. ok das war falsch ausgedrückt.

    aber bevor ich jetzt noch weiter was falsches sage, einfach die Frage

    Wie implementiere ich den am elegantesten eine Add Methode die einen iterator auf anfang und auf ende engegennimmt und das auf einen festgelegten typ.


  • Mod



  • Ich meine jetzt keine Addiermethode

    Sondern eine Methode meiner eigenen "List" Klasse, die es erlaubt Objekte eines bestimmten Typs hinzuzufügen.


  • Mod

    Also eine Art std::transform, aber als Bestandteil des Containers, so dass wegen Verzichts auf OutputIterator das Einfügen effizienter gestaltet werden kann, weil die Anzahl der Elemnte im voraus bestimmt werden kann? Und das ganze dann per Tagdispatching, um mit unterschiedlichen Iteratorkategorien effizient umzugehen?


  • Mod

    Dazu braucht es kein tag dispatching

    template <typename Iter, typename Container, typename UnaryF>
    void insert(Iter first, Iter last, Container& c, UnaryF f) {
      if constexpr (std::is_same_v<typename std::iterator_traits<Iter>::iterator_category,
                                   std::random_access_iterator_tag>)
        c.reserve(c.size() + std::distance(first, last));
    
      std::transform(first, last, f, std::back_inserter(c));
    }
    

    (Ungetestet)

    Edit: Warum kann man in diesem Textfeld nicht ordentlich per Hand einrücken?



  • Ok.

    Ich prüfe aktuell noch ob in der übergebenen Elementen begin bis end keine doppelten einträge vorhanden sind bevor ich die meinem eigentlichen Container hinzufüge:

    Iter temp(begin);
    for (; temp != end; ++temp)
    {
        auto var = func(*temp);
        const VarMap::iterator pos = _varMap.find(var->GetName());
        if (pos != _varMap.end())
        {
            map.clear();
            break;
        }
    
        Add(&map, var);
    }
    
    if (!map.empty())
    {
        _varMap.insert(map.begin(), map.end());
    }
    

    wie mache ich dass dann im Beispiel von Arcoth?



  • kommt drauf an was man unter tag dispatching versteht. 😉

    Aber könnte ich das nun nicht auch in einem enable_if direkt im template machen?


  • Mod

    booster schrieb:

    kommt drauf an was man unter tag dispatching versteht. 😉

    Ich verstehe darunter das dispatchen (Aufrufen) einer von mehreren Funktionen anhand von Tags.

    booster schrieb:

    Ich prüfe aktuell noch ob in der übergebenen Elementen begin bis end keine doppelten einträge vorhanden sind bevor ich die meinem eigentlichen Container hinzufüge:

    Das deutet auf einen Container hin, der prinzipiell keine doppelten Einträge haben darf. Warum also keine Hashmap/Hashset?



  • kommt drauf an was man unter tag dispatching versteht

    Das war eigentlich auf die Antwort von camper bezogen. Die er anscheinend wieder gelöscht hat. Die stand zuvor noch zwischen meinen beiden letzten Beiträgen.

    Warum also keine Hashmap/Hashset?

    also z.B. unordered_map?

    Wenn ich hier den back inserter verwende und alle elemente von begin und end reinpacke und hier wären doppelte Einträge vorhanden, würde eine Exception geworfen? Ist das richtig?



  • template <typename Iter, typename Container, typename UnaryF>
    void insert(Iter first, Iter last, Container& c, UnaryF f) {
      if constexpr (std::is_same_v<typename std::iterator_traits<Iter>::iterator_category,
                                   std::random_access_iterator_tag>)
        c.reserve(c.size() + std::distance(first, last));
    
      std::transform(first, last, f, std::back_inserter(c));
    }
    

    Darf ich zu dem Code nochmals Fragen stellen.
    1. Wo wird den hier nun geprüft ob der Inhalt des Inhals vom typ shared_ptr<MyVariable> ist?

    2. wieso wird im true fall im if constexpr nur das c.reserve. ausgeführt?
    Das hinzufügen passiert dann immer oder wie?



  • Hallo ich versuche mal meine Fragen selber zu beantworten

    1. Wo wird den hier nun geprüft ob der Inhalt des Inhals vom typ shared_ptr<MyVariable> ist?

    Gar nirgends. Da ging wohl die eigentliche Frage des Threads unter. 😞

    2. wieso wird im true fall im if constexpr nur das c.reserve. ausgeführt?

    Speicher nur reservieren wenn random access iterator !?

    Jetzt bleibt aber immer noch die Frage. Wie prüfe ich ob der Iterator auf elemente von shared_ptr<MyVariable> zeigt?



  • Warum also keine Hashmap/Hashset?

    nochmals dazu. Verwende aktuell eine std::map die hat ja auch unique key.

    Mein Vorhaben ist aber elemente die über first und last rein kommen zu überprüfen. Hier könnten ja doppelte Einträge vorhanden sein. Ich möchte alle elemente nur hinzufügen wenn kein doppelter eintrag vorhanden ist.

    würde ich nur

    std::transform(first, last, func, std::back_inserter(container));
    

    verwenden erhalte ich erst dann eine exception wenn ich einen doppelten Eintrage hinzufüge. Die bis dato eingefügten wären dann ja schon drin!


  • Mod

    booster schrieb:

    Warum also keine Hashmap/Hashset?

    nochmals dazu. Verwende aktuell eine std::map die hat ja auch unique key.

    ...

    würde ich nur

    std::transform(first, last, func, std::back_inserter(container));
    

    verwenden erhalte ich erst dann eine exception wenn ich einen doppelten Eintrage hinzufüge. Die bis dato eingefügten wären dann ja schon drin!

    Nope. Wenn dein container std::map ist, wird das gar nicht erst compilieren mangels push_back-Memberfunktion in std::map.

    Und wieso schon wieder std::map, wenn der Key doch schon Bestandteil des gemapten Types ist ? Für solche Fälle ist std::set da.

    Ich verstehe auch das Problem noch nicht. Wir haben hier keinen vollständigen minimalen Code und keine Fehlermeldung.


  • Mod

    booster schrieb:

    kommt drauf an was man unter tag dispatching versteht

    Das war eigentlich auf die Antwort von camper bezogen. Die er anscheinend wieder gelöscht hat. Die stand zuvor noch zwischen meinen beiden letzten Beiträgen.

    Die steht dort, wo sie immer stand.

    booster schrieb:

    Warum also keine Hashmap/Hashset?

    nochmals dazu. Verwende aktuell eine std::map die hat ja auch unique key.

    Bei näherer Betrachtung scheint ein set angebracht zu sein, da sich der Key innerhalb des Objekts befindet. Falls die Berechnung des Keys also nicht zu teuer ist, kannst du den key comparator entsprechend implementieren.

    booster schrieb:

    Die bis dato eingefügten wären dann ja schon drin!

    Natürlich. Ich dachte, es ginge darum, Duplikate zu entfernen, nicht, sie gar nicht erst als Input zuzulassen?

    Dass die eingefügten Elemente vom Typ Blabla sind... was macht das für einen Unterschied? Wenn das Einfügen kompiliert, sind sie kompatibel mit dem Elementtyp deines Containers.



  • @ camper.

    Nope. Wenn dein container std::map ist, wird das gar nicht erst compilieren mangels push_back-Memberfunktion in std::map.

    Ja das tut es nicht. Ich wollte ja auch gar keine allgemeine insert Funktion für irgendeinen beliebigen Container haben.

    Und wieso schon wieder std::map, wenn der Key doch schon Bestandteil des gemapten Types ist ? Für solche Fälle ist std::set da

    1. std::map wurde halt bisher im Code verwendet. Ich kann den Code nicht komplett auf einmal umstellen irgendwo muss ich anfangen.
    2. wenn ich nun set habe dann ist aber der gesamte typ der key wie suche ich dann nach einem element mit bestimmten Namen effizient.

    Ich verstehe auch das Problem noch nicht. Wir haben hier keinen vollständigen minimalen Code und keine Fehlermeldung.

    Nun ja weil ich noch nicht genau weiß wie implementieren
    Aber ich versuche mal ein Beispiel. Kommt...


Anmelden zum Antworten