Frage

Wie kann ich schreiben einen Wrapper, die jede Funktion wickeln kann und wie die Funktion selbst genannt werden?

Der Grund, warum ich brauche diese: Ich habe eine Timer-Objekt wollen, die eine Funktion und verhalten sich genauso wie die Funktion wickeln kann selbst, und es meldet sich die kumulierte Zeit aller Anrufe

.

Das Szenario würde wie folgt aussehen:

// a function whose runtime should be logged
double foo(int x) {
  // do something that takes some time ...
}

Timer timed_foo(&foo); // timed_foo is a wrapping fct obj
double a = timed_foo(3);
double b = timed_foo(2);
double c = timed_foo(5);
std::cout << "Elapsed: " << timed_foo.GetElapsedTime();

Wie kann ich diese Timer Klasse schreiben?

Ich versuche, so etwas wie folgt aus:

#include <tr1/functional>
using std::tr1::function;

template<class Function>
class Timer {

public:

  Timer(Function& fct)
  : fct_(fct) {}

  ??? operator()(???){
    // call the fct_,   
    // measure runtime and add to elapsed_time_
  }

  long GetElapsedTime() { return elapsed_time_; }

private:
  Function& fct_;
  long elapsed_time_;
};

int main(int argc, char** argv){
    typedef function<double(int)> MyFct;
    MyFct fct = &foo;
    Timer<MyFct> timed_foo(fct);
    double a = timed_foo(3);
    double b = timed_foo(2);
    double c = timed_foo(5);
    std::cout << "Elapsed: " << timed_foo.GetElapsedTime();
}

(BTW, ich kenne gprof und andere Werkzeuge zur Laufzeitprofilierung, aber mit einer solchen Timer Objekt die Laufzeit von wenigen ausgewählten Funktionen zu protokollieren bequemer für meine Zwecke.)

War es hilfreich?

Lösung

Hier ist eine einfach Art und Weise Funktionen zu wickeln.

template<typename T>
class Functor {
  T f;
public:
  Functor(T t){
      f = t;
  }
  T& operator()(){
    return f;
  }
};


int add(int a, int b)
{
  return a+b;
}

void testing()
{
  Functor<int (*)(int, int)> f(add);
  cout << f()(2,3);
}

Andere Tipps

Im Grunde, was Sie wollen ist in der aktuellen C unmöglich machen ++. Für eine beliebige Anzahl von arity der Funktion, die Sie wickeln möchten, müssen Sie durch Überlastung

const reference
non-const reference

Aber es ist dann immer noch nicht perfekt Weiterleitung (einige Grenzfälle stehen nach wie vor), aber es sollte auch vernünftig arbeiten. Wenn Sie sich auf const Referenzen begrenzen, können Sie mit diesem gehen (nicht getestet):

template<class Function>
class Timer {
    typedef typename boost::function_types
       ::result_type<Function>::type return_type;

public:

  Timer(Function fct)
  : fct_(fct) {}

// macro generating one overload
#define FN(Z, N, D) \
  BOOST_PP_EXPR_IF(N, template<BOOST_PP_ENUM_PARAMS(N, typename T)>) \
  return_type operator()(BOOST_PP_ENUM_BINARY_PARAMS(N, T, const& t)) { \
      /* some stuff here */ \
      fct_(ENUM_PARAMS(N, t)); \
  }

// generate overloads for up to 10 parameters
BOOST_PP_REPEAT(10, FN, ~)
#undef FN

  long GetElapsedTime() { return elapsed_time_; }

private:
  // void() -> void(*)()
  typename boost::decay<Function>::type fct_;
  long elapsed_time_;
};

Beachten Sie, dass für den Rückgabetyp, können Sie boost die Funktionstypen Bibliothek verwenden. Dann

Timer<void(int)> t(&foo);
t(10);

Sie können Überlastung auch reine Wertparameter verwendet wird, und dann, wenn Sie etwas durch Verweis übergeben möchten, verwenden Sie boost::ref. Das ist eigentlich eine ziemlich verbreitete Technik, vor allem, wenn solche Parameter werden gespeichert werden (diese Technik wird auch für boost::bind verwendet):

// if you want to have reference parameters:
void bar(int &i) { i = 10; }

Timer<void(int&)> f(&bar);
int a; 
f(boost::ref(a)); 
assert(a == 10);

Sie können auch diese Überlastungen sowohl const und nicht-const Versionen gehen und fügen Sie wie oben erläutert. Schauen Sie in Boost.Preprocessor , wie man schreibt die richtigen Makros.

Sie sollten sich bewusst sein, dass die ganze Sache schwieriger geworden, wenn Sie beliebige Callables in der Lage sein wollen, passieren (nicht nur Funktionen), da Sie einen Weg benötigen dann ihre Ergebnistyp zu bekommen (das ist gar nicht so einfach) . C ++ 1x wird diese Art von Sachen machen viel einfacher.

Ich nehme an, Sie brauchen diese für Testzwecke und wollen sie nicht als echte Proxies oder Dekorateure verwenden. So werden Sie nicht Operator verwenden müssen () und andere mehr weniger bequeme Methode des Anrufs verwenden können.

template <typename TFunction>
class TimerWrapper
{
public:
    TimerWrapper(TFunction function, clock_t& elapsedTime):
        call(function),
        startTime_(::clock()),
        elapsedTime_(elapsedTime)
    {
    }

    ~TimerWrapper()
    {
        const clock_t endTime_ = ::clock();
        const clock_t diff = (endTime_ - startTime_);
        elapsedTime_ += diff;
    }

    TFunction call;
private:
    const clock_t startTime_;
    clock_t& elapsedTime_;
};


template <typename TFunction>
TimerWrapper<TFunction> test_time(TFunction function, clock_t& elapsedTime)
{
    return TimerWrapper<TFunction>(function, elapsedTime);
}

So funktionieren einige von Ihnen testen Sie nur test_time Funktion und nicht die direkte TimerWrapper Struktur

verwenden sollten
int test1()
{
    std::cout << "test1\n";
    return 0;
}

void test2(int parameter)
{
    std::cout << "test2 with parameter " << parameter << "\n";
}

int main()
{
    clock_t elapsedTime = 0;
    test_time(test1, elapsedTime).call();
    test_time(test2, elapsedTime).call(20);
    double result = test_time(sqrt, elapsedTime).call(9.0);

    std::cout << "result = " << result << std::endl;
    std::cout << elapsedTime << std::endl;

    return 0;
}

Stroustrup hatte eine Funktion Wrapper (injaction) Fertigkeit mit Überlastung des operator-> demonstriert. Die Schlüsselidee ist: operator-> wird repeatly genannt, bis es eine native Zeigertyp erfüllt, so lassen Timer::operator-> ein temporäres Objekt zurück, und das temporäre Objekt seinen Zeiger zurück. Dann wird passieren folgende:

  1. Temp obj erstellt (Ctor genannt).
  2. Zielfunktion genannt.
  3. Temp obj destructed (dtor genannt).

Und Sie können einen beliebigen Code innerhalb des Ctor und der dtor injizieren. So wie hier.

template < class F >
class Holder {
public:
    Holder  (F v) : f(v) { std::cout << "Start!" << std::endl ; }
    ~Holder ()           { std::cout << "Stop!"  << std::endl ; }
    Holder* operator->() { return this ; }
    F f ;
} ;

template < class F >
class Timer {
public:
    Timer ( F v ) : f(v) {}
    Holder<F> operator->() { Holder<F> h(f) ; return h ; }
    F f ;
} ;

int foo ( int a, int b ) { std::cout << "foo()" << std::endl ; }

int main ()
{
    Timer<int(*)(int,int)> timer(foo) ;
    timer->f(1,2) ;
}

Die Umsetzung und die Nutzung sind beide einfach.

Sie können wahrscheinlich eine Antwort finden, wenn Sie bei der Implementierung von std aussehen :: tr1 :: Funktion, die Sie enthalten.

In c ++ 11 std :: Funktion wird mit variadische Vorlagen implementiert. Unter Verwendung eines solchen Vorlagen, um Ihre Timer-Klasse kann wie folgt aussehen

template<typename>
class Timer;

template<typename R, typename... T>
class Timer<R(T...)>
{
    typedef R (*function_type)(T...);

    function_type function;
public:
    Timer(function_type f)
    {
        function = f;
    }

    R operator() (T&&... a)
    {
        // timer starts here
        R r = function(std::forward<T>(a)...);
        // timer ends here
        return r;
    }
};

float some_function(int x, double y)
{
    return static_cast<float>( static_cast<double>(x) * y );
}


Timer<float(int,double)> timed_function(some_function); // create a timed function

float r = timed_function(3,6.0); // call the timed function

Eine Lösung mit Hilfe von Makros und Vorlagen: Zum Beispiel Sie wickeln mögen

double foo( double i ) { printf("foo %f\n",i); return i; }
double r = WRAP( foo( 10.1 ) );

Vor und nach dem Aufruf von foo () die Wrapper-Funktionen beginWrap () und endWrap () aufgerufen werden soll. (Mit endWrap () eine Template-Funktion).

void beginWrap() { printf("beginWrap()\n"); }
template <class T> T endWrap(const T& t) { printf("endWrap()\n"); return t; }

Das Makro

#define WRAP(f) endWrap( (beginWrap(), f) );

verwendet den Vorrang des Komma-Operator beginWrap (), um sicherzustellen, zuerst genannt wird. Das Ergebnis von f ist endWrap () übergeben, die gibt es einfach. So ist die Ausgabe:

beginWrap()
foo 10.100000
endWrap()

Und das Ergebnis r enthält 10.1.

Sie sind für eine große Herausforderung, wenn Sie eine generische Klasse zu schaffen suchen, und rufen Sie eine beliebige Funktion wickeln kann. In diesem Fall würden Sie den Funktors (der Betreiber ()) machen müssen, um einen int als Parameter zurückzukehren doppelt und nehmen. Dann haben Sie eine Familie von Klassen erstellt, die alle Funktionen mit derselben Signatur aufrufen können. Sobald Sie mehr Arten von Funktionen hinzufügen möchten, müssen Sie mehr functors dieser Signatur, z.B.

MyClass goo(double a, double b)
{
   // ..
}

template<class Function>
class Timer {

public:

  Timer(Function& fct)
  : fct_(fct) {}

  MyClass operator()(double a, double b){

  }

};

EDIT: Einige Rechtschreibfehler

Es ist nicht wirklich klar, mich für das, was Sie suchen .. jedoch für das gegebene Beispiel, es ist einfach:

void operator() (int x)
{
   clock_t start_time = ::clock();    // time before calling
   fct_(x);                           // call function
   clock_t end_time = ::clock();      // time when done

   elapsed_time_ += (end_time - start_time) / CLOCKS_PER_SEC;
}

Hinweis: Dies wird die Zeit in Sekunden messen. Wenn Sie hochpräzise Timer haben wollen, haben Sie wahrscheinlich OS-spezifische Funktionalität (wie GetTickCount oder Queryperformancecounter unter Windows).

Wenn Sie eine generische Funktion Wrapper haben wollen, sollten Sie einen Blick auf Boost.Bind die tremendeously helfen.

Wenn Ihr Compiler variadische Makros unterstützt, würde ich versuchen, diese:

class Timer {
  Timer();// when created notes start time
  ~ Timer();// when destroyed notes end time, computes elapsed time 
}

#define TIME_MACRO(fn, ...) { Timer t; fn(_VA_ARGS_); } 

Also, es zu benutzen, werden Sie dies tun:

void test_me(int a, float b);

TIME_MACRO(test_me(a,b));

Das ist aus dem Stehgreif, und Sie würden um spielen müssen Typen wieder zu arbeiten (ich denke, man müsste einen Typnamen zum TIME_MACRO Anruf hinzufügen und dann haben sie eine temporäre Variable erzeugen).

Hier ist, wie ich es tun würde, einen Funktionszeiger anstelle einer Vorlage:

// pointer to a function of the form:   double foo(int x);
typedef double  (*MyFunc) (int);


// your function
double foo (int x) {
  // do something
  return 1.5 * x;
}


class Timer {
 public:

  Timer (MyFunc ptr)
    : m_ptr (ptr)
  { }

  double operator() (int x) {
    return m_ptr (x);
  }

 private:
  MyFunc m_ptr;
};

Habe ich es nicht einen Verweis auf die Funktion zu übernehmen, sondern nur einen einfacher Funktionszeiger. Verbrauch bleibt gleich:

  Timer t(&foo);
  // call function directly
  foo(i);
  // call it through the wrapper
  t(i);

In C ++ Funktionen Bürger erster Klasse sind, können Sie buchstäblich eine Funktion als Wert übergeben.

Da Sie wollen, dass es einen int nehmen und zurück eine doppelte:

Timer(double (*pt2Function)(int input)) {...
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top