打印文件名在编译时保存

时间:2018-05-15 10:55:10

标签: c string macros filenames

我的目标是打印文件名而不是文件名的相对路径。我正在使用宏TRACE()进行实验。 由于它都在同一个文件中,我将模拟文件名作为TRACE()的输入。所以在现实生活中,你可以说输入被替换为__FILE__

代码:

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

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

#define __FILENAME__(x) TOSTRING(strrchr(x, '\\'))

#define TRACE(s, ...) \
    { \
    if (strrchr(s, '\\')) { \
        static const char str[] = __FILENAME__(s) "\n\r"; \
        printf(str, ##__VA_ARGS__); \
    } else { \
        static const char str[] = s "\n\r"; \
        printf(str, ##__VA_ARGS__); \
    } \
    }

int main() {
    TRACE("file.c");
    TRACE("parent\\file.c");
    return 0;
}

输出:

file.c
strrchr("parent\\file.c", '\\')

因此,如果它是本地文件,则打印为file.c,这很棒。这意味着宏中的if情况正在运行:)。但是当它是另一个文件夹中的文件时,我无法“字符串化”计算strrchr(s, '\\')。为什么?

此外,我没有看到定义中的计算问题,因为一切都是在编译时定义的! (这就是if案例有效的原因,对吗?)

如果我从TOSTRING()删除__FILENAME__,我会收到大量错误。因为它无法将__FILENAME__的输出与str[]

连接起来

有没有办法解决这个问题?

2 个答案:

答案 0 :(得分:2)

初步观察

请注意,在C(而不是C ++)中,您无法使用函数调用的结果初始化static const char str[]数组。如果strrchr()发现了反斜杠,您可能希望在反斜杠后打印一个名称。字符串化不会将调用strrchr()的结果字符串化。

另请注意,通常不应创建以下划线开头的函数或变量名。 C11 §7.1.3 Reserved identifiers说(部分):

  
      
  • 所有以下划线开头且以大写字母或其他下划线开头的标识符始终保留供任何使用。
  •   
  • 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。
  •   

另见What does double underscore (__const) mean in C?

由于TRACE宏的第一个参数已经是一个字符串,因此应用字符串化没有多大好处 - 除非您希望在打印名称时显示双引号。

简单适应

要获得或多或少的结果,您需要接受每次传递跟踪(或更精细的初始化方案)时都会有运行时开销调用strrchr(),以下几行:

#define TRACE(s, ...) \
    do { \
        const char *basename = strrchr(s, '\\'); \
        if (basename == 0) \
            basename = s; \
        else \
            basename++; \
        printf(basename, ## __VA_ARGS__); \
    } while (0)

do { … } while (0)成语是标准的;它允许你写:

if (something)
    TRACE("hocuspocus.c: test passed\n");
else
    TRACE("abracadabra.c: test failed\n");

如果在问题中使用仅括号表示法,则第一个TRACE后面的分号会使else成为语法错误。另请参阅C #define macro for debug printingWhy use apparently meaningles do { … } while (0) and if … else statements in macros?以及do { … } while (0) — what is it good for?

只要您知道它是GCC(和Clang因为它与GCC兼容)扩展,并且不是标准C的一部分,## __VA_ARGS__技巧就可以了。

您计划如何使用变量参数也不完全清楚。看起来你可以做到:

TRACE("some\\kibbitzer.c: value %d is out of the range [%d..%d]\n",
      value, MIN_RANGE, MAX_RANGE);

其中文件名嵌入格式字符串中。也许你想到了:

TRACE(__FILE__ ": value %d is out of the range [%d..%d]\n",
      value, MIN_RANGE, MAX_RANGE);

那可行; __FILE__是一个字符串文字,与__func__不同,static const char __func__[] = "…function name…";是预定义的标识符(strrchr())。

最后(现在),考虑跟踪输出是应该转到标准输出还是标准错误。很容易争辩应该是标准错误;它(可能)不是该计划常规输出的一部分。

我建议查看“调试宏”的问题和答案 - 但是自从我写了得分最高的答案后,我感到偏见。

减少运行时开销

只要您没有弄乱自动变量等,就可以减少每个文件名单次调用#define TRACE(s, ...) \ do { \ static const char *basename = 0; if (basename == 0) \ { if ((basename = strrchr(s, '\\')) == 0) \ basename = s; \ else \ basename++; \ } \ printf(basename, ## __VA_ARGS__); \ } while (0) 的运行时间开销。如果您使用的是字符串文字,那么就可以了。< / p>

basename

这会将basename初始化为null;在第一次通过代码时,strrchr()被设置为字符串中的正确位置;此后,没有进一步致电def datauji(self): uji = [] for x in self.fiturs: a = [x[0],x[-5:]] #I think the problem in this line uji.append(a) return uji with open('DataUjiBaru.csv','wb') as dub: testing = csv.writer(dub) datatest = d.datauji() datatest.pop(0) for x in datatest: testing.writerow(x)

警告:显示的代码尚未编译。

答案 1 :(得分:1)

我认为理解宏和函数的工作方式存在一些问题。

宏不是“执行”的,它们只是简单的文本替换。是的,这发生在编译时(实际上是预编译),但只是替换。

编译时,宏不会执行,编码或调用任何函数(如strrchr)。

在您的代码中,您有 -

#define __FILENAME__(x) TOSTRING(strrchr(x, '\\'))

每当使用__FILENAME__(foo)时,它都会被"strrchr(foo, '\\')"替换。我确信这不是你想要的。

就个人而言,我认为没有任何理由在这里使用宏。只需将其变为正常功能即可。编译器将为您优化它。