Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C Preprocessor: Own implementation for __COUNTER__

I'm currently using the __COUNTER__ macro in my C library code to generate unique integer identifiers. It works nicely, but I see two issues:

  • It's not part of any C or C++ standard.
  • Independent code that also uses __COUNTER__ might get confused.

I thus wish to implement an equivalent to __COUNTER__ myself.

Alternatives that I'm aware of, but do not want to use:

  • __LINE__ (because multiple macros per line wouldn't get unique ids)
  • BOOST_PP_COUNTER (because I don't want a boost dependency)

BOOST_PP_COUNTER proves that this can be done, even though other answers claim it is impossible.

In essence, I'm looking for a header file "mycounter.h", such that

#include "mycounter.h"

__MYCOUNTER__
__MYCOUNTER__ __MYCOUNTER__
__MYCOUNTER__

will be preprocessed by gcc -E to

(...)

0
1 2
3

without using the built-in __COUNTER__.

Note: Earlier, this question was marked as a duplicate of this, which deals with using __COUNTER__ rather than avoiding it.

like image 261
mic_e Avatar asked Mar 27 '14 16:03

mic_e


People also ask

What is __ function __ in C++?

(C++11) The predefined identifier __func__ is implicitly defined as a string that contains the unqualified and unadorned name of the enclosing function. __func__ is mandated by the C++ standard and is not a Microsoft extension.

Is #ifdef a preprocessor in C?

The #ifdef is one of the widely used directives in C. It allows conditional compilations. During the compilation process, the preprocessor is supposed to determine if any provided macros exist before we include any subsequent code.

What is preprocessor command in C?

We can consider a preprocessor as a compilation process, which runs when the developer runs the program. It is a pre-process of execution of a program using c/c++ language. To initialize a process of preprocessor commands, it's mandated to define with a hash symbol (#).

What is #ifdef C?

The #ifdef identifier statement is equivalent to #if 1 when identifier has been defined. It's equivalent to #if 0 when identifier hasn't been defined, or has been undefined by the #undef directive.


2 Answers

You can't implement __COUNTER__ directly. The preprocessor is purely functional - no state changes. A hidden counter is inherently impossible in such a system. (BOOST_PP_COUNTER does not prove what you want can be done - it relies on #include and is therefore one-per-line only - may as well use __LINE__. That said, the implementation is brilliant, you should read it anyway.)

What you can do is refactor your metaprogram so that the counter could be applied to the input data by a pure function. e.g. using good ol' Order:

#include <order/interpreter.h>

#define ORDER_PP_DEF_8map_count  \
ORDER_PP_FN(8fn(8L, 8rec_mc(8L, 8nil, 0)))

#define ORDER_PP_DEF_8rec_mc     \
ORDER_PP_FN(8fn(8L, 8R, 8C,      \
                8if(8is_nil(8L), \
                    8R,          \
                    8let((8H, 8seq_head(8L))  \
                         (8T, 8seq_tail(8L))  \
                         (8D, 8plus(8C, 1)),  \
                          8if(8is_seq(8H),    \
                              8rec_mc(8T, 8seq_append(8R, 8seq_take(1, 8L)), 8C),  \
                              8rec_mc(8T, 8seq_append(8R, 8seq(8C)), 8D) )))))

ORDER_PP (
  8map_count(8seq( 8seq(8(A)), 8true, 8seq(8(C)), 8true, 8true ))  //((A))(0)((C))(1)(2)
)

(recurses down the list, leaving sublist elements where they are and replacing non-list elements - represented by 8false - with an incrementing counter variable)

I assume you don't actually want to simply drop __COUNTER__ values at the program toplevel, so if you can place the code into which you need to weave __COUNTER__ values inside a wrapper macro that splits it into some kind of sequence or list, you can then feed the list to a pure function similar to the example.

Of course a metaprogramming library capable of expressing such code is going to be significantly less portable and maintainable than __COUNTER__ anyway. __COUNTER__ is supported by Intel, GCC, Clang and MSVC. (not everyone, e.g. pcc doesn't have it, but does anyone even use that?) Arguably if you demonstrate the feature in use in real code, it makes a stronger case to the standardisation committee that __COUNTER__ should become part of the next C standard.

like image 80
Leushenko Avatar answered Sep 29 '22 07:09

Leushenko


You are confusing two different things:

1 - the preprocessor which handles#define and #include like stuff. It does only works as the text (meaning character sequences) level and has very few computing capabilities. It is so limited that it cannot implement __COUNTER__. The preprocessor work consist only in macro expansion and file replacement. The crucial point it that it occur before the compilation even start.

2 - the C++ language and in particular the template (meta)programming language which can be used to compute stuff during the compilation phase. It is indeed turing complete but as I already said compilation start after preprocessing.

So what you are asking is not doable in standard C or C++. To solve this problem boost implement its own preprocessor which is not standard compliant and has much more computing capabilities. In particular it is possible to use build an analogue to __counter__ with it.

like image 24
hivert Avatar answered Sep 29 '22 05:09

hivert