2017-09-29 3 views
1

Wir wechseln zu C++ 11, aber immer noch ein paar Monate entfernt. Bitte zögern Sie nicht, C++ 11 Antworten zu geben, aber wir sind neugierig, ob es eine nicht-hässliche Möglichkeit gibt, es in C++ 98/03 zu tun.Functor zum Anwenden einer willkürlichen Elementfunktion auf einen Container von Containern mit Objekten


Ein Mitarbeiter kam zu mir mit dem Code ähnelt:

typedef void(B::*fptr_t)(); 

void A::my_foreach(uint idx, fptr_t fptr) { 
    for(iter_type iter = asdf[idx].begin(); iter != asdf[idx].end(); ++iter) 
    iter->second->*fptr(); 
} 

void A::get(uint idx) { my_foreach(idx, &B::get); } 
void A::get(uint idx) { my_foreach(idx, &B::get); } 

... zu fragen, ob es eine bessere Art und Weise war, auf die ich antwortete mit:

void A::get(uint idx) { 
    std::for_each(asdf[idx].begin() 
       , asdf[idx].end() 
       , boost::bind(&B::get, _1)); 
} 
void A::put(uint idx) { 
    std::for_each(asdf[idx].begin() 
       , asdf[idx].end() 
       , boost::bind(&B::put, _1)); 
} 

. .. aber das ließ mich immer noch fühlen, als könnte mehr getan werden, um Code-Duplikation zu vermeiden. Die allgemeinen Lösungen, die mir aufgetreten sind, sind:

  1. Re-Faktor der Code so die beteiligten Klassen weniger Verantwortung haben, und wickeln Sie den Behälter (n) in Dekorateur/Proxy-Objekte, die einen Teil der benötigten Funktionalität.
  2. Generisches apply to my container Elementfunktion, die die beabsichtigte Funktion als ein Argument annimmt (das heißt, Anrufer die Funktion bereitstellen muss)
  3. Stellen zwei private Elementfunktionen: Die Funktion, die unter (2) und einer einen Funktors zurückzukehren es zu nennen , dann öffentliche Funktorobjekte, die im Konstruktor initialisiert wurden.
  4. Verwenden boost::bind die ganze Sache zu wickeln, einen Funktor machen, die wie folgt verwendet werden:

.

functor_t get = my_binder(&B::get); 
functor_t put = my_binder(&B::put); 

... wo my_binder ein Objekt erzeugen würde, die die gleichen Operationen ausführen können, aber das hat sich rasch wegen hässlich wie ausgeführt werden viele Funktionen tatsächlich in dem obigen Code (operator[], X::begin, X::end, something for applying the function ptr to each element of the container).

Gibt es einen anderen/besseren Weg vor C++ 11, oder sind diese Lösungen ziemlich viel verfügbar?

+0

Sie können ersetzen 'boost :: bind (& B :: get, _1) 'mit' std :: mem_fun (& B :: get) ', und dann schreibe eine Funktion, die eine' std :: mem_fun_t ' – Praetorian

Antwort

1

std::mem_fn ist das Bindemittel aus Schritt 4.

Sie nicht ganz Erwähnung die Tatsache, dass Ihr Behälter (z std::map) sein assoziatives aussieht. Ich würde vorschlagen, Boost-Bereich mit für Bereiche der Anpassung an die abgebildeten Werte zu projizieren:

Live On Coliru

#include <boost/range/algorithm.hpp> 
#include <boost/range/adaptors.hpp> 

using namespace boost::adaptors; 

template<typename T> struct Container { 
    template <typename F> void my_foreach(uint idx, F&& f) { 
     boost::for_each(_data[idx] | map_values, std::forward<F>(f)); 
    } 

    std::map<int, std::map<int, T> > _data; 
}; 

#include <iostream> 
struct B { 
    B(std::string s) : _s(s) {} 
    void foo()  const { std::cout << "foo(" << _s << ")\n"; } 
    void bar(int i) const { std::cout << "bar(" << _s << ", " << i << ")\n"; } 
    private: 
    std::string _s; 
}; 

#include <functional> 
int main() { 
    Container<B> c; 
    c._data = { // just some demo data 
     { 1, { { 100, {"hundred"} }, { 1000, {"thousand"} }, { 1000000, {"million"} }, } }, 
     { 2, { { 100, {"hundert"} }, { 1000, {"tausend"} }, { 1000000, {"miljon"} }, } }, 
     { 3, { { 100, {"cent"} }, { 1000, {"mille"} }, { 1000000, {"million"} }, } }, 
     { 4, { { 100, {"honderd"} }, { 1000, {"duizen"} }, { 1000000, {"miljoen"} }, } }, 
    }; 

    c.my_foreach(3, std::mem_fn(&B::foo)); 
    c.my_foreach(1, [](B const& b) { b.bar(42); }); 
} 

Druck:

foo(cent) 
foo(mille) 
foo(million) 
bar(hundred, 42) 
bar(thousand, 42) 
bar(million, 42) 
+0

Im eigentlichen Code denke ich, es ist' Vektor > ', aber die Frage an diesem Punkt ist akademischer, also habe ich den genauen Behältertyp weggelassen, weil ich mehr daran interessiert bin, etwas neues zu lernen, als eine Lösung zu diesem bestimmten Problem (er benutzt es mit doppeltem Code zu vermeiden Sie es, viel Zeit damit zu verbringen, dies zu erforschen). –

+0

Nun, Ihr Code hat 'iter-> second', also macht es nur Sinn, wenn 'TheType' wie' std :: pair 'ist. Sie können dann immer noch den 'map_values' Adapter verwenden (denke ich). Wenn das alles irrelevant wäre, dann wäre vielleicht "std :: mem_fn" die Antwort. – sehe

+0

Guter Punkt, ich hatte nicht wirklich zu tief über die verwendeten Container nachgedacht. Ich werde mit mem_fn basteln und eine bessere Vorstellung davon bekommen, was es macht. –

Verwandte Themen