C++ 从_文件_编译时修剪路径和扩展名

C++ 从_文件_编译时修剪路径和扩展名,c++,gcc,C++,Gcc,----问题--- 我正在尝试将文件名“/path/to/Module.cpp”转换为保存值“Module”编译时的const char*。这有助于我在微控制器中打印人体工程学日志。我使用GCC 8.3 关于如何仅剥离路径组件,有很多很好的示例,例如 这导致TAG保持“Module.cpp”。这是一个很好的开始,但我也想去掉“.cpp”。当然是编译时。有什么想法吗 ----回答--- 多亏了@KamilCuk,我才想出了以下头文件,当包含在cpp文件中时,它为ESP IDF日志宏创建了一个很好的

----问题---

我正在尝试将文件名“/path/to/Module.cpp”转换为保存值“Module”编译时的
const char*
。这有助于我在微控制器中打印人体工程学日志。我使用GCC 8.3

关于如何仅剥离路径组件,有很多很好的示例,例如

这导致
TAG
保持“Module.cpp”。这是一个很好的开始,但我也想去掉“.cpp”。当然是编译时。有什么想法吗

----回答---

多亏了@KamilCuk,我才想出了以下头文件,当包含在cpp文件中时,它为ESP IDF日志宏创建了一个很好的标记:

#ifndef _LOG_HPP_
#define _LOG_HPP_

#include "esp_log.h"
#include <string_view>

// Note: path processing naïvely assumes a valid Unix file path containing
// directories and an extension.

/**
 * Find the length of stem in a file name
 * @param path A file name with '/' as path separator and '.' as extension separator
 * @return Number of characters in file stem excluding terminating zero
 */
constexpr size_t stemNameLen(const std::string_view& path) {
    return path.find_last_of('.') - path.find_last_of('/') - 1;
}

// Rudimentary unit test
static_assert(stemNameLen(std::string_view("../foo/bar/MyModule.cpp")) == 8);

/**
 * Get the stem in a file name
 * @param path A file name with '/' as path separator and '.' as extension separator
 * @return A string_view holding the stem of the input file name
 */
constexpr std::string_view stemName(const std::string_view& path) {
    return path.substr(path.find_last_of('/') + 1, stemNameLen(path));
}

// Rudimentary unit test
static_assert(stemName(std::string_view("../foo/bar/MyModule.cpp")) == "MyModule");

/// Helper class for creating a C-style zero-terminated string from a string_view
template <size_t N>
class TerminatedString {
public:
    constexpr TerminatedString(const std::string_view& path) {
        size_t i = 0;
        for (auto it = path.cbegin(); i + 1 < sizeof(_str) && it != path.cend(); i++, it++) {
            _str[i] = *it;
        }
    }
    constexpr const char *str() const {
        return _str;
    }
private:
    char _str[N] {'\0', };
};

/// Module name from the file which includes this header
static constexpr std::string_view moduleName = stemName(__BASE_FILE__);
/// A zero-terminated log prefix from module name, initialized compile-time
static constexpr TerminatedString<moduleName.length() + 1> logPrefix{moduleName};

// Sanity check, assumes all file stems in project are less than 100 chars
static_assert(moduleName.length() < 100);

#define err(args...) ESP_LOGE(logPrefix.str(), args)
#define warn(args...) ESP_LOGW(logPrefix.str(), args)
#define info(args...) ESP_LOGI(logPrefix.str(), args)
#define debug(args...) ESP_LOGD(logPrefix.str(), args)
#define trace(args...) ESP_LOGV(logPrefix.str(), args)

#endif // _LOG_HPP_
\ifndef\u LOG\u水电站_
#定义_LOG_水电站_
#包括“esp_log.h”
#包括
//注意:路径处理天真地假设有效的Unix文件路径包含
//目录和扩展名。
/**
*在文件名中查找词干的长度
*@param path以“/”作为路径分隔符,“.”作为扩展名分隔符的文件名
*@返回文件干中的字符数,不包括终止零
*/
constexpr size\u t stemNameLen(const std::string\u视图和路径){
返回path.find_last_of('.')-path.find_last_of('/')-1;
}
//基本单元测试
静态断言(stemNameLen(std::string_视图(“../foo/bar/MyModule.cpp”))==8);
/**
*获取文件名中的词干
*@param path以“/”作为路径分隔符,“.”作为扩展名分隔符的文件名
*@return一个字符串视图,包含输入文件名的词干
*/
constexpr std::string\u view stemName(const std::string\u view&path){
返回path.substr(path.find_last_of('/')+1,stemNameLen(path));
}
//基本单元测试
静态断言(stemName(std::string_视图(“../foo/bar/MyModule.cpp”))==“MyModule”);
///用于从字符串视图创建以零结尾的C样式字符串的帮助器类
样板
类终止字符串{
公众:
constexpr TerminatedString(const std::string_视图和路径){
尺寸i=0;
对于(auto it=path.cbegin();i+1
以下“有效”,但并不完全干净。清理它只是一项练习。无论如何,它可能会告诉你如何做:

#include <cstdio>

constexpr unsigned long filename_we_size(const char *path) {
    // I know - some C pointer stuff. I don't know which C++ functions are 
    // constexpr which are not, and I am too lazy to check, so I used C pointers. 
    // Preferably rewrite it in more C++-ish style.
    auto i = path;
    while (*i) ++i;
    auto end = i;
    while (*i != '.' && i != path) --i;
    const auto ext_len = end - i;
    while (*i != '/' && i != path) --i;
    const auto filename_len = end - i;
    return filename_len - ext_len;
}

constexpr const char *filename_we(const char *path, char *out) {
    auto i = path;
    while (*i) ++i;
    while (*i != '/' && i != path) --i;
    if (*i) ++i;
    auto r = out;
    while (*i != '.' && *i) *r++ = *i++;
    *r = 0;
    return r;
}

// A structure. *Something* has to store the memory.
template <size_t N>
struct Tag {
    char mem[N]{};
    constexpr Tag(const char *path) {
        filename_we(path, mem);
    }
    constexpr const char *str() const {
        return mem;
    }
    constexpr operator const char *() const{
        return mem;
    }    
    constexpr char operator[](size_t i) const {
        return mem[i];
    }
};

static constexpr Tag<filename_we_size(__FILE__)> TAG{__FILE__};

int main() {
    printf("%s\n", TAG.str());
}
#包括
constexpr无符号长文件名大小(const char*path){
我知道一些C指针的东西,我不知道哪些C++函数是
//constexpr不是,我懒得检查,所以我使用C指针。
//最好用C++风格重写。
自动i=路径;
而(*i)+i;
自动结束=i;
而(*i!='.&&i!=路径)--i;
const auto ext_len=end-i;
而(*i!='/'&&i!=路径)--i;
const auto filename_len=end-i;
返回文件名\u len-ext\u len;
}
constexpr const char*filename\u we(const char*path,char*out){
自动i=路径;
而(*i)+i;
而(*i!='/'&&i!=路径)--i;
如果(*i)+i;
自动r=输出;
而(*i!='.&&&*i)*r++=*i++;
*r=0;
返回r;
}
//建筑物*有些东西必须存储内存。
样板
结构标签{
charmem[N]{};
constexpr标记(const char*path){
文件名_we(路径,mem);
}
constexpr const char*str()const{
返回mem;
}
constexpr运算符constchar*()const{
返回mem;
}    
constexpr char运算符[](大小i)const{
返回mem[i];
}
};
静态constexpr标记{{uuuuu文件};
int main(){
printf(“%s\n”,TAG.str());
}
以下“有效”,但并不完全干净。清理它只是一项练习。无论如何,它可能会告诉你如何做:

#include <cstdio>

constexpr unsigned long filename_we_size(const char *path) {
    // I know - some C pointer stuff. I don't know which C++ functions are 
    // constexpr which are not, and I am too lazy to check, so I used C pointers. 
    // Preferably rewrite it in more C++-ish style.
    auto i = path;
    while (*i) ++i;
    auto end = i;
    while (*i != '.' && i != path) --i;
    const auto ext_len = end - i;
    while (*i != '/' && i != path) --i;
    const auto filename_len = end - i;
    return filename_len - ext_len;
}

constexpr const char *filename_we(const char *path, char *out) {
    auto i = path;
    while (*i) ++i;
    while (*i != '/' && i != path) --i;
    if (*i) ++i;
    auto r = out;
    while (*i != '.' && *i) *r++ = *i++;
    *r = 0;
    return r;
}

// A structure. *Something* has to store the memory.
template <size_t N>
struct Tag {
    char mem[N]{};
    constexpr Tag(const char *path) {
        filename_we(path, mem);
    }
    constexpr const char *str() const {
        return mem;
    }
    constexpr operator const char *() const{
        return mem;
    }    
    constexpr char operator[](size_t i) const {
        return mem[i];
    }
};

static constexpr Tag<filename_we_size(__FILE__)> TAG{__FILE__};

int main() {
    printf("%s\n", TAG.str());
}
#包括
constexpr无符号长文件名大小(const char*path){
我知道一些C指针的东西,我不知道哪些C++函数是
//constexpr不是,我懒得检查,所以我使用C指针。
//最好用C++风格重写。
自动i=路径;
而(*i)+i;
自动结束=i;
而(*i!='.&&i!=路径)--i;
const auto ext_len=end-i;
而(*i!='/'&&i!=路径)--i;
const auto filename_len=end-i;
返回文件名\u len-ext\u len;
}
constexpr const char*filename\u we(const char*path,char*out){
自动i=路径;
而(*i)+i;
而(*i!='/'&&i!=路径)--i;
如果(*i)+i;
自动r=输出;
而(*i!='.&&&*i)*r++=*i++;
*r=0;
返回r;
}
//建筑物*有些东西必须存储内存。
样板
结构标签{
charmem[N]{};
constexpr标记(const char*path){
文件名_we(路径,mem);
}
constexpr const char*str()const{
返回mem;
}
constexpr运算符constchar*()const{
返回mem;
}    
constexpr char运算符[](大小i)const{
返回mem[i];
}
};
静态constexpr标记{{uuuuu文件};
int main(){
printf(“%s\n”,TAG.str());
}

不确定编译器是否能做到这一点。这是一个非常复杂的函数调用,需要动态分配。还有,那会是暂时的吗?是的,我不得不承认这是一个挑战,是的