컴파일 타임에 메소드가 정확히 한 곳에서 호출되는지 확인하십시오.


15

컴파일 타임에 메소드가 정확히 한 곳에서 호출되는지 확인할 수 있는지 궁금합니다.

함수가 두 번 이상 (예 : 루프에서) 호출되면 괜찮지 만 두 개의 개별 루프에서 호출해서는 안됩니다.

이것은 두 부분으로 나눌 수 있습니다, 나는 또한 중 일부를 커버 솔루션에 관심이 있어요 :
(A) 방법을 확인이 적어도 하나 개의 장소에 호출
에 호출 (b)의 방법을 위해 가장 한 곳에서

코드 구조를 완전히 제어 할 수 있으며 동일한 아이디어를 얻는 다른 관용구도 환영합니다.

// class.h

class MyClass {
  public:
    void my_method();
}

다음은 컴파일해서는 안됩니다 (전화하지 마십시오)

#include "class.h"

int main() {
  MyClass my_class;
}

다음은 컴파일하지 않아야합니다 (여러 곳에서 호출)

#include "class.h"

int main() {
  MyClass my_class;
  my_class.my_method();
  while(true) {
    my_class.my_method();
  }
}

다음은 컴파일해야합니다 (정확히 한 곳에서 호출).

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}

2
그것을 방법으로 만들지 마십시오. 한 곳에 코드를 인라인으로 넣습니다.
user207421

2
클로저 유형이 각 람다마다 고유하기 때문에 람다 (빈 람다 일 수 있음) 로이 작업을 수행 할 수도 있다고 생각합니다. 다시 말하지만, 이것은 런타임 오류이지만 요청한 것이 아닙니다. 해결하려는 문제에 대한 자세한 내용을 제공하면 해결 방법을 찾을 수 있습니다.
인디애나 Kernick

2
비표준 __COUNTER__매크로를 사용하여이 작업을 수행 할 수 있습니다 . 같은 것 static_assert(__COUNTER__ == 0); my_class.my_method();. 그러나 각 변환 단위에서 카운터가 재설정되므로 변환 단위당 한 번만 함수가 호출되는지 확인할 수 있습니다.
인디애나 Kernick

4
왜 그렇게 하시겠습니까? 함수 포인트의 일부는 여러 곳에서 호출 할 수 있다는 것입니다.
Chipster

4
왜 이런 일 을하고 싶은지 설명 해야합니다. 아마도 당신이 요구하는 솔루션이 실제 목표를 달성하는 데 최선이 아닐 수도 있습니다.
tenfour

답변:


6

낮은 기술 접근 ​​방식 :

코드 구조 (빌드 시스템 포함)를 제어 할 수 있기 때문에 여기 저 기술 솔루션이 있습니다.

  • 함수 이름을 충분히 고유하게 만드십시오
  • 코드에서 함수 이름을 grep하십시오. 선언과 정의가 같은 위치에 있다고 가정하면 두 번 예상됩니다.
    • 헤더에 한 번
    • 단일 통화 사이트에서 한 번

또는

정말로 정말로 C ++로 해결하고 싶다면 시도해보십시오.

  • 컴파일 타임 카운터를 사용하여 컴파일 단위 내의 사용 횟수를 파악하십시오.
  • 헤더가 여러 컴파일 단위에 포함 된 경우 함수가 ODR을 위반하는지 확인하십시오.

그러나 컴파일 타임 카운터는 검은 마술입니다 (예 : I 및 TMP를 정말로 좋아합니다).이 목적으로 ODR 위반을 강제하는 것은 비슷한 부두처럼 보입니다 (적어도 연결에 실패한 테스트 사례가 필요합니다).

그러나 진지하게 :

이러지 마 무엇을하든 래퍼 함수를 ​​사용하면 거의 노력하지 않고 왜곡 할 수 있습니다.

auto call_my_method(MyClass& o)
{
   return o.my_method();
}

MyClass::my_method()랩퍼에서만 호출됩니다. 다른 사람들은 컴파일러에 의해 인라인 된 래퍼를 호출합니다.

다른 사람들이 제안한 바와 같이 : 당신이하려는 일을 설명한다면 훨씬 도움이 될 수 있습니다.


1

다음은 작동 할 수있는 대략적인 아이디어입니다 (댓글이 너무 길지만 좋은 SO 답변에는 불완전합니다).

템플릿 인스턴스화 수를 계산 / 확인하여이를 달성 할 수 있습니다.
템플릿은 사용시 에만 인스턴스화됩니다 .

마찬가지로 템플릿 메소드 / 함수 본문은 호출되지 않은 경우 구문 분석 또는 컴파일 또는 링크되지 않습니다 (유효한 구문 보장 이외). 이것은 그들의 몸 안에서 어떤 인스턴스도 만들어지지 않았다는 것을 의미합니다).

전역 인스턴스화 횟수와 정적 어설 션을 유지하는 템플릿 (또는 과거 인스턴스화를 확인하는 다른 TMP 메커니즘)을 유지 관리하는 템플릿을 만들 수 있습니다.


"전역"인스턴스화 수는 현재 컴파일 단위에 국한됩니다.
atomsymbol

1

C 전처리 기와 GNU 인라인 어셈블리를 사용하여이 질문에 대한 부분적인 해결책이 있습니다.

헤더 파일 a.h:

struct A {
    // Do not call this method directly, use the macro below to call it
    int _method_vUcaJB5NKSD3upQ(int i, int j);
};

// Use inline assembly to ensure that this macro is used at most once
#define method_vUcaJB5NKSD3upQ(args...) \
    _method_vUcaJB5NKSD3upQ(args); \
    asm (".global once_vUcaJB5NKSD3upQ; once_vUcaJB5NKSD3upQ:");

구현 파일 a.cc:

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

int A::_method_vUcaJB5NKSD3upQ(int i, int j) { return i+j+5; }

// Ensure that the macro is used at least once
extern "C" const char once_vUcaJB5NKSD3upQ;
static const char get_vUcaJB5NKSD3upQ = once_vUcaJB5NKSD3upQ;

int main() {
    A a;
    for(int i=0; i<7; i++) {
        // Use a separate statement to call the method
        // (terminated by a semicolon, it cannot be a sub-expression)
        auto x = a.method_vUcaJB5NKSD3upQ(2, 3);
        std::cout << x << std::endl;
    }
    return 0;
}

이 솔루션은 프로그램이 래퍼 매크로를 사용하지 않고 밑줄로 시작하는 메소드를 직접 호출하는 것을 막지 않는다는 점에서 부분적입니다.


0

constexpr 카운터를 사용하십시오. 다른 질문에 구현이 있습니다.


1
이 방법이 잘못된 것 같습니다.
Chipster

해당 질문에 대한 답변에서 참조 된 open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 문제 는 이것이 표준의 버그이며 잘못된 형식으로 작성되어야 함을 나타냅니다.
SD57

적어도 아직 형성되지 않았습니까?
Chipster

아직 틀린 형태가 아니라면 최대한 많은 사람들이 가능한 빨리 사용해야하므로이 사용 사례를 지원해야합니다!
user1685095
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.