2012-07-02 3 views
0

나는 사용자에게 통계를 전달하는 프로젝트를 진행 중이다. Dog라는 클래스를 만들었습니다. 그리고 몇 가지 기능이 있습니다. 말하기, 우는 소리, 실행하기, 가져 오기 등통화 통계를 추적하는 방법은 무엇입니까? C++

각 함수가 몇 번이나 호출되었는지 알려주고 싶습니다. 또한 생성자 호출과 소멸자 호출에도 관심이 있습니다.

나는 모든 기능을 정의한 헤더 파일을 가지고 있으며, 그 다음에는 그것을 구현하는 별도의 .cc 파일이있다. 내 질문은 각 함수가 호출 된 횟수를 추적하는 방법이 있습니까?

"print"라는 함수가 "통계"를 가져 와서 표준 출력으로 출력합니다. 정적 정수를 클래스 자체의 일부로 사용하여 여러 정수를 선언하여 해당 사항을 추적 할 것을 고려했습니다. 나는 컴파일러가 정수의 복사본을 만들고 최소값으로 초기화한다는 것을 알고있다. 그리고 나서 .cc 함수의 정수를 증가시킬 것이다.

또한 정적 변수가 .cc의 전역 변수로 간주됩니다. 어떤 방법이 더 쉬울까요? 아니면 이것을 할 수있는 더 좋은 방법이 있습니까?

도움을 주시면 대단히 감사하겠습니다.

+0

메서드를 호출 할 때마다 증가하는 로컬 변수가있을 수 있습니다. – Brendan

답변

4

정적 멤버 변수를 사용하는 것이 좋습니다. 그러나 컴파일러는 "정수의 복사본을 만들고 최소값으로 초기화"하지 않습니다. .cc 파일에 각 파일에 대한 정의를 제공하고 거기에 0으로 초기화해야합니다. (C++ 11을 사용한다면 상황은 조금 다르지만 기본 아이디어는 같습니다.)

정적 멤버 대신 정적 글로벌 변수를 사용할 이유가 없습니다.

foo.h :

class Foo { 
    static int countCtor_; 
    static int countDtor_; 
    static int countprint_: 
    Foo(); 
    ~Foo(); 
    static void print(); 
}; 

foo.cc :

#include <iostream> 
#include "foo.h" 

int Foo::countCtor_ = 0; 
int Foo::countDtor_ = 0; 
int Foo::countprint_ = 0; 

Foo::Foo() { 
    ++countCtor_; 
    // Something here 
} 
Foo::~Foo() { 
    ++countDtor_; 
    // Something here 
} 
void Foo::print() { 
    ++countprint_; 
    std::cout << "Ctor: " << countCtor_ << "\n" 
      << "Dtor: " << countDtor_ << "\n" 
      << "print: " << countprint_ << "\n"; 
} 

당신이 기능을 많이 가지고하지만, 관련된 반복 조금 성가신-이 실수를하는 것은 매우 쉽다이다 ++ countBaz_를 할 때 ++ countBar_ (특히 상용구를 복사하여 붙여 넣는 경우) 정적 테이블과 카운트 [__ FUNC__]를 증가시키는 매크로와 같이 좀 더 멋진 것이 필요할 수 있으므로 사용하면됩니다. 각 기능에서 똑같은 라인. 이처럼 :

foo.H :

#include <map> 
class Foo { 
    static std::map<const char*, int> counts_; 
    Foo(); 
    ~Foo(); 
    void print(); 
}; 

foo.cc : 위 예 코드

#include <iostream> 
#include "foo.h" 

std::map<const char *, int> Foo::counts_; 

#define INC_COUNT_() do { ++counts_[__FUNC__]; } while (0) 

Foo::Foo() { 
    INC_COUNT_(); 
    // Something here 
} 
Foo::~Foo() { 
    INC_COUNT_(); 
    // Something here 
} 
void Foo::print() { 
    INC_COUNT_(); 
    for (std::map<const char *, int>::const_iterator it = counts_.begin(); 
     it != counts_.end(); ++it) { 
    std::cout << it->first << ": " << it->second << "\n"; 
    } 
} 

는 __func__가 자리한다. 유감스럽게도, 그 자리에서 사용할 수있는 표준 호환 값은 없습니다. 대부분의 컴파일러에는 __func__, __FUNC__, __FUNCTION__, __FUNCSIG__ 및 __PRETTY_FUNCTION__의 하위 집합이 있습니다. 그러나 이들 중 어느 것도 C++ 03의 표준입니다. C++ 11은 __func__을 표준화하지만, "구현 정의 문자열"로만 사용됩니다.이 문자열은 유용하거나 고유하지 않을 수도 있습니다. 그 위에 다른 컴파일러에서 값이 달라집니다. 또한, 일부는 더 재미있게 만들기 위해 식별자가 아닌 매크로 일 수 있습니다. ":"

당신이, 당신은 문자열 (__ func__) + 같은 것을 사용할 수 있습니다 C++ 11에서, 진정한 휴대용 코드를 원하는 경우

+ STRINGIZE (__ LINE __)를 -이 다소 추한 것, 그러나 적어도 각 기능은있을 것이다 고유 한 이름. 그리고 C++ 03에는 이에 상응하는 것이 없습니다. "portable enough"만 있으면, 사용하는 모든 컴파일러의 문서를 참조하거나 autoconf와 같은 것을 의지하십시오.

+0

첫 번째 foo.cc의 서식이 왜 엉망인지 알아낼 수 없습니다. 나는 각 공백 라인에 4 개의 공백을 넣고 그것을 시도했다. 실제 빈 라인과 어느 쪽의 방법 으로든, 코드의 일부는 코드로 나타나지 않는다 ... – abarnert

+1

나는'__FUNC__'을 알지 못한다. 그러나 나는 당신을 의미한다고 생각하고있다. 표준'__func__'입니다. 그러나 함수의 데코 레이팅 된 이름을 사용하여 오버로드 된 함수 이름을 별도로 계산해야합니다. – jxh

+0

죄송합니다, 당신 말이 맞습니다; MSVC 확장과 gcc 확장이 모두 있다고해서 그것이 표준이라는 것을 의미하지는 않습니다. 나는 대답을 편집 할 것이다. – abarnert

1

로컬에 static 지역 변수를 넣을 수 있습니다. 이러한 변수는 논리적으로 클래스에 연결되지 않았으므로 더 깨끗하게 보이므로 클래스를 만들 수 없습니다.

또한 작업을 단순화하기위한 매크로가있을 수 있습니다. 나는 일반적으로 매크로를 사용하지 않는 것이 좋습니다,하지만 적절한 사용하는 것 같아 :

#define DEFINE_COUNTER \ 
    static int noCalls = 0; \ 
    noCalls++; 


void foo() 
{ 
    DEFINE_COUNTER 
} 
+1

이런 식으로 일을한다면 그의 인쇄 기능이 통계를 가져 오는 방법은 무엇입니까? – abarnert

+0

@abarnert 여러 옵션 - 매크로에 의해 수정 된 전역'map', 숫자로 매개 변수로 함수를 호출하십시오 ... –

+0

매크로로 수정 된 글로벌 맵이있는 경우 함수 로컬 통계는 무엇입니까? – abarnert

3

당신은 당신을 위해 이러한 호출을 계산합니다 표준 프로파일 링 도구를 사용할 수없는 이유가 있습니까? gprof과 같은 것?

그렇지 않으면 정적 정수가 좋습니다.

+0

이 답변의 전반부는 좋지만 후반부는 아닙니다. 정적 정수를 메소드 자체에두면 그가 설명하는 "인쇄"기능 (또는 디버거 외부에서 값에 액세스하는 다른 방법)을 만들 수 없습니다. – abarnert

+0

@abarnert 감사합니다. 편집 됨. – NominSim

1

옵저버 패턴 또는 메소드 호출 차단을 구현하는 라이브러리를 사용하십시오. this list에서 하나를 선택하거나 Vitamin과 같은 것을 사용할 수 있습니다.

2

이러한 통계 프로그램에서 모든 시간을 추적 할 가정하면, 당신은 당신의 함수 이름의 unordered_map을 사용할 수

std::unordered_map<const char *, unsigned> stats; 

void foo() { 
    // use __FUNCDNAME__ for MSVC 
    ++stats[__PRETTY_FUNCTION__]; 
    //... 
} 

컴파일러 특정 함수 이름 지정자의 사용은 장식 기능을 얻기 위해 의도적으로이 이름. 이는 오버로드 된 함수 이름이 별도의 함수로 계산되도록하기위한 것입니다.

이 기술을 사용하면 다른 것을 생각하지 않고도 쉽게 새 기능을 추가 할 수 있지만 해시 충돌이있는 경우 약간의 추가 비용이 부과됩니다 (stats 맵의 크기를 더 크게 조정하여 해결할 수 있음). 키가 포인터 타입이기 때문에 해시로 계산 된 해시가 없습니다. 포인터 값 자체를 해시로 사용하기 때문입니다.

이것은 프로파일 링을위한 일회용 코드 일 경우 먼저 플랫폼에서 사용할 수있는 코드 프로파일 링 도구를 사용해보십시오.

+0

+1. 마이크로 최적화 효율성에 초점을 맞추기는 다소 잘못되었지만 여전히 비슷한 대답보다 훨씬 쉽고 읽기 쉽습니다. – abarnert

+0

@abarnert : upvote에 감사드립니다. 질문의 목적이 단순한 정보 수집 운동 인 경우 효율성이 중요하지 않아야한다는 것에 동의합니다. 그러나 성능에 민감한 응용 프로그램에서 솔루션이 깨끗하고 효율적이면 항상 좋습니다. 감사합니다 – jxh

+0

사실 맵의 예상되는 작은 값 N에 대해 unordered_map보다 맵이 더 효율적일 수 있다고 생각합니다. 로그 10은 문자열 해싱보다 빠르며 3 * 10은 해시의 기본 시작 크기보다 작을 수 있습니다. 표. 특히 작성 및 읽기가 더 빠르며, unordered_map을 볼 때 왜 그것이 맵 위에 선택되었는지 궁금합니다. 또한 컴파일러, 실행 및 코드의 사소한 변경 사항을 통해 훨씬 더 예측할 수 있습니다. 그리고 무엇보다도 C++ 98에서 작동합니다. – abarnert

관련 문제