2012-03-09 13 views
1

単純な関数タイマー/プロファイラを作成しようとしています。私の主な目的は、私が素早く、簡単に、控えめに、私がプロファイルしたい任意の関数呼び出しに追加できる関数を作成することです。任意の引数と戻り値を持つ関数のC++「単純な」関数コールバック

のでfoobar、およびbazをプロファイリングする例えば、私は、できるだけ元のコードに影響を与える、次の操作を行うことができますft(機能タイマー)機能を持つようにしたい:

ft(foo()); 
ft(bar(1, 2, 3)); 
int result = ft(baz()); 
string result = ft(qux("a", 2, 3.4)); 

注意を、 bazquxの場合は、ftから返される結果は、関数自身が返す値でなければなりません。

ftは、私は、任意の引数を持つファンクションのファンクションコールバックを行う方法を言及する複数のスレッドに遭遇して、それでも戻り値を処理する方法に言及することをそう多くなかった

タイミングの全てとのロギングなどを扱い。

これは最も近いもの:Variable number of arguments (va_list) with a function callback?ですが、値を返す関数だけでなく、void関数も処理しようとしています。

マクロの使用がこれを行う方法だと思っていますが、もっと良い方法があるかもしれません。

は、ここで(簡略)私の現在の微弱な試みです:

static Timer fTimer; 

class VoidFuncBase 
{ 
public: 
    virtual void operator()() = 0; 
}; 

class VoidFuncWrapper0 : public VoidFuncBase 
{ 
public: 
    typedef void (*func)(); 
    VoidFuncWrapper0(func fp) : fp_(fp) { } 
    void operator()() { fp_(); } 
private: 
    func fp_; 
}; 

template<typename P1> 
class VoidFuncWrapper1 : public VoidFuncBase 
{ 
public: 
    typedef void (*func)(const P1 &); 
    VoidFuncWrapper1(func fp, const P1 &p1) : fp_(fp), p1_(p1) { } 
    void operator()() { fp_(p1_); } 
private: 
    func fp_; 
    P1 p1_; 
}; 

template<typename P1, typename P2> 
class VoidFuncWrapper2 : public VoidFuncBase 
{ 
public: 
    typedef void (*func)(const P1 &, const P2 &); 
    VoidFuncWrapper2(func fp, const P1 &p1, const P2 &p2) 
    : fp_(fp), p1_(p1), p2_(p2) { } 
    void operator()() { fp_(p1_, p2_); } 
private: 
    func fp_; 
    P1 p1_; 
    P2 p2_; 
}; 

template<typename R> 
class FuncBase 
{ 
public: 
    virtual R operator()() = 0; 
}; 

template<typename R> 
class FuncWrapper0 : public FuncBase<R> 
{ 
public: 
    typedef R (*func)(); 
    FuncWrapper0(func fp) : fp_(fp) { } 
    R operator()() { return fp_(); } 
private: 
    func fp_; 
}; 

template<typename R, typename P1> 
class FuncWrapper1 : public FuncBase<R> 
{ 
public: 
    typedef R (*func)(const P1 &); 
    FuncWrapper1(func fp, const P1 &p1) : fp_(fp), p1_(p1) { } 
    R operator()() { return fp_(p1_); } 
private: 
    func fp_; 
    P1 p1_; 
}; 

template<typename R, typename P1, typename P2> 
class FuncWrapper2 : public FuncBase<R> 
{ 
public: 
    typedef R (*func)(const P1 &, const P2 &); 
    FuncWrapper2(func fp, const P1 &p1, const P2 &p2) 
    : fp_(fp), p1_(p1), p2_(p2) { } 
    R operator()() { return fp_(p1_, p2_); } 
private: 
    func fp_; 
    P1 p1_; 
    P2 p2_; 
}; 

template<typename R> 
R ft(FuncBase<R> func, std::string functionName) 
{ 
    unsigned long threadId = getThreadId(); 
    double startTimeMs = fTimer.getMilliseconds(); 

    R result = func(); 

    double duration = fTimer.getMilliseconds() - startTimeMs; 
    logf("%u %s took %fms", threadId, functionName.c_str(), duration); 

    return result; 
} 

void ft(VoidFuncBase func, std::string functionName, int logTimeoutMs) 
{ 
    unsigned long threadId = getThreadId(); 
    double startTimeMs = timer.getMilliseconds(); 

    func(); 

    double duration = timer.getMilliseconds() - startTimeMs; 
    logf("%u %s took %fms", threadId, functionName.c_str(), duration); 
} 

は現在、私は

"error: cannot declare parameter 'func' to be of abstract type 'VoidFuncBase'".

を取得しています。しかし、私はおそらく、とにかくこれで間違った方向に向かっています。

+0

std ::の機能があります。 C++ 03では、すべての種類のテンプレートクラスと関数を作成する必要があるので、首を痛めます。 –

+0

@VJovic:_All kinds_、非型テンプレートパラメータ、式テンプレート、型特性、不思議な繰り返しテンプレートパターン、...、? –

+0

C/C++と呼ばれる言語はありません。 –

答えて

3

バリデーションのテンプレートは、行く方法です!いずれにしても、関数を少し呼び出す方法を変更する必要があります。ただし、関数がオーバーロードされると面白くなります。呼び出し後の処理は、呼び出しの前に設定されたオブジェクトのデストラクタに入るだけであることに注意してください。

+0

OPは彼がC++ 11の機能を使用できるとは言いませんでした。 –

+0

@VJovic:そして、彼は特に言及しませんでした... C++ 11が現在の標準です。 –

+0

@VJovic:彼はそれもやっていないとは言わなかったし、C++ 11も現在の*標準である。これは、C++ 11で解決し易い特定の問題の1つで、さまざまな理由からC++ 03での痛みです。バリデーショナルテンプレートがなければ、すべてのオーバーロードを手動で行う必要があります(プリプロセッサを使用すると、書き込み/読み込み/保守の*シンプルなコードではなく、*プリミティブを助けることができます)。* rvalue-references *を使用しないと、あなたがcvの組み合わせの指数的な可能性を実装しようとしていない限り、元のコードの意味を完全に –

2

Deitmarの答えは、ソリューションの95%であったが、ここで私はそれが私の場合は仕事を得るために行われる調整されている。

  1. は、私は特定を追加するために必要な空戻り値の型とサポート機能を行うためにこの場合のテンプレート。
  2. 純粋な関数ではなくメソッドを呼び出す必要があるので、関数の参照方法を調整する必要がありました。
  3. おそらく2のために、関数が呼び出されているオブジェクトを渡す必要があることがわかりました。おそらく、これは実際には必要ではありませんが、それを働かせる唯一の方法です。
  4. 私は関数コールバックの後で、しかしftが返る前にいくつかの作業をしたいので、そのコードのいくつかの簡単な変更があります。
  5. それぞれの呼び出しで追加情報を渡す必要があります。つまり、わかりやすいログができるように、関数名の文字列を渡す必要があります。

ここに作業コード(要約)があります。

template <class C, typename R, typename... T, typename... A> 
R ft(C* obj, R (C::*func)(T...), std::string functionName, A&&... args) 
{ 
    double startTimeMs = fTimer.getMilliseconds(); 

    //extra pre-call work 

    R result = (obj->*func)(std::forward<A>(args)...); 

    //extra post-call work 

    double duration = fTimer.getMilliseconds() - startTimeMs; 
    logf("%s took %fms", functionName.c_str(), duration); 

    return result; 
} 

template <class C, typename... T, typename... A> 
void ft(C* obj, void (C::*func)(T...), std::string functionName, A&&... args) 
{ 
    double startTimeMs = fTimer.getMilliseconds(); 

    //extra pre-call work 

    (obj->*func)(std::forward<A>(args)...); 

    //extra post-call work 

    double duration = fTimer.getMilliseconds() - startTimeMs; 
    logf("%s took %fms", functionName.c_str(), duration); 
} 

関数呼び出しは、このように作られています:void Foo::bar(int arg);

ft(this, &Foo::bar, "Foo::bar", (3)); 

サブクラスからオブジェクトを扱う署名とthisオブジェクトのメソッドについて

、私がチェックしなければならなかったものオブジェクト型が関与しています。これを行う一般的な方法があるかもしれませんが、これはそうではありません!:

Shape* shape = getShape(); 
double area = 0.0; 
if(shape->getType() == SQUARE) 
{ 
    area = ft((Square*)shape, &Square::getArea, "Square::getArea"); 
} 
else if(shape->getType() == TRIANGLE) 
{ 
    area = ft((Triangle*)shape, &Triangle::getArea, "Triangle::getArea"); 

} 
else if(shape->getType() == CIRCLE) 
{ 
    area = ft((Circle*)shape, &Circle::getArea, "Circle::getArea"); 
} 
関連する問題