7

我目前正在使用 C 进行编码,并且我有很多 printfs,因此我可以在某些时候跟踪我的应用程序的流程。问题是有时我想要比其他人更多的细节,所以我通常会花时间注释/取消注释我的 C 代码,以便获得适当的输出。

使用 Java 或 C# 时,我通常可以使用 Aspects 将我的实现代码与日志记录逻辑分开。

您在 C 中使用过任何类似的技术来解决这个问题吗?

我知道我可以放置一个名为 DEBUG 的标志,它可以打开或关闭,所以我不必每次想显示或隐藏 printfs 时都四处评论/取消评论我的整个代码。问题是我还想摆脱代码中的日志记录逻辑。

如果用 C++ 代替 CI 编码,会更好吗?

编辑

似乎有一个 AspectC++,所以对于 C++ 似乎有一个解决方案。C呢?

谢谢

4

6 回答 6

11

IME you cannot really separate logging from the algorithms that you want to log about. Placing logging statements strategically takes time and experience. Usually, the code keeps assembling logging statements over its entire lifetime (though it's asymptotic). Usually, the logging evolves with the code. If the algorithm changes often, so will usually the logging code.

What you can do is make logging as unobtrusive as possible. That is, make sure logging statements always are one-liners that do not disrupt reading the algorithm, make it so others can insert additional logging statements into an existing algorithm without having to fully understand your logging lib, etc.

In short, treat logging like you treat string handling: Wrap it in a nice little lib that will be included and used just about everywhere, make that lib fast, and make it easy to use.

于 2010-11-14T16:48:15.847 回答
5

并不真地。

如果您有可用的可变参数宏,您可以轻松地玩这样的游戏:

#ifdef NDEBUG
    #define log(...) (void)0
#else
    #define log(...) do {printf("%s:%d: ", __FILE__, __LINE__); printf(__VA_ARGS__);} while(0)
#endif

您还可以以更精细的粒度关闭和打开日志记录:

#define LOG_FLAGS <something>;

#define maybe_log(FLAG, ...) do { if (FLAG&LOG_FLAGS) printf(__VA_ARGS__);} while(0)

int some_function(int x, int y) {
    maybe_log(FUNCTION_ENTRY, "x=%d;y=%d\n", x, y);
    ... do something ...
    maybe_log(FUNCTION_EXIT, "result=%d\n", result);
    return result;
}

显然,这可能有点乏味,只允许从每个函数返回一次,因为您不能直接获取函数返回。

这些宏和调用中的任何一个printf都可以替换为允许实际日志记录格式和目标与业务逻辑分离的东西(其他宏或可变函数调用),但某种日志记录的事实不能是,真的。

aspectc.org does claim to offer a C and C++ compiler with language extensions supporting AOP. I have no idea what state it's in, and if you use it then of course you're not really writing C (or C++) any more.

Remember that C++ has multiple inheritance, which is sometimes helpful with cross-cutting concerns. With enough templates you can do remarkable things, perhaps even implementing your own method dispatch system that allows some sort of join points, but it's a big thing to take on.

于 2010-11-14T16:34:13.763 回答
4

在 GCC 上,您可以使用可变参数宏:http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html。可以dprintf()使用任意数量的参数进行定义。

使用附加隐藏verbose_level参数,您可以过滤消息。

在这种情况下,日志记录逻辑将仅包含

dprintf_cond(flags_or_verbose_level, msg, param1, param2);

并且无需将其与其余代码分开。

于 2010-11-14T16:33:14.587 回答
1

一个标志和正确的逻辑可能是更安全的方法,但你可以在编译类型时做同样的事情。IE。使用#define 和#ifdef 包含/排除printfs。

于 2010-11-14T16:29:35.757 回答
1

Hmm, this sounds similar to a problem I encountered when working on a C++ project last summer. It was a distributed app which had to be absolutely bulletproof and this resulted in a load of annoying exception handling bloat. A 10 line function would double in size by the time you added an exception or two, because each one involved building a stringstream from a looong exception string plus any relevant parameters, and then actually throwing the exception maybe five lines later.

So I ended up building a mini exception handling framework which meant I could centralise all my exception messages inside one class. I would initialise that class with my (possibly parameterised) messages on startup, and this allowed me to write things like throw CommunicationException(28, param1, param2) (variadic arguments). I think I'll catch a bit of flak for that, but it made the code infinitely more readable. The only danger, for example, was that you could inadvertently throw that exception with message #27 rather than #28.

于 2010-11-14T16:47:07.297 回答
0
#ifndef DEBUG_OUT

# define DBG_MGS(level, format, ...) 
# define DBG_SET_LEVEL(x) do{}while(0)

#else

extern int dbg_level;
# define DBG_MSG(level, format, ...)              \
   do {                                           \
      if ((level) >= dbg_level) {                 \
          fprintf(stderr, (format), ## __VA_ARGS__); \
      }                                           \
   } while (0)
# define DBG_SET_LEVEL(X) do { dbg_level = (X); } while (0)

#endif

The ## before __VA_ARGS__ is a GCC specific thing that makes , __VA_ARGS__ actually turn into the right code when there are no actual extra arguments.

The do { ... } while (0) stuff is just to make you put ; after the statements when you use them, like you do when you call regular functions.

If you don't want to get as fancy you can do away with the debug level part. That just makes it so that if you want you can alter the level of debugging/tracing date you want.

You could turn the entire print statement into a separate function (either inline or a regular one) that would be called regardless of the debug level, and would make the decision as to printing or not internally.

#include <stdarg.h>
#include <stdio.h>

int dbg_level = 0;

void DBG_MGS(int level, const char *format, ...) {
    va_list ap;
    va_start(ap, format);
    if (level >= dbg_level) {
        vfprintf(stderr, format, ap);
    }
    va_end(ap);
}

If you are using a *nix system then you should have a look at syslog.

You might also want to search for some tracing libraries. There are a few that do similar things to what I have outlined.

于 2010-11-14T22:44:58.563 回答