매크로를 사용하여 소스 파일 라인을 계산합니까?


15

C / C ++ 전처리기를 사용하여 소스 파일 내의 행을 매크로 또는 컴파일 가능한 시간 값으로 계산할 수 있습니까? 예 나는 대체 할 수있는 MAGIC1, MAGIC2그리고 MAGIC3사용할 때 다음에, 어떻게 든 값 4를 얻을 MAGIC3?

MAGIC1 // can be placed wherever you like before the relevant 
       // lines - either right before them, or in global scope etc.
foo(); MAGIC2
bar(); MAGIC2
baz(); MAGIC2
quux(); MAGIC2
// ... possibly a bunch of code here; not guaranteed to be in same scope ...
MAGIC3

노트:

  • 전 처리기 기능에 대한 컴파일러 별 확장은 허용되지만 바람직하지 않습니다.
  • 이것이 C와 대조적으로 C ++의 일부의 도움으로 만 가능하다면, 그것은 수용 가능하지만 바람직하지 않습니다 (즉, C에서 작동하는 것을 원합니다).
  • 분명히 이것은 외부 프로세서 스크립트를 통해 소스 파일을 실행하여 수행 할 수 있지만 이것이 내가 요구하는 것은 아닙니다.

6
있다 라는 매크로__LINE__ 현재 행 번호를 나타냅니다
ForceBru

2
를 검색 __COUNTER__및 / 또는 BOOST_PP_COUNTER?
KamilCuk

11
해결해야 할 실제 문제 는 무엇입니까 ? 왜 이것이 필요한가요?
일부 프로그래머 친구

1
합니까 도움을?
user1810087

1
@ PSkocik : 나는 컴파일 타임 상수로 사용할 수있는 것을 원한다 int arr[MAGIC4].
einpoklum

답변:


15

__LINE__선에 대한 정수를 제공 하는 전 처리기 매크로가 있습니다. 당신은 그것의 가치를 어떤 줄에서 취한 다음 나중의 줄에서 가져 와서 비교할 수 있습니다.

static const int BEFORE = __LINE__;
foo();
bar();
baz();
quux();
static const int AFTER = __LINE__;
static const int COUNT = AFTER - BEFORE - 1; // 4

소스 라인이 아닌 무언가의 발생을 계산하려면 GCC 및 MSVC와 같은 일부 컴파일러 __COUNTER__에서 지원하는 비표준 옵션이 될 수 있습니다 .

#define MAGIC2_2(c)
#define MAGIC2(c) MAGIC2_2(c)
static const int BEFORE = __COUNTER__;
void foo(); MAGIC2(__COUNTER__);
void bar(
    int multiple,
    float lines); MAGIC2(__COUNTER__);
void baz(); MAGIC2(__COUNTER__);
void quux(); MAGIC2(__COUNTER__);
static const int AFTER = __COUNTER__;
static const int COUNT = AFTER - BEFORE - 1; // 4

__COUNTER__소스 파일이나 일부 포함 된 헤더에서 이전에 사용되었을 수 있으므로 초기 값을 사용했습니다.

C ++ 대신 C에서는 상수 변수에 제한 enum이 있으므로 대신 a를 사용할 수 있습니다.

enum MyEnum
{
    FOO = COUNT // C: error: enumerator value for ‘FOO’ is not an integer constant
};

const를 enum다음 과 같이 바꾸십시오 .

enum {BEFORE = __LINE__};
foo();
bar();
baz();
quux();
enum { COUNT = __LINE__ - BEFORE - 1};
enum MyEnum
{
    FOO = COUNT // OK
};

나는 이것이 전처리기로 얻을 수있는 가장 가까운 것으로 생각합니다. 전처리 기는 하나의 패스이므로 나중에 계산 된 값을 백 패치 할 수 없지만 전역 변수 참조는 작동하며이를 최적화해야합니다. 정수 상수 표현식에서는 작동하지 않지만 카운트에 필요하지 않도록 코드를 구조화 할 수 있습니다.
PSkocik

2
__COUNTER__C 또는 C ++에서는 표준이 아닙니다. 특정 컴파일러에서 작동한다는 것을 알고 있으면 지정하십시오.
피터

@einpoklum 아니오, BEFORE그리고 AFTER매크로가 아닙니다
Alan Birtles

이 솔루션의 비 카운터 버전에는 문제가 있습니다. 이전과 이후는 소스 라인과 동일한 범위에서만 사용할 수 있습니다. 이것이 문제임을 반영하기 위해 "예"스 니펫을 수정했습니다.
einpoklum

1
@ user694733 진정한 질문은 [C ++]로 태그되었습니다. C 열거 상수의 경우 작동합니다.
Fire Lancer

9

OP의 요청은 매크로를 사용하는 것이지만 매크로를 사용하지 않는 다른 방법을 추가하고 싶습니다.

C ++ 20에는 source_location파일 이름, 줄 번호 및 함수 이름과 같은 소스 코드에 대한 특정 정보를 나타내는 클래스가 도입되었습니다 . 이 경우에는 쉽게 사용할 수 있습니다.

#include <iostream>
#include <source_location>

static constexpr auto line_number_start = std::source_location::current().line();
void foo();
void bar();
static constexpr auto line_number_end = std::source_location::current().line();

int main() {
    std::cout << line_number_end - line_number_start - 1 << std::endl; // 2

    return 0;
}

그리고 여기에 실례가 있습니다 .


매크로가 없으면 매크로보다 낫습니다. 그러나이 방법을 사용하면 계산 한 줄과 동일한 범위에서만 줄 수를 사용할 수 있습니다. 또한 source_locationC ++ 20에서 실험적입니까?
einpoklum

매크로가없는 솔루션이 매크로보다 훨씬 낫다는 데 동의합니다. source_location이제 공식적으로 C ++ 20의 일부입니다. 여기를 확인 하십시오 . godbolt.org 에서 이미 실험적이지 않은 방식으로 지원하는 gcc 컴파일러 버전을 찾을 수 없습니다 . 당신은 당신의 진술을 조금 더 설명해 주시겠습니까?- 제가 계산 한 줄과 같은 범위에서만 줄 수를 사용할 수 있습니까?
NutCracker

함수 내에 제안을 넣었다고 가정하십시오 (즉, 계산 된 줄은 선언이 아니라 호출입니다). 그것은 작동 -하지만 난 단지이 line_number_startline_number_end아무데도, 그 범위 내에서. 다른 곳에서 원한다면 런타임에 전달해야합니다.
einpoklum

표준이 여기에서 제공하는 예를 살펴보십시오 . 이것이 기본 인수라면 여전히 컴파일 타임의 일부입니다.
NutCracker

예, 그러나 line_number_end컴파일 타임에는 해당 범위를 벗어나는 것으로 보이지 않습니다 . 틀 렸으면 말해줘.
einpoklum

7

완벽을 기하기 위해 : MAGIC2매 줄마다 추가 하고 싶다면 다음을 사용할 수 있습니다 __COUNTER__.

#define MAGIC2 static_assert(__COUNTER__ + 1, "");

/* some */     MAGIC2
void source(); MAGIC2
void lines();  MAGIC2

constexpr int numberOfLines = __COUNTER__;

int main()
{
    return numberOfLines;
}

https://godbolt.org/z/i8fDLx (반환3 )

시작 값과 끝 값을 저장하여 재사용 할 수 있습니다. __COUNTER__ .

전반적으로 이것은 실제로 성가시다. 또한 전 처리기 지시문을 포함하거나 //주석으로 끝나는 줄을 계산할 수 없습니다 . 내가 사용하는 것 __LINE__대신에 다른 대답을 참조하십시오.


1
왜 사용 static_assert합니까?
idclev 463035818

1
소스 파일에 "9"를 입력하면 __COUNTER__다른 헤더 등에서 처음 사용할 때 0으로 가정 할 수 없습니다 .
Fire Lancer

값을 __COUNTER__두 번 사용해야하고 차이를
가져와야합니다

1
@ formerlyknownas_463035818 __COUNTER__자체로는 허용되지 않으며 무언가로 확장되거나 계산되지 않습니다 (100 %의 규칙을 기억할 수는 없습니다).
소방서

7

서로 다른 카운터를 사용할 수 있고 (다른 카운터를 사용하지 않는 한) 다른 카운터를 허용하는 다소 강력한 솔루션 __COUNTER__:

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)

#define COUNT_THIS_LINE static_assert(__COUNTER__ + 1, "");
#define START_COUNTING_LINES(count_name) \
  enum { EXPAND_THEN_CONCATENATE(count_name, _start) = __COUNTER__ };
#define FINISH_COUNTING_LINES(count_name) \
  enum { count_name = __COUNTER__ - EXPAND_THEN_CONCATENATE(count_name, _start) - 1 };

이것은 구현 세부 사항을 숨 깁니다 (매크로 안에 숨기는 경우에도 ...). @MaxLanghof의 답변을 일반화했습니다. 참고__COUNTER__ 우리는 수를 시작할 때 아닌 값을 가질 수있다.

사용 방법은 다음과 같습니다.

START_COUNTING_LINES(ze_count)

int hello(int x) {
    x++;
    /* some */     COUNT_THIS_LINE
    void source(); COUNT_THIS_LINE
    void lines();  COUNT_THIS_LINE
    return x;
}

FINISH_COUNTING_LINES(ze_count)

int main()
{
    return ze_count;
}

또한 이것은 전처리 기가를 지원하는 경우 유효한 C __COUNTER__입니다.

GodBolt에서 작동 .

내 카운터를 배치하여 - 당신은 C를 사용하는 경우 ++, 당신도 글로벌 네임 스페이스를 오염시키지에이 솔루션을 수정할 수 있습니다 namespace macro_based_line_counts { ... }, 또는 namespace detail등)


5

C 또는 C ++에서 (컴파일 타임) 배열 크기를 지정하려면 주석을 기반으로 할 수 있습니다

int array[]; //incomplete type
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
/*lines to be counted; may use array (though not sizeof(array)) */
/*...*/
int array[ __LINE__ - LINE0 ]; //complete the definition of int array[]

sizeof(array)중간 줄에 필요한 경우 정적 변수 참조로 대체 할 수 있으며 (정수 상수 표현식이 아닌 경우) 최적화 컴파일러는 동일하게 처리해야합니다 (정적 변수를 배치 할 필요가 없음) 메모리에)

int array[]; static int count;
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
//... possibly use count here
enum { LINEDIFF = __LINE__ - LINE0 }; 
int array[ LINEDIFF ]; /*complete the definition of int array[]*/ 
static int count = LINEDIFF; //fill the count in later

__COUNTER__A를 반대 기반 솔루션 (즉, 확장 가능한 경우) __LINE__기반의 하나는 동일하게 작동합니다.

constexprC ++에서 s뿐만 아니라 작동 enum하지만 enum일반 C에서도 작동합니다 (위의 솔루션은 일반 C 솔루션입니다).


이것은 라인 수의 사용이 계산 된 라인과 동일한 범위에있는 경우에만 작동합니다. 아이언 참고 문제가 될 수 있음을 강조하기 위해 질문을 약간 편집했습니다.
einpoklum

1
@einpoklum __COUNTER__기반 솔루션에도 문제가 있습니다. __COUNTER__적어도 당신이 사용하기 전에 마법 매크로가 유일한 사용자가되기를 바랍니다 __COUNTER__. 문제는 기본적으로 __COUNTER__/__LINE__전 처리기 기능 이라는 간단한 사실에 기인 하며 전처리 기는 한 번에 작동하므로 나중에 __COUNTER__/를 기반으로 정수 상수 표현식을 백 패치 할 수 없습니다 __LINE__. (적어도 C에서) 유일한 방법은 예를 들어 크기가없는 정방향 배열 선언을 사용하여 (불완전하게 형식화 된 배열 선언) 처음부터 필요를 피하는 것입니다.
PSkocik

1
레코드의 경우 \ 영향을 미치지 않습니다 __LINE__. 줄 바꿈이 있으면__LINE__ . 증가합니다. 예 1 , 예 2 .
맥스 랭 호프

@MaxLanghof 감사합니다. 그것을 몰랐다. 결정된.
PSkocik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.