0

I use __FILE__ during logging and debug my program. And when I use it, I get full source file path. So, I wanna write my own macro similar the macro __FILE__.

I looked for a solution, but I found nothing about it. So, does it exist a CMake way to implement user macro, that will be generate something data similar the __FILE__ macro?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
voltento
  • 833
  • 10
  • 26
  • 2
    Why not just use `__FILE__` then? – tkausl Oct 29 '17 at 13:58
  • __FILE__ prints full file path, but It looks very big and ugly in logs. – voltento Oct 29 '17 at 13:59
  • 4
    I guess you can create a custom command that loops over all source files of a target and adds a compiler flag `-DFILE=${SOURCE_FILE_NAME}` after extracting the `${SOURCE_FILE_NAME}` variable based on the file path. – Corristo Oct 29 '17 at 14:03
  • 4
    That's a golden XY problem. Just process the value of `__FILE__` before logging it :) – Quentin Oct 29 '17 at 14:03
  • @Corristo I was thinking of that. It seems like this would be simpler to solve in c++ than CMake.. – drescherjm Oct 29 '17 at 14:04
  • @drescherjm I think, that C++ source doesn't have to know about project structure. – voltento Oct 29 '17 at 14:06
  • 3
    It does not have to do that. All it needs is to do remove the path from `__FILE__` leaving just the file name. You can certainly write a c or c++ function that removes the path. – drescherjm Oct 29 '17 at 14:06
  • [`__FILE__` in MSVC prints the basename by default unless you specify `/FC`](https://msdn.microsoft.com/en-us/library/b0084kay(v=vs.100).aspx) – phuclv Oct 30 '17 at 06:48
  • 1
    I found solution here https://stackoverflow.com/questions/8487986/file-macro-shows-full-path for my platform – voltento Oct 30 '17 at 07:03
  • 1
    This is now closed, but you can very well extract the basename at compile-time without build system trickery: https://godbolt.org/g/egaQcs (notice `offset .L.str.1+59` where `59` is hardcoded). – Quentin Oct 30 '17 at 09:09

2 Answers2

2

Just remove the path from __FILE__.

Possible solution:

#include <libgen.h>
#include <stdio.h>

#define LOG(message) \
do { \
    char* filename = basename(__FILE__); \
    printf("In %s: %s", filename, message); \
} while(false);

Example has to be extended by check of log level. And instead of a fixed message a variadic number of arguments should be used.

Note: This is for Unix. Assume your OS provides a similar function.

Th. Thielemann
  • 2,592
  • 1
  • 23
  • 38
1

As I mentioned in a comment already, you can create a CMake function that loops over all source files of a given target and adds a macro definition to the compile flags for that source file.

function(add_filepath_macro target) 
    get_target_property(SOURCE_FILES ${target} SOURCES)

    foreach (FILE_PATH IN LISTS SOURCE_FILES)
        file(RELATIVE_PATH RELATIVE_FILE_PATH ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH})
        set_property(SOURCE ${FILE_PATH} APPEND PROPERTY COMPILE_DEFINITIONS FILEPATH="${RELATIVE_FILE_PATH}")
    endforeach()
endfunction()

This now defines the macro FILEPATH in every TU that is part of the target you pass to it.

There is one caveat though: In contrast to the regular __FILE__ macro you can't use the FILEPATH macro in header files, because including one such header in different translation units immediately causes an ODR violation and thus undefined behaviour.

Demo

Let's see how this works for a very small example. Start with the following project structure:

.
├── cmake
│   └── add_filepath_macro.cmake
├── CMakeLists.txt
├── lib
│   ├── a.cpp
│   ├── a.h
│   └── CMakeLists.txt
└── main.cpp

The cmake/add_filepath_macro.cmake file contains nothing but the code shown above.

The we have the library files

// lib/a.h
#pragma once
char const* foo();

// lib/a.cpp
#include "a.h"
char const* foo() {
    return FILEPATH;
}

with corresponding CMakeLists.txt

# lib/CMakeLists.txt

add_library(liba
    a.cpp
)

target_include_directories(liba PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_filepath_macro(liba)

as well as a small demo executable

// main.cpp
#include <iostream>

#include "a.h"

int main() {
    std::cout << foo() << '\n' << FILEPATH << '\n';
}

and finally the project CMakeLists.txt

# CMakeLists.txt

# not tested with older version, but I guess it should work with anything > 3.0
cmake_minimum_required(VERSION 3.9) 

project(cmake_filepath_macro_demo LANGUAGE CXX)

include(cmake/add_filepath_macro.cmake)
add_subdirectory(lib)

add_executable(demo
    main.cpp
)

add_filepath_macro(demo)

Running the resulting executable then produces the following output

$ ./demo 
lib/a.cpp
main.cpp
Corristo
  • 4,911
  • 1
  • 20
  • 36