Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing compile-time mechanism checking uniqueness of a string

The simplest way of defining my problem is that I'm trying to implement a mechanism that would check whether the same string had already been used (or a pair (number, string)). I would like this mechanism to be implemented in a smart way using C preprocessor. I would also like that this mechanism gave me compile errors when there is a conflict or run-time errors in Debug mode (by checking assertions). We don't want the developer to make a mistake when adding a message, as every message should be unique. I know that it could be done by calculating a hash or for example crc/md5 but this mechanism would be conflict-vulnerable which I need to avoid. It is crucial that every message can be used only once.

Example behaviour of this mechanism:

addMessage(1, "Message1") //OK 
addMessage(2, "Message2") //OK 
. 
. 
. 
addMessage(N, "MessageN") //OK 
addMessage(2, "Message2") //Compile error, Message2 has already been used 

Alternative behaviour (when Debugging code):

addMessage(1, "Message1") //OK 
addMessage(2, "Message2") //OK 
. 
. 
. 
addMessage(N, "MessageN") //OK 
addMessage(2, "Message2") //Assertion failed, because Message2 has already been used 

The preferred way of doing it would be smart usage of #define and #undef directives. In general the preprocessor should be used in a smart way (I am not sure if this is possible) maybe it can be achieved by appropriate combinations of macros? Any C preprocessor hacker that could help me solve this problem?

//EDIT: I need those messages to be unique globally, not only inside one code block (like function of if-statement).

//EDIT2: The best description of the problem would be that I have 100 different source files and I would like to check with a preprocessor (or possibly other mechanism other than parsing source files with a script at a start of the compilation every-time, which would be very time-consuming and would add another stage to an enough complicated project) if a string (or a preprocessor definition) was used more than one time. I still have no idea how to do it (I know it may not be possible at all but I hope it actually is).

like image 865
Paweł Jastrzębski Avatar asked Dec 15 '22 20:12

Paweł Jastrzębski


2 Answers

This will give an error on duplicate strings:

constexpr bool isequal(char const *one, char const *two) {
  return (*one && *two) ? (*one == *two && isequal(one + 1, two + 1))
    : (!*one && !*two);
}

constexpr bool isunique(const char *test, const char* const* list)
{
    return *list == 0 || !isequal(test, *list) && isunique(test, list + 1);
}

constexpr int no_duplicates(const char* const* list, int idx)
{
    return *list == 0 ? -1 : (isunique(*list, list + 1) ? no_duplicates(list + 1, idx + 1) : idx);
}

template <int V1, int V2> struct assert_equality
{
    static const char not_equal_warning = V1 + V2 + 1000;
};

template <int V> struct assert_equality<V, V>
{
    static const bool not_equal_warning = 0;
};

constexpr const char* l[] = {"aa", "bb", "aa", 0};
static_assert(assert_equality<no_duplicates(l, 0), -1>::not_equal_warning == 0, "duplicates found");

Output from g++:

g++ -std=c++11 unique.cpp 
unique.cpp: In instantiation of ‘const char assert_equality<0, -1>::not_equal_warning’:
unique.cpp:29:57:   required from here
unique.cpp:20:53: warning: overflow in implicit constant conversion [-Woverflow]
unique.cpp:29:1: error: static assertion failed: duplicates found

The first template parameter (in this case 0) to 'assert_equality' tells you the fist position of a duplicate string.

like image 79
Markus Lenger Avatar answered May 03 '23 23:05

Markus Lenger


I am not sure that it is easily doable using the standard C++ preprocessor (I guess that it is not). You might use some other preprocessor (e.g. GPP)

You could make it the other way: generate some X-macro "header" file from some other source (using e.g. a tiny awk script, which would verify the unicity). Then customize your build (e.g. add some rules to your Makefile) to run that generating script to produce the header file.

Alternatively, if you insist that processing being done inside the compiler, and if your compiler is a recent GCC, consider customizing GCC with MELT (e.g. by adding appropriate builtins or pragmas doing the job).

In the previous century, I hacked a small Emacs function to do a similar job (uniquely numbering error messages) within the emacs editor (renumbering some #define-s before saving the C file).

like image 45
Basile Starynkevitch Avatar answered May 04 '23 00:05

Basile Starynkevitch