19

任意の関数をラップでき、関数自体と同じように呼び出すことができるラッパーを作成するにはどうすればよいですか?

これが必要な理由: 関数をラップし、関数自体と同じように動作し、さらにすべての呼び出しの累積時間をログに記録できる Timer オブジェクトが必要です。

シナリオは次のようになります。

// 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();

Timerこのクラスを書くにはどうすればよいですか?

私はこのようなことを試みています:

#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();
}

gprof(ところで、ランタイムをプロファイリングするためのツールやその他のツールを知っていますが、そのようなTimerオブジェクトを使用して、選択したいくつかの関数のランタイムをログに記録する方が私の目的には便利です。)

4

11 に答える 11

11

基本的に、現在のC++ではやりたいことは不可能です。ラップしたい関数のアリティがいくつあっても、次のようにオーバーロードする必要があります。

const reference
non-const reference

しかし、それでも完全に転送されるわけではありません(一部のエッジケースはまだ有効です)が、適切に機能するはずです。const参照に制限する場合は、これを使用できます(テストされていません)。

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_;
};

戻り型には、boostの関数型ライブラリを使用できることに注意してください。それで

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

純粋な値パラメーターを使用してオーバーロードすることもできます。参照によって何かを渡したい場合は、を使用しますboost::ref。これは実際にはかなり一般的な手法であり、特にそのようなパラメータを保存する場合はそうです(この手法は次の場合にも使用されboost::bindます)。

// 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);

または、上記で説明したように、constバージョンとnon-constバージョンの両方にこれらのオーバーロードを追加することもできます。適切なマクロを作成する方法については、Boost.Preprocessorを調べてください。

任意の呼び出し可能オブジェクト(関数だけでなく)を渡すことができるようにしたい場合は、結果タイプを取得する方法が必要になるため、全体がより困難になることに注意する必要があります(これはそれほど簡単ではありません)。C ++ 1xを使用すると、この種の作業がはるかに簡単になります。

于 2009-05-18T20:29:05.820 に答える
10

関数をラップする簡単な方法は次のとおりです。

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);
}
于 2009-05-18T20:13:36.833 に答える
7

これはテスト目的で必要であり、実際のプロキシまたはデコレーターとして使用するつもりはないと思います。したがって、operator() を使用する必要はなく、他のあまり便利ではない呼び出し方法を使用できます。

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);
}

したがって、関数の一部をテストするにtest_timeは、直接TimerWrapper構造ではなく関数のみを使用する必要があります

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;
}
于 2009-05-18T21:07:55.943 に答える
5

含めた std::tr1::function の実装を見れば、おそらく答えが見つかるかもしれません。

c++11 では、std:: 関数は可変個引数テンプレートで実装されます。このようなテンプレートを使用すると、タイマー クラスは次のようになります。

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
于 2013-05-29T02:31:16.927 に答える
3

Stroustrupは、をオーバーロードする関数ラッパー(注入)スキルを示していましたoperator->。重要なアイデアは次のとおりです。operator->ネイティブポインタタイプに一致するまで繰り返し呼び出されるためTimer::operator->、一時オブジェクトを返し、一時オブジェクトはそのポインタを返します。次に、次のことが起こります。

  1. temp objが作成されました(ctorが呼び出されました)。
  2. 呼び出されたターゲット関数。
  3. temp objが破棄されました(dtorが呼び出されました)。

また、ctorとdtor内に任意のコードを挿入できます。このような。

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) ;
}

実装と使用法はどちらも簡単です。

于 2009-05-19T05:10:32.373 に答える
1

コンパイラが可変長マクロをサポートしている場合は、次のようにします。

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_); } 

したがって、それを使用するには、次のようにします。

void test_me(int a, float b);

TIME_MACRO(test_me(a,b));

それはカフから外れており、戻り値の型を機能させるためにいじる必要があります (TIME_MACRO 呼び出しに型名を追加してから、一時変数を生成させる必要があると思います)。

于 2009-05-18T20:52:59.657 に答える
1

あなたが探しているものについては、私には本当に明確ではありません..しかし、与えられた例では、それは単純です:

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;
}

注: これは時間を秒単位で測定します。高精度のタイマーが必要な場合は、おそらく OS 固有の機能 ( Windows のGetTickCountQueryPerformanceCounterなど) を確認する必要があります。

汎用関数ラッパーが必要な場合は、非常に役立つ Boost.Bind を確認する必要があります

于 2009-05-18T19:51:53.867 に答える
1

任意の関数をラップして呼び出すことができるジェネリック クラスを作成しようとしている場合、大きな課題に直面しています。この場合、ファンクター (operator()) が double を返し、int をパラメーターとして受け取るようにする必要があります。次に、同じシグネチャですべての関数を呼び出すことができるクラスのファミリを作成しました。より多くのタイプの関数を追加したい場合、そのシグネチャのファンクターがさらに必要になります。

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

template<class Function>
class Timer {

public:

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

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

  }

};

編集:いくつかのスペルミス

于 2009-05-18T19:53:46.840 に答える
0

テンプレートの代わりに関数ポインタを使用して、これを行う方法は次のとおりです。

// 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;
};

関数への参照を取得せず、単なる関数ポインターを取得するように変更しました。使用法は同じままです:

  Timer t(&foo);
  // call function directly
  foo(i);
  // call it through the wrapper
  t(i);
于 2009-05-18T20:30:08.500 に答える
0

C++ では、関数は第一級市民であり、文字通り関数を値として渡すことができます。

int を取り、double を返したいので:

Timer(double (*pt2Function)(int input)) {...
于 2009-05-18T19:52:36.517 に答える