__FILE__ 매크로는 전체 경로를 보여줍니다


164

__FILE__C에서 사용 가능한 표준 사전 정의 매크로 는 파일의 전체 경로를 보여줍니다. 경로를 단축 할 수있는 방법이 있습니까? 대신에

/full/path/to/file.c

내가 참조

to/file.c

또는

file.c

18
전 처리기 전용 솔루션을 찾는 것이 정말 좋습니다. 문자열 작업을 기반으로 한 제안이 런타임에 실행 될까봐 두렵습니다.
cdleonard

9
gcc를 사용하고 있기 때문에 __FILE__명령 줄에서 전달하는 파일 이름을 변경하여 내용을 변경할 수 있다고 생각합니다 . 대신 gcc /full/path/to/file.c에을 시도하십시오 cd /full/path/to; gcc file.c; cd -;. 물론 포함 경로 또는 출력 파일 위치에 gcc의 현재 디렉토리에 의존하는 경우에는 그보다 약간 더 많은 것이 있습니다. 편집 : GCC의 문서는 전체 경로의 제안 하지 입력 파일 이름 인수,하지만 내가 Cygwin에서의 GCC 4.5.3에 대한보고 있어요 게 아니에요. 따라서 Linux에서도 시도해 볼 수 있습니다.
Steve Jessop

4
GCC 4.5.1 (특히 arm-none-eabi 용으로 제작)은 명령 줄에서 파일 이름의 정확한 텍스트를 사용합니다. 필자의 경우 현재 디렉토리를 적절한 위치 (프로젝트 파일의 위치?)에 배치하거나 상대 경로를 사용하는 대신 모든 파일 이름을 사용하여 GCC를 호출하는 것은 IDE의 결함이었습니다. "현재"디렉토리가 실제로 GUI 앱을위한 위치를 설명하는 것과 관련된 불편 함 때문에 많은 IDE가 (특히 Windows에서) 그렇게한다고 생각합니다.
RBerteig

3
@SteveJessop-이 의견을 읽으시기 바랍니다. 나는 __FILE__인쇄 된 것으로 보이는 상황 ../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c이 있으며 gcc 가이 파일이 표시된 것처럼 상대적으로 위치하도록 파일을 컴파일 한 위치를 알고 싶습니다.
Chan Kim

1
이 질문은 연결된 질문의 속임수가 아닙니다. 하나의 경우, 연결된 것은 C ++에 관한 것이며, 그 결과로 C ++ 매크로에 대한 답을 찾을 수 있습니다. 둘째, 매크로 솔루션을 요구하는 OP의 질문에는 아무것도 없습니다. 그것은 엄숙하게 문제를 지적하고 개방형 질문을합니다.
Falken 교수

답변:


166

시험

#include <string.h>

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

Windows의 경우 '/'대신 '\\'를 사용하십시오.


13
/Windows에서 유효한 경로 구분 기호입니다.
Hans Passant

11
/CreateFile () 등에 전달 된 파일 이름의 유효한 경로 구분 기호입니다. 그러나 이것이 명령 프롬프트에서 인수 /로 사용되는 전통 (CPM에서 상속)이 있기 때문에 항상 Windows의 어느 곳에서나 사용할 수있는 것은 아닙니다 /. 그러나 품질 도구에서 분할 파일 이름에주의 것 모두 사용 관리하는 사람에 대한 문제를 방지하기 위해 슬래시와 백 슬래시 문자 /.
RBerteig

5
@AmigableClarkKant, 아니요 두 개의 구분 기호를 동일한 파일 이름으로 혼합 할 수 없습니다.
RBerteig

2
당신의 플랫폼이 그것을 지원한다면 char* fileName = basename(__FILE__); 그것은 확실히 리눅스와 OS X에 있지만, Windows에 대해 모른다.
JeremyP

14
이로 단축 될 수 있습니다 strrchr("/" __FILE__, '/') + 1. "/"를 앞에 __FILE__붙임으로써 strrchr는 무언가를 찾도록 보장되므로 조건부? :는 더 이상 필요하지 않습니다.
ɲeuroburɳ

54

cmake를 사용하는 경우 팁이 있습니다. 보낸 사람 : http://public.kitware.com/pipermail/cmake/2013-January/053117.html

팁을 복사 하여이 페이지에 모두 있습니다.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst
  ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")

GNU make를 사용한다면, 이것을 자신의 makefile로 확장 할 수 없었습니다. 예를 들어 다음과 같은 줄이있을 수 있습니다.

CXX_FLAGS+=-D__FILENAME__='\"$(subst $(SOURCE_PREFIX)/,,$(abspath $<))\"'"

$(SOURCE_PREFIX)제거 할 접두사는 어디 입니까?

그런 다음 __FILENAME__대신 사용하십시오 __FILE__.


11
헤더 파일에서 참조 되는 FILE에 대해서는 작동하지 않습니다 .
Baiyan Huang

2
@BaiyanHuang에 동의하지만 의견이 명확하지는 않습니다. __FILE__단순한 전 처리기 기호가 아니며, 현재 파일의 변경 사항은 현재 파일 (헤더 또는 소스 모듈)의 이름을 내보내는 데 자주 사용됩니다. 여기 __FILENAME__에는 가장 바깥 쪽 소스 만있을 것입니다.
19 분 59 초

3
이 답변의 솔루션은 Bourne 쉘 이스케이프를 사용하기 때문에 이식성이 없습니다. CMake를 사용하여 깨끗하고 휴대 가능한 방식으로 구현하는 것이 좋습니다. 여기에 define_file_basename_for_sources 매크로 응답을 참조 하십시오.
Colin D Bennett

3
이것의 GNU Make 변형은 CFLAGS + = -D__FILENAME __ = \ "$ (notdir $ <) \"
ctuffli

4
@firegurafiku 임베디드 플랫폼에서 코드 크기는 종종 속도보다 제약이됩니다. 각 파일에 디버그 문이 있으면 전체 경로 문자열로 파일 크기를 빠르게 확장 할 수 있습니다. 나는 현재 그러한 플랫폼을 다루고 있으며 __FILE__모든 곳을 참조하는 것이 코드 공간에서 빠져 나옵니다.
mrtumnus

26

소스 및 헤더 파일 모두에서 작동하고 매우 효율적이며 컴파일러 별 확장이없는 모든 플랫폼에서 컴파일 시간에 작동하는 훌륭한 솔루션을 방금 생각했습니다. 이 솔루션은 또한 프로젝트의 상대 디렉토리 구조를 유지하므로 파일이있는 폴더를 알고 프로젝트의 루트에 대해서만 알 수 있습니다.

아이디어는 빌드 도구로 소스 디렉토리의 크기를 가져 와서 __FILE__매크로에 추가 하여 디렉토리를 완전히 제거하고 소스 디렉토리에서 시작하는 파일 이름 만 표시하는 것입니다.

다음 예제는 CMake를 사용하여 구현되었지만 트릭이 매우 간단하기 때문에 다른 빌드 도구와 작동하지 않을 이유가 없습니다.

CMakeLists.txt 파일에서 CMake의 프로젝트 경로 길이를 가진 매크로를 정의하십시오.

# The additional / is important to remove the last character from the path.
# Note that it does not matter if the OS uses / or \, because we are only
# saving the path size.
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")

소스 코드 __FILENAME__에서 소스 경로 크기를 __FILE__매크로에 추가 하는 매크로를 정의하십시오 .

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

그런 다음 매크로 대신이 새로운 매크로를 사용하십시오 __FILE__. __FILE__경로는 항상 CMake 소스 디렉토리의 경로로 시작 하기 때문에 작동합니다 . __FILE__문자열에서 파일 을 제거 하면 프리 프로세서가 올바른 파일 이름을 지정하고 CMake 프로젝트의 루트를 기준으로합니다.

성능에 관심이있는 경우 컴파일 시간 상수 와 알려진 컴파일러 __FILE__모두 를 사용 하므로 컴파일러를 사용하여 최적화 할 수 있으므로을 사용하는 것만 큼 효율적 입니다.__FILE__SOURCE_PATH_SIZE

이것이 실패하는 유일한 장소는 생성 된 파일에서 이것을 사용하고 오프 소스 빌드 폴더에있는 경우입니다. 그런 다음 CMAKE_BUILD_DIR대신 변수를 사용하여 다른 매크로를 만들어야 CMAKE_SOURCE_DIR합니다.


4
나는 이것을 처음 이해하지 못했습니다. 나는 예제를 코딩하고 gcc와 clang에 대해 실행했으며 작동했습니다. 또한 다양한 리터럴 숫자 값을 추가하는 실험을 해 보았으며 예상대로 작동합니다. 그리고 마침내 나에게 새벽. __FILE__바이트 배열에 대한 포인터입니다. 따라서 숫자 리터럴을 추가하면 포인터가 추가됩니다. 매우 영리한 @RenatoUtsch
Daniel

소스 파일이 cmake list 디렉토리에있는 경우에만 작동합니다. 소스 파일이 외부에 있으면 파일이 깨져서 리터럴 문자열 외부에서 액세스 할 수 있습니다. 그러니 조심하세요
Andry

실제로 코드 크기를 줄이지 않습니다. 전체 경로가 여전히 이진 파일로 컴파일되고 포인터 만 수정되는 것 같습니다.
Tarion 2019

__FILE__매크로와 SOURCE_PATH_SIZE 매크로는 모두 컴파일시 알려진 상수입니다. 현대 최적화 컴파일러가 문자열의 일부가 사용되지 않음을 감지하고 단순히 바이너리에서 제거 할 수 있기를 기대합니다. 어쨌든,이 몇 바이트가 이진 크기를 크게 변화시킬 것이라고 생각하지 않으므로 실제로는 신경 쓰지 않을 것입니다.
RenatoUtsch

@RenatoUtsch 내가 작업중 인 프로젝트에는 파일 이름 만 지정하는 변경 사항이 있지만 C 파일 이름을 헤더에 부여하는 단점도 있습니다. 재현 가능한 빌드를 얻기 위해 변경되었습니다. 따라서 gcc에 -O2가 있으면 문자열이 실제로 최적화되고 빌드가 재현 가능합니까?
Paul Stelian

18

순전히 컴파일 타임 솔루션. sizeof()문자열 리터럴이 길이 + 1을 반환 한다는 사실을 기반으로합니다 .

#define STRIPPATH(s)\
    (sizeof(s) > 2 && (s)[sizeof(s)-2] == '/' ? (s) + sizeof(s) - 1 : \
    sizeof(s) > 3 && (s)[sizeof(s)-3] == '/' ? (s) + sizeof(s) - 2 : \
    sizeof(s) > 4 && (s)[sizeof(s)-4] == '/' ? (s) + sizeof(s) - 3 : \
    sizeof(s) > 5 && (s)[sizeof(s)-5] == '/' ? (s) + sizeof(s) - 4 : \
    sizeof(s) > 6 && (s)[sizeof(s)-6] == '/' ? (s) + sizeof(s) - 5 : \
    sizeof(s) > 7 && (s)[sizeof(s)-7] == '/' ? (s) + sizeof(s) - 6 : \
    sizeof(s) > 8 && (s)[sizeof(s)-8] == '/' ? (s) + sizeof(s) - 7 : \
    sizeof(s) > 9 && (s)[sizeof(s)-9] == '/' ? (s) + sizeof(s) - 8 : \
    sizeof(s) > 10 && (s)[sizeof(s)-10] == '/' ? (s) + sizeof(s) - 9 : \
    sizeof(s) > 11 && (s)[sizeof(s)-11] == '/' ? (s) + sizeof(s) - 10 : (s))

#define __JUSTFILE__ STRIPPATH(__FILE__)

조건부 연산자 캐스케이드를 프로젝트의 최대 파일 이름으로 자유롭게 확장하십시오. 문자열의 끝에서 충분히 확인하면 경로 길이는 중요하지 않습니다.

매크로 재귀로 하드 코딩 된 길이가없는 비슷한 매크로를 얻을 수 있는지 볼 수 있습니다 ...


3
멋진 답변입니다. -O1컴파일 타임이 되려면 적어도 이것을 사용해야합니다.
디지털 외상

1
거꾸로되지 않습니까? 마지막으로 나타나는 '/' 를 찾으려면 sizeof(s) > 2먼저 검사 부터 시작해야합니다 . 또한 이것은 컴파일 타임에 -Os에서 작동하지 않았습니다. 전체 경로 문자열이 출력 바이너리에있었습니다.
mrtumnus

15

적어도 gcc의 경우, 값은 컴파일러의 명령 행에 지정된__FILE__ 파일 경로 입니다. 다음 과 같이 컴파일하면file.c

gcc -c /full/path/to/file.c

__FILE__확장됩니다 "/full/path/to/file.c". 대신 이렇게하면 :

cd /full/path/to
gcc -c file.c

다음으로 __FILE__확장됩니다 "file.c".

이것은 실용적 일 수도 있고 아닐 수도 있습니다.

C 표준에는이 동작이 필요하지 않습니다. 그것에 대해 말한다 모두 __FILE__는 "현재 소스 Fi를 르 (문자열 리터럴)의 추정 이름"으로 확장한다는 것이다.

대안은 #line지시문 을 사용하는 것 입니다. 현재 행 번호 및 선택적으로 소스 파일 이름을 대체합니다. 파일 이름을 무시하고 줄 번호 만 남겨 두려면 __LINE__매크로를 사용하십시오 .

예를 들어, 맨 위에 다음을 추가 할 수 있습니다 file.c.

#line __LINE__ "file.c"

이것에 대한 유일한 문제는 지정된 줄 번호를 다음 줄에 할당하고 첫 번째 인수 #line숫자 시퀀스 여야 하므로 다음과 같은 작업을 수행 할 수 없다는 것입니다

#line (__LINE__-1) "file.c"  // This is invalid

#line지시문 의 파일 이름이 파일 의 실제 이름과 일치 하는지 확인하는 것은 연습으로 남습니다.

최소한 gcc의 경우 진단 메시지에보고 된 파일 이름에도 영향을 미칩니다.


1
Keith Thompson은 훌륭한 솔루션입니다. 감사합니다. 유일한 문제는이 매크로 __LINE__가 하나의 가치를 깎아내는 것 같습니다 . 그래서 gest __LINE__in line xgest로 평가되었습니다 x-1. gcc 이상 5.4.
elklepo

1
@ Klepak : 당신 말이 맞습니다. 그리고 그것은 표준 행동입니다. 이 지시문은 구현이 다음 순서의 소스 라인이 숫자 시퀀스로 지정된 라인 번호를 갖는 소스 라인으로 시작하는 것처럼 작동하도록합니다. 그리고 숫자 순서 여야 하므로 사용할 수 없습니다 #line __LINE__-1, "file.c". 답변을 업데이트하겠습니다.
키스 톰슨

1
Clang, MSVC 또는 Intel C Compiler와 같은 다른 컴파일러는 어떻습니까?
Franklin Yu

8

파티에 약간 늦었지만 GCC의 경우 -ffile-prefix-map=old=new옵션을 살펴보십시오 .

old 디렉토리에있는 파일을 컴파일 할 때 , 파일이 new 디렉토리에있는 것처럼 컴파일 결과에 대한 참조를 기록하십시오 . 이 옵션을 지정하는 것은 모든 개별 -f*-prefix-map옵션 을 지정하는 것과 같습니다 . 위치에 독립적 인 재현 가능한 빌드를 만드는 데 사용할 수 있습니다. 참조 -fmacro-prefix-map-fdebug-prefix-map.

따라서 Jenkins 빌드의 -ffile-prefix-map=${WORKSPACE}/=/경우 로컬 개발자 패키지 설치 접두사를 제거하기 위해를 추가 하고 다른 것을 추가 합니다.

참고 불행히도 -ffile-prefix-map옵션은, 이후 GCC 8 만 avalable입니다 -fmacro-prefix-map, 이는 내 생각 은 않는 __FILE__부분. 예를 들어 GCC 5에 대해서는 -fdebug-prefix-map영향을 미치지 않는 것만 나타납니다 __FILE__.


1
옵션은 -ffile-prefix-map실제로 옵션 -fdebug-prefix-map-fmacro-prefix-map옵션을 모두 의미합니다 . 에서 참조 참조 reproducible-builds.org/docs/build-path에게 트랙 있다는 GCC 버그를 -fmacro-prefix-map하고 -ffile-prefix-map있다 gcc.gnu.org/bugzilla/show_bug.cgi?id=70268
Lekensteyn

6
  • C ++ 11
  • msvc2015u3, gcc5.4, clang3.8.0

    template <typename T, size_t S>
    inline constexpr size_t get_file_name_offset(const T (& str)[S], size_t i = S - 1)
    {
        return (str[i] == '/' || str[i] == '\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <typename T>
    inline constexpr size_t get_file_name_offset(T (& str)[1])
    {
        return 0;
    }

    '

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }

다음과 같은 경우 코드에서 컴파일 시간 오프셋을 생성합니다.

  • gcc: 적어도 gcc6.1 + -O1
  • msvc: constexpr 변수에 결과를 넣습니다.

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
  • clang: 컴파일 시간 평가를하지 않으면 지속

최적화를 비활성화 한 디버그 구성에서도 3 개의 컴파일러가 모두 컴파일 시간 평가를 수행하도록하는 트릭이 있습니다.

    namespace utility {

        template <typename T, T v>
        struct const_expr_value
        {
            static constexpr const T value = v;
        };

    }

    #define UTILITY_CONST_EXPR_VALUE(exp) ::utility::const_expr_value<decltype(exp), exp>::value

    int main()
    {
         printf("%s\n", &__FILE__[UTILITY_CONST_EXPR_VALUE(get_file_name_offset(__FILE__))]);
    }

https://godbolt.org/z/u6s8j3


남자, 아름답고 효과가 있습니다. 감사합니다! 이 답변이 왜 그렇게 과소 평가되었는지 모르겠다.
로마

5

사용하는 경우 VC에서 /FC, __FILE__를 빼고, 전체 경로로 확장 /FC옵션은 __FILE__파일 이름을 확장합니다. 심판 : 여기


4

이 작업을 수행 할 수있는 컴파일 시간이 없습니다. 분명히 다른 답변 중 일부가 시연 한 것처럼 C 런타임을 사용하여 런타임에 수행 할 수는 있지만 컴파일 타임에는 전임자가 시작되면 운이 좋지 않습니다.


1
strrchr대답은 그럴듯하지 전처리에 의해 비록 물론 아직도의 컴파일 타임에 계산 될 수있다. gcc가 실제로 수행하는지 여부를 알지 못했지만 확인하지는 않았지만 strlen컴파일 타임에 문자열 리터럴을 계산한다고 확신합니다 .
Steve Jessop

@Steve-어쩌면 컴파일러 특정 동작에 크게 의존합니다.
Sean

이 코드가 성능에 중요하다는 것을 의심하기 때문에 그것이 큰 의존이라고 생각하지 않습니다. 그리고 그렇다면 루프 밖으로 옮기십시오. 기본 이름 만 포함하는 문자열 리터럴이 절대적으로 필요하기 때문에 이것이 큰 문제인 경우 소스에서 일부 스크립트를 실행 하여 빌드 시 올바른 문자열을 계산할 수 있습니다.
Steve Jessop

5
성능이 중요하지는 않지만 개인 정보 보호로 쉽게 볼 수 있습니다. 릴리스 된 EXE 파일에 고정 된 문자열로 고객 별 조직 관행을 밝힐 이유가 없습니다. 더 나쁜 것은 고객을 대신하여 만든 응용 프로그램의 경우 이러한 문자열로 인해 고객이 자신의 제품을 만든 사람이 아니라는 것을 원하지 않을 수 있습니다. 에 __FILE__의해 암시 적으로 호출되므로이 assert()누출은 다른 명백한 조치없이 발생할 수 있습니다.
RBerteig

@RBerteig __FILE__자체 의 기본 이름은 고객이 원하지 않는 것을 나타낼 수도 있으므로 __FILE__전체 절대 경로 이름을 포함하든 기본 이름 만 포함하든 관계없이 어디에서나 사용 하면 지적한 것과 동일한 문제가 있습니다. 이 상황에서 모든 출력을 면밀히 조사해야하며 고객에게 출력하기 위해 특수 API를 도입해야합니다. 나머지 출력은 / dev / NULL 또는 stdout에 덤프하고 stderr을 닫아야합니다. :-)
tchen

4

사용 기본 이름 () 는 Windows에있는 경우, 기능, 또는 _splitpath () .

#include <libgen.h>

#define PRINTFILE() { char buf[] = __FILE__; printf("Filename:  %s\n", basename(buf)); }

man 3 basename껍질 에도 시도하십시오 .


2
@mahmood : char file_copy[] = __FILE__; const char *filename = basename(__FILE__);. 복사 이유는 기본 이름이 입력 문자열을 수정할 수 있기 때문입니다. 또한 결과 포인터가 basename다시 호출 될 때까지만 좋은지 확인 해야합니다. 이것은 스레드로부터 안전하지 않다는 것을 의미합니다.
Steve Jessop

@SteveJessop, 아 잊어 버렸습니다. 진실.
교수 Falken

1
@Amigable : 공정하게 말하면 basename입력 문자열 에 끝에가 없으므로 수정할 필요 __FILE__가 없기 때문에 실제로 결과 입력 문자열을 수정 하지 않을 것 /입니다. 그래서 당신은 그것을 벗어날 수도 있지만, 나는 누군가가 처음 basename볼 때 모든 제한 사항과 함께 그것을보아야한다고 생각합니다.
Steve Jessop

@SteveJessop basename ()의 BSd 매뉴얼 페이지는 basename ()의 레거시 버전이 const char *를 사용하고 문자열을 수정하지 않는다고 언급했습니다. 리눅스 매뉴얼 페이지는 const에 대해서는 언급하지 않지만 인수 문자열의 일부를 반환 할 수 있다고 언급합니다. 따라서 basename ()을 보수적으로 다루는 것이 가장 좋습니다.
교수 Falken

@SteveJessop은, 도니는 다르게, 난 단지 지금 조심스럽게 의견을보고 한 후, 4 년, 실현이 있음을 /문자열 수단의 기본 이름의 끝에 인수를 수정하는 좋은 이유가있을 수 있습니다.
교수 Falken

4

CMAKEGNU 컴파일러와 함께 사용 하는 경우 다음 global정의가 정상적으로 작동합니다.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")

4

나는 수년간 @Patrick의 대답 과 동일한 솔루션을 사용했습니다 .

전체 경로에 심볼 링크가 포함되어 있으면 작은 문제가 있습니다.

더 나은 솔루션.

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")

왜 이것을 사용해야합니까?

  • -Wno-builtin-macro-redefined__FILE__매크로 재정의에 대한 컴파일러 경고를 음소거합니다 .

    해당 컴파일러가이를 지원하지 않으면 아래 의 강력한 방법을 참조하십시오 .

  • 파일 경로에서 프로젝트 경로를 제거하는 것이 실제 요구 사항입니다. 당신은 어디에 있는지 찾기 위해 시간을 낭비하고 싶지 않습니다 header.h파일, src/foo/header.h또는 src/bar/header.h.

  • 설정 파일 에서 __FILE__매크로를 제거해야 cmake합니다.

    이 매크로는 대부분 존재하는 코드에서 사용됩니다. 간단히 재정의하면 자유로울 수 있습니다.

    컴파일러 gcc는 명령 행 인수에서이 매크로를 사전 정의합니다. 그리고 전체 경로는로 makefile생성됩니다 cmake.

  • 하드 코드 입력 CMAKE_*_FLAGS이 필요합니다.

    add_definitions()and와 같은 최신 버전에는 컴파일러 옵션 또는 정의를 추가하는 명령이 있습니다 add_compile_definitions(). 이 명령은 subst소스 파일에 적용하기 전에 make 기능을 구문 분석 합니다. 그것은 우리가 원하는 것이 아닙니다.

에 대한 강력한 방법 -Wno-builtin-macro-redefined.

include(CheckCCompilerFlag)
check_c_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)

이 컴파일러 옵션을 set(*_FLAGS ... -D__FILE__=...)줄 에서 제거하십시오 .


포함 파일에서 오는 내용에는 작동하지 않습니다.
chqrlie

코드를 게시 할 수 있습니까? 일반적인 경우는 로컬 범위에서 변수를 설정하고 다른 범위에서 사용하는 것입니다.
Levi.G

예를 들어 __FILE__, 헤더 파일에 정의 된 인라인 함수에서 진단을 생성하기 위해 정의와 함께 사용 하는 경우 런타임 진단은 포함 파일 이름 대신 컴파일러에 전달 된 파일 이름을보고하지만 행 번호는 포함 파일을 참조하십시오.
chqrlie

그렇습니다 #define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args). 가장 일반적인 사용법은 입니다. LOG()매크로를 사용할 때 실제로 log.h메시지 를보고 싶지 않습니다 . 결국 __FILE__매크로는 포함 된 파일 대신 모든 C / Cpp 파일 (컴파일 단위)에서 확장됩니다.
Levi.G

3

@ red1ynx가 제안한 것의 약간의 변형은 다음 매크로를 만드는 것입니다.

#define SET_THIS_FILE_NAME() \
    static const char* const THIS_FILE_NAME = \
        strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__;

각 .c (pp) 파일에서 다음을 추가하십시오.

SET_THIS_FILE_NAME();

그러면 다음 THIS_FILE_NAME대신에 참조 할 수 있습니다 __FILE__.

printf("%s\n", THIS_FILE_NAME);

이는 매크로가 참조 될 때마다 .c (pp) 파일 당 한 번씩 생성이 수행됨을 의미합니다.

.c (pp) 파일에서만 사용하도록 제한되며 헤더 파일에서는 사용할 수 없습니다.


3

__FILENAME__매번 전체 경로를 자르는 것을 피하는 매크로 를 만들었 습니다. 문제는 결과 파일 이름을 cpp-local 변수에 보관하는 것입니다.

.h 파일 에 정적 전역 변수를 정의하면 쉽게 수행 할 수 있습니다 . 이 정의는 .h 를 포함하는 각 .cpp 파일 에 별도의 독립 변수를 제공합니다 . 멀티 스레딩을 방지하려면 변수를 스레드 로컬 (TLS)로 만드는 것이 좋습니다.

하나의 변수는 File Name (shrunk)을 저장합니다. 다른 하나는 줄지 않은 가치를 지니고 있습니다 __FILE__. h 파일 :

static __declspec( thread ) const char* fileAndThreadLocal_strFilePath = NULL;
static __declspec( thread ) const char* fileAndThreadLocal_strFileName = NULL;

매크로 자체는 모든 로직으로 메소드를 호출합니다.

#define __FILENAME__ \
    GetSourceFileName(__FILE__, fileAndThreadLocal_strFilePath, fileAndThreadLocal_strFileName)

그리고 기능은 다음과 같이 구현됩니다.

const char* GetSourceFileName(const char* strFilePath, 
                              const char*& rstrFilePathHolder, 
                              const char*& rstrFileNameHolder)
{
    if(strFilePath != rstrFilePathHolder)
    {
        // 
        // This if works in 2 cases: 
        // - when first time called in the cpp (ordinary case) or
        // - when the macro __FILENAME__ is used in both h and cpp files 
        //   and so the method is consequentially called 
        //     once with strFilePath == "UserPath/HeaderFileThatUsesMyMACRO.h" and 
        //     once with strFilePath == "UserPath/CPPFileThatUsesMyMACRO.cpp"
        //
        rstrFileNameHolder = removePath(strFilePath);
        rstrFilePathHolder = strFilePath;
    }
    return rstrFileNameHolder;
}

removePath ()는 다른 방법으로 구현 될 수 있지만 빠르고 간단하게 strrchr을 사용하는 것 같습니다.

const char* removePath(const char* path)
{
    const char* pDelimeter = strrchr (path, '\\');
    if (pDelimeter)
        path = pDelimeter+1;

    pDelimeter = strrchr (path, '/');
    if (pDelimeter)
        path = pDelimeter+1;

    return path;
}


2

FILE 매크로를 약간 향상시키기를 바랍니다.

#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

이것은 Czarek Tomczak이 요청한 것처럼 / 및 \를 잡아 내 혼합 환경에서 훌륭하게 작동합니다.


6
포함 된 매크로라는 이름을 정의하는 FILE것은 정말 나쁜 생각 <stdio.h>입니다.
키이스 톰슨

알아 둘만 한. 나는 단지 Czarek에게 내 \\ / 솔루션을 보여주고 싶었으므로 명명 체계에 신경 쓰지 않았다.
alexander golks

2

시험

#pragma push_macro("__FILE__")
#define __FILE__ "foobar.c"

소스 파일의 include 문 다음에 추가

#pragma pop_macro("__FILE__")

소스 파일의 끝에.


2
push_macropop_macro비 표준입니다. (gcc는 Microsoft Windows 컴파일러와의 호환성을 위해 이들을 지원합니다.) 어쨌든 __FILE__; 소스 파일의 종료 후에는 복원 된 값이 사용되지 않습니다. 의 값을 변경하는 청소기 방법 __FILE__입니다#line __LINE__ "foobar.c"
키이스 톰슨

1
그리고 이것은 gcc의 전처리기에 내부 오류를 일으 킵니다. gcc.gnu.org/bugzilla/show_bug.cgi?id=69665
Keith Thompson

1

다음은 Linux (경로 '/')와 Windows ( '\'와 '/'혼합) 모두에서 작동하는 이식 가능한 기능입니다.
gcc, clang 및 vs로 컴파일

#include <string.h>
#include <stdio.h>

const char* GetFileName(const char *path)
{
    const char *name = NULL, *tmp = NULL;
    if (path && *path) {
        name = strrchr(path, '/');
        tmp = strrchr(path, '\\');
        if (tmp) {
             return name && name > tmp ? name + 1 : tmp + 1;
        }
    }
    return name ? name + 1 : path;
}

int main() {
    const char *name = NULL, *path = NULL;

    path = __FILE__;
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path ="/tmp/device.log";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\\Downloads\\crisis.avi";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\\Downloads/nda.pdf";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:/Downloads\\word.doc";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = NULL;
    name = GetFileName(NULL);
    printf("path: %s, filename: %s\n", path, name);

    path = "";
    name = GetFileName("");
    printf("path: %s, filename: %s\n", path, name);

    return 0;
}

표준 출력 :

path: test.c, filename: test.c
path: /tmp/device.log, filename: device.log
path: C:\Downloads\crisis.avi, filename: crisis.avi
path: C:\Downloads/nda.pdf, filename: nda.pdf
path: C:/Downloads\word.doc, filename: word.doc
path: (null), filename: (null)
path: , filename: 

1

컴파일 타임 계산을 사용하는 솔루션은 다음과 같습니다.

constexpr auto* getFileName(const char* const path)
{
    const auto* startPosition = path;
    for (const auto* currentCharacter = path;*currentCharacter != '\0'; ++currentCharacter)
    {
        if (*currentCharacter == '\\' || *currentCharacter == '/')
        {
            startPosition = currentCharacter;
        }
    }

    if (startPosition != path)
    {
        ++startPosition;
    }

    return startPosition;
}

std::cout << getFileName(__FILE__);

1

이 페이지에서 배송중인 바이너리에서 추악한 빌드 위치를 가리키는 절대 소스 경로를 제거하는 방법을 찾고 있다면 아래의 요구 사항에 적합 할 수 있습니다.

비록 이것이 CMake의 사용을 가정 한 이후 저자가 자신의 소망을 표명 한 정확한 답변을 제공하지는 않지만 꽤 가깝습니다. 그것은 나에게 많은 시간을 절약 할 수 있었기 때문에 아무도 언급하지 않은 것이 유감입니다.

OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)

위의 변수를 설정 ON하면 형식으로 빌드 명령이 생성됩니다.

cd /ugly/absolute/path/to/project/build/src && 
    gcc <.. other flags ..> -c ../../src/path/to/source.c

결과적으로 __FILE__매크로는../../src/path/to/source.c

CMake 설명서

그래도 설명서 페이지의 경고에주의하십시오.

상대 경로를 사용하십시오 (작동하지 않을 수 있습니다!).

모든 경우에 작동한다고 보장되지는 않지만 CMake 3.13 + gcc 4.5에서 작동했습니다.


CMake 3.4+ 문서에는 "이 변수는 효과가 없습니다. 이전 릴리스에서 부분적으로 구현 된 효과는 CMake 3.4에서 제거되었습니다."라고 설명합니다. 그것이 3.13에서 당신을 위해 일했다면, 그것에 대한 또 다른 이유가 있습니다.
mike.dld

0

당신은 GCC를 사용하고 있기 때문에, 당신은 수있는 장점이 걸릴 의를

__BASE_FILE__이 매크로는 C 문자열 상수 형식으로 기본 입력 파일 이름으로 확장됩니다. 이것은 프리 프로세서 또는 C 컴파일러의 명령 행에 지정된 소스 파일입니다.

컴파일 타임에 소스 파일 표현 (전체 경로 / 상대 경로 / 기본 이름)을 변경하여 파일 이름을 표시 할 방법을 제어하십시오.


6
차이가 없습니다. 내가 사용 __FILE__하고 __BASE_FILE__있지만 그들은 모두 파일의 전체 경로를 보여
마흐무드

컴파일러를 어떻게 호출합니까?
ziu

2
그런 다음 SCONS가 gcc를 이렇게 호출합니다 gcc /absolute/path/to/file.c. 이 동작을 변경하는 방법을 찾으면 (SO에 다른 질문을 열어?)? 런타임에 문자열을 수정할 필요가 없습니다.
ziu

17
이 답변은 100 % 잘못되었습니다. __BASE_FILE__(워드 프로세서 말하는대로, unclearly이기는하지만)의 이름을 생성하는 명령 줄에 지정된 파일 , 예를 test.c하거나 /tmp/test.c컴파일러를 호출하는 방법에 따라 달라집니다. __FILE__헤더 파일 안에있는 경우를 제외하고 는 정확히 동일 합니다.이 경우 __FILE__현재 파일의 이름 을 생성 foo.h하지만 (예 :) __BASE_FILE__계속 생성 test.c합니다.
Quuxplusone

0

다음은 문자열 라이브러리 (Linux 커널, 임베디드 시스템 등)가없는 환경에서 작동하는 솔루션입니다.

#define FILENAME ({ \
    const char* filename_start = __FILE__; \
    const char* filename = filename_start; \
    while(*filename != '\0') \
        filename++; \
    while((filename != filename_start) && (*(filename - 1) != '/')) \
        filename--; \
    filename; })

이제 FILENAME대신 대신 사용하십시오 __FILENAME__. 예, 여전히 런타임이지만 작동합니다.


플랫폼에 따라 '/'와 '\'를 모두 확인하고 싶을 수 있습니다.
Technophile

0
#include <algorithm>
#include <string>
using namespace std;
string f( __FILE__ );
f = string( (find(f.rbegin(), f.rend(), '/')+1).base() + 1, f.end() );

// searches for the '/' from the back, transfers the reverse iterator 
// into a forward iterator and constructs a new sting with both

0

Windows와 * nix 모두에 대한 간단한 대답 :

#define __FILENAME__ std::max<const char*>(__FILE__,\
    std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))

std::max<const char*>대신에 필요한 std::max가요?
chqrlie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.