Frage

Ich bin nur ungern ich sagen kann dies nicht herausfinden, aber ich kann nicht herausfinden. Ich habe gegoogelt und gesucht Stack-Überlauf, und gehe leer aus.

Die abstrakte und möglicherweise allzu vage Form der Frage ist, wie kann ich die Züge Muster zu instantiate Elementfunktionen verwenden [Update: I den falschen Begriff hier verwendet wird. Es sollte „Politik“ und nicht als seine „Traits“. Traits beschreibt bestehende Klassen. Politik verschreiben synthetische Klassen.] Die Frage kam auf, während eine Reihe von multivariaten Funktion Optimizern Modernisierung, dass ich vor mehr als 10 Jahren geschrieben habe.

Die Optimizern arbeiten alle durch einen geradlinigen Weg durch den Parameterraum der Auswahl von dem aktuellen besten Punkt (die „update“), dann auf dieser Linie einen besseren Punkt zu finden (die „line search“), dann wird die Prüfung auf der "done" Zustand, und wenn nicht getan, Iterieren.

Es gibt verschiedene Methoden für das Update durchführen, die Line-Suche, und möglicherweise für den done-Test, und andere Dinge. Mischen und Anpassen. Verschiedene Update Formeln erfordern unterschiedliche state-variablen Daten. Zum Beispiel erfordert die LMQN Update einen Vektor und die BFGS Update erfordert eine Matrix. Wenn Steigungen Auswertung billig ist, sollte die Linie-Such tun. Wenn nicht, sollte es nur Funktionsauswertungen verwenden. Einige Methoden erfordern eine genauere Line-Suche als andere. Das sind nur einige Beispiele.

Die Originalversion instanziiert mehrere der Kombinationen durch virtuelle Funktionen. Einige Züge werden durch die Einstellung Modus-Bits ausgewählt, die zur Laufzeit getestet werden. Yuck. Es wäre trivial die Züge zu definieren, mit ID # definieren und die Mitgliederfunktionen mit # ifdef ist und Makros. Aber das ist so vor zwanzig Jahren. Es nervt mich, dass ich keine Whizknall moderne Art und Weise herausfinden können.

Wenn es nur ein Merkmal ist, dass variierte, konnte ich das neugierig wiederkehrende Vorlage Muster verwenden. Aber ich sehe keine Möglichkeit, dass auf beliebige Kombinationen von Merkmalen zu erweitern.

Ich habe versucht, es zu tun boost::enable_if mit, etc .. Die speziellen Statusinformationen einfach waren. Ich schaffte es die Funktionen zu erledigen, sondern nur durch Nicht-Freund externe Funktionen zurückgreifen, die den this-Zeiger als Parameter haben. Ich habe nie auch herausgefunden, wie die Funktionen Freunde zu machen, viel weniger Mitgliederfunktionen. Der Compiler (VC ++ 2008) beschwerte sich immer, dass die Dinge nicht übereinstimmten. Ich würde schreien, „SFINAE, du Idiot!“ aber der Trottel bin wahrscheinlich ich.

Vielleicht Tag-Versand ist der Schlüssel. Ich habe nicht sehr tief in das bekommen haben.

Sicherlich ist es möglich, nicht wahr? Wenn ja, was ist am beste Praxis?

UPDATE: Hier ist ein weiterer Versuch, es zu erklären. Ich möchte, dass die Benutzer in der Lage sein, einen Auftrag (Manifest) ausfüllen für eine benutzerdefinierte Optimierer, so etwas wie ein chinesisches Menü Bestellung aus - eine aus der Spalte A, ein aus der Spalte B, etc .. Kellner, von Spalte A (updaters) , ich werde das BFGS Update mit Cholesky-Zersetzungs Sauce haben. Aus der Spalte B (line-Such), werde ich mit einem eta von 0,4 und ein rho von 1e-4 finden Sie in die kubische Interpolation Line-Suche. Etc ...

UPDATE: Okay, okay. Hier ist das Spiel-around, dass ich getan habe. Ich biete es ungern, weil ich es vermuten ein völlig verquer Ansatz. Es läuft in Ordnung unter vc ++ 2008.

#include <boost/utility.hpp>
#include <boost/type_traits/integral_constant.hpp>

namespace dj {

struct CBFGS {
    void bar() {printf("CBFGS::bar %d\n", data);}
    CBFGS(): data(1234){}
    int data;
};

template<class T>
struct is_CBFGS: boost::false_type{};

template<>
struct is_CBFGS<CBFGS>: boost::true_type{};

struct LMQN {LMQN(): data(54.321){}
    void bar() {printf("LMQN::bar %lf\n", data);}
    double data;
};

template<class T>
struct is_LMQN: boost::false_type{};

template<>
struct is_LMQN<LMQN> : boost::true_type{};

// "Order form"
struct default_optimizer_traits {
    typedef CBFGS update_type; // Selection from column A - updaters
};

template<class traits> class Optimizer;

template<class traits>
void foo(typename boost::enable_if<is_LMQN<typename traits::update_type>, 
         Optimizer<traits> >::type& self) 
{
    printf(" LMQN %lf\n", self.data);
}

template<class traits>
void foo(typename boost::enable_if<is_CBFGS<typename traits::update_type>,  
         Optimizer<traits> >::type& self) 
{
    printf("CBFGS %d\n", self.data);
}

template<class traits = default_optimizer_traits>
class Optimizer{
    friend typename traits::update_type;
    //friend void dj::foo<traits>(typename Optimizer<traits> & self); // How?
public:
    //void foo(void); // How???
    void foo() {
        dj::foo<traits>(*this);
    }
    void bar() {
        data.bar();
    }
//protected: // How?
    typedef typename traits::update_type update_type;
    update_type data;
};

} // namespace dj



int main() {
    dj::Optimizer<> opt;
    opt.foo();
    opt.bar();
    std::getchar();
    return 0;
}
War es hilfreich?

Lösung

Ich denke, Template-Spezialisierung ein Schritt in der richtigen Richtung. Das funktioniert nicht mit Funktionen arbeiten, so dass ich zu den Klassen eingeschaltet. Habe ich es, so dass es die Daten modifiziert. Ich bin nicht so auf den geschützten Mitglieder und Freunde und verkauft. Geschützte Mitglieder ohne Vererbung ist ein Geruch. Machen Sie es öffentlich oder Accessoren bieten und es privat machen.

template <typename>
struct foo;

template <>
struct foo<LMQN>
{
    template <typename OptimizerType>
    void func(OptimizerType& that)
    {
        printf(" LMQN %lf\n", that.data.data);
        that.data.data = 3.14;
    }
};

template <>
struct foo<CBFGS>
{
    template <typename OptimizerType>
    void func(OptimizerType& that)
    {
        printf(" CBFGS %lf\n", that.data.data);
    }
};

template<class traits = default_optimizer_traits>
class Optimizer{
public:
    typedef typename traits::update_type update_type;
    void foo() {
        dj::foo<typename traits::update_type>().func(*this);
    }
    void bar() {
        data.bar();
    }
    update_type data;
};

Andere Tipps

Eine einfache Lösung für nur Gebrauch tag-basierte Weiterleitung sein könnte, z.B. etwas wie folgt aus:

template<class traits>
void foo(Optimizer<traits>& self, const LMQN&) {
    printf(" LMQN %lf\n", self.data.data);
}

template<class traits>
void foo(Optimizer<traits>& self, const CBFGS&) {
    printf("CBFGS %d\n", self.data.data);
}

template<class traits = default_optimizer_traits>
class Optimizer {
    friend class traits::update_type;
    friend void dj::foo<traits>(Optimizer<traits>& self, 
                            const typename traits::update_type&);
public:
    void foo() {
        dj::foo<traits>(*this, typename traits::update_type());
    }
    void bar() {
        data.bar();
    }
protected:
    typedef typename traits::update_type update_type;
    update_type data;
};

Oder wenn Sie möchten bequem Gruppe mehrere Funktionen zusammen für verschiedene Züge, vielleicht etwas wie folgt aus:

template<class traits, class updater=typename traits::update_type> 
struct OptimizerImpl;

template<class traits>
struct OptimizerImpl<traits, LMQN> {
    static void foo(Optimizer<traits>& self) {
        printf(" LMQN %lf\n", self.data.data);
    }
};

template<class traits> 
struct OptimizerImpl<traits, CBFGS> {
    static void foo(Optimizer<traits>& self) {
        printf("CBFGS %d\n", self.data.data);
    }
};

template<class traits = default_optimizer_traits>
class Optimizer{
    friend class traits::update_type;
    friend struct OptimizerImpl<traits>;
public:
    void foo() {
        OptimizerImpl<traits>::foo(*this);
    }
    // ...
};

Es wäre trivial sein, um die Eigenschaften zu definieren, mit ID # definieren und die Mitgliederfunktionen mit # ifdef ist und Makros. Aber das ist so vor zwanzig Jahren.

Auch wenn es neue Methoden wert sein kann lernen, Makros sind oft der einfachste Weg, Dinge zu tun und nicht als Werkzeug, nur weil sie „alt“ verworfen werden sollten. Wenn Sie an der MPL in Boost- und das Buch auf TMP aussehen werden Sie vielen Gebrauch des Vorprozessors finden.

Hier ist, was ich (die OP) kam mit. Können Sie es kühler machen?

Die Hauptvorlage Optimizer-Klasse Erbt Politik-Implementierungsklassen. Es gibt diejenigen Klassen Zugriff auf die geschützten Mitglieder der Optimizer die sie benötigen. Eine weitere Optimierung Template-Klasse teilt sich das Manifest in seine Bestandteile und instanziiert die Hauptvorlage Optimizer.

#include <iostream>
#include <cstdio>

using std::cout;
using std::endl;

namespace dj {

// An updater.
struct CBFGS {
    CBFGS(int &protect_)
        : protect(protect_)
    {}

    void update () {
        cout << "CBFGS " << protect << endl;
    }   

    // Peek at optimizer's protected data
    int &protect;

};

// Another updater
struct LMQN {
    LMQN(int &protect_)
        : protect(protect_)
    {}

    void update () {
        cout << "LMQN " << protect << endl;
    }

    // Peek at optimizer's protected data
    int &protect;

};

// A line-searcher
struct cubic_line_search {
    cubic_line_search (int &protect2_)
        : protect2(protect2_)
    {}

    void line_search() {
        cout << "cubic_line_search  " << protect2 << endl;
    }   

    // Peek at optimizer's protected data
    int &protect2;

};

struct default_search_policies {
    typedef CBFGS update_type;
    typedef cubic_line_search line_search_type;
};

template<class Update, class LineSearch>
class Opt_base: Update, LineSearch
{
public:
    Opt_base()
        : protect(987654321) 
        , protect2(123456789)
        , Update(protect)
        , LineSearch(protect2)
    {}
    void minimize() {
        update();
        line_search();
    }

protected:
    int protect;
    int protect2;
};

template<class Search_Policies=default_search_policies>
class Optimizer: 
    public Opt_base<typename Search_Policies::update_type
                  , typename Search_Policies::line_search_type
                    >
{};

} // namespace dj



int main() {
    dj::Optimizer<> opt; // Use default search policies
    opt.minimize();

    struct my_search_policies {
        typedef dj::LMQN update_type;
        typedef dj::cubic_line_search line_search_type;
    };

    dj::Optimizer<my_search_policies> opt2;
    opt2.minimize();

    std::getchar();
    return 0;
}

Ihre Nutzung von enable_if ist etwas seltsam. Ich habe es nur 2 Möglichkeiten gesehen verwendet:

  • anstelle des Rückgabetyp
  • als zusätzliche Parameter (vorbelegt)

es für einen echten Parameter verwendet, kann das Chaos verursachen.

Wie auch immer, es ist auf jeden Fall möglich, es zu benutzen für Member-Funktionen:

template<class traits = default_optimizer_traits>
class Optimizer{
  typedef typename traits::update_type update_type;
public:

  typename boost::enable_if< is_LQMN<update_type> >::type
  foo()
  {
    // printf is unsafe, prefer the C++ way ;)
    std::cout << "LQMN: " << data << std::endl;
  }

  typename boost::enable_if< is_CBFGS<update_type> >::type
  foo()
  {
    std::cout << "CBFGS: " << data << std::endl;
  }


private:
  update_type data;
};

Beachten Sie, dass standardmäßig enable_if kehrt void, die als Rückgabetyp in den meisten Fällen sehr gut geeignet ist. Die „Parameter“ Syntax wird in der Regel für die Konstruktor Fälle reserviert, weil man dann keinen Rückgabetyp zur Verfügung haben, aber im Allgemeinen bevorzugt den Rückgabetyp zu verwenden, so dass es mit der Überladungsauflösung nicht Meddle des Fall ist.

Bearbeiten :

Die bisherige Lösung nicht funktioniert, wie in den Kommentaren zur Kenntnis genommen. Ich konnte keine Alternative mit enable_if finden, nur die „einfachen“ überliest Art und Weise:

namespace detail
{
  void foo_impl(const LMQN& data)
  {
    std::cout << "LMQN: " << data.data << std::endl;
  }

  void foo_impl(const CBFGS& data)
  {
    std::cout << "CBFGS: " << data.data << std::endl;
  }
} // namespace detail

template<class traits = default_optimizer_traits>
class Optimizer{
  typedef typename traits::update_type update_type;

public:
  void foo() { detail::foo_impl(data); }

private:
  update_type data;
};

Es ist nicht enable_if aber es macht den Job ohne Optimizer Interna jedem auszusetzen. KISS?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top