How to get unique values at preprocessing across files

核能气质少年 提交于 2019-12-05 08:13:01

I would suggest that your solution is unimplementable with the standard C preprocessor, and a different solution is required. Also the proposed solution is incomplete - generating a UID alone is insufficient, you need to be able to associate that UID with the original string, and it is not clear how that is to be achieved.

It seems it would be simpler to write a separate custom preprocessor that searches the source for instances of LOG_MSG( <some_string> ), extracts <some string> and replaces it with a UID generated by your preprocessor, and builds a string table for use by the host.

The macro definition would then be:

#define LOG_MSG(a) log_msg( a )

But while in the original un-preprocessed code a will be a literal string, log_msg() will be defined log_msg( int a ).

Execution of your preprocessor will then be a necessary pre-build step before compilation proper. Most IDEs support pre-build and pre-compile steps that can be used to integrate this tool, or adding as a make rule for example is simple enough.

Any attempt to compile un-preprocessed code will fail to compile because the parameter would be a string literal rather than an integer, so there is no danger of omitting to perform the pre-processing.

The only thing you will need to ensure than is that you use the string-table on the host that is associated with the specific build of the target code - but you had that problem in any case.

Note that it will only work when a is a literal string - but that is the case with the originally proposed solution in any case. Your pre-processor could check that and issue an error is a literal string were not passed.

In general, it's not possible to assure a unique identifier is always generated for each macro expansion.

You can semi-solve the problem by using the builtin __LINE__ macro, assuming the macro is used at most once per line. However, since you need to use it across multiple files, you'll need to manually define another macro that is unique per file. For example, at the top of foo.c:

#define FILE_NAME foo
#include "log.h" // or whatever defines `LOG_MSG`

Then, you can use macro concatenation on FILE_NAME and __LINE__ to generate a unique identifier.

If just a string will suffice (instead of an actual variable), you can use the __FILE__ macro as well, combined with the "stringized" version of __LINE__ like so:

#define FILE_LINE(x, y)  FILE_LINE_(x, y)
#define FILE_LINE_(x, y) x ## #y
#define LOG_UNIQUE_ID_STRING FILE_LINE(__FILE__, __LINE__)

Again, this assumes at most one invocation per logical line.

Some compilers also support a __COUNTER__ macro, which can be used in place of __LINE__, which allows for multiple invocations per logical line (but again, you need to combine it with something else, since __COUNTER__ is not unique across multiple translation units (aka C files)).

While its abuse of pre processor directives, you can generate pseudo random numbers as mentioned here and concatenate with another macro like __FILE__ or __LINE__, to generate a unique string.

__COUNTER__ is the easy but nonstandard way of doing this.

The Boost Preprocessor library will also be useful. For example, the following "myheader.h" header file will output a unique label wherever it's included.

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/slot/slot.hpp>

#if !defined(MYUNIQID)
#define MYUNIQID
#define BOOST_PP_VALUE 1
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#else
#define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1))
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#endif


BOOST_PP_CAT(__FILE__, BOOST_PP_SLOT(1)):

Your unique id will be generated at the line wherever you #include "myheader.h"

Why not give the #line preprocessor directive a try? You can give unique starting line numbers to each of your source files and then use __LINE__ for your unique identifier. You would have to be certain that you maintain enough lines between each file so that all of the numbers remain unique. Here is an example:

file_identifiers.h

/* Maintain 10,000 lines between each file.  If a file has greater
   than 10,000 lines then these numbers will have to be increased. */
#define FILE_IDENTIFIER_MAIN    10000
#define FILE_IDENTIFIER_LOG     20000

log.h

#include <stdint.h>
void log_msg (int32_t unique_id);
void log_test (void);

log.c

#include <stdio.h>
#include "log.h"
#include "file_identifiers.h"

#line FILE_IDENTIFIER_LOG   //log.c now starts with line 20000

void log_msg (int32_t unique_id)
{
    printf("Unique ID: %d\r\n", unique_id);
}

void log_test (void)
{
    log_msg(__LINE__);
}

main.c

#include <stdlib.h>
#include "file_identifiers.h"
#include "log.h"

#line FILE_IDENTIFIER_MAIN    //main.c now starts with line 10000

int main (void)
{
    log_msg(__LINE__);
    log_test();
    exit (EXIT_SUCCESS);
}

OUTPUT

Unique ID: 10003
Unique ID: 20008
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!