Matching list of #define to a pattern - c++

I have an header with a lot of #defines, like this:
#define XY_ABC_FOO 1
#define XY_ABC_BAR 3
#define XY_ABC_PIPPO 5
#define XY_ABC_PLUTO 7
#define XY_ABC_ETC 19
...
and so on and on.
I'd like to put all those in a vector.I can do it by hand (in a few minutes).
std::vector<int> defs = { 1, 3, 5, 7, 19 , ... }
But then, next time definition are added in the header, someone will have to remember to add them in my code too.
Is there any very clever preprocessor/metaprogramming trick to catch them all at compile time?
I don't particularly care about fast compilation, it's test code so it will be compiled seldom and mostly overnight.

You could do it with awk:
awk '/^#define XY_ABC_\w+ \d+$/ {
if(line) {
line = line ", " $2
} else {
line = "std::vector<int> defs = { " $2
}
END { print line " };" }' < header.hpp > defs.hpp
Then in your main program use #include defs.hpp to get the declaration.

You seem to want to achieve that using only #defines, but I'm almost completely sure that it's impossible.
But if you'd allow those values to be constexprs, then you'd be able to do following. (And it's probably a best thing you can get without external tools.)
#define DEFS \
C(XY_ABC_FOO, 1) \
C(XY_ABC_BAR, 3) \
C(XY_ABC_PIPPO, 5) \
C(XY_ABC_PLUTO, 7) \
C(XY_ABC_ETC, 19)
#define C(name, value) constexpr int name = value;
DEFS
#undef C
/* ... */
#define C(name, value) value,
std::vector<int> defs = {DEFS};
#undef C

Related

Fortran and cpp option : How to protect a comma?

An option is defined (value = 1 or 2) to chose between two instructions and I would like to use with an instruction which have a comma.
#define option 1
#if option == 1
#define my_instr(instr1, instr2) instr1
#else if option == 2
#define my_instr(instr1, instr2) instr2
#endif
It works but when there is a comma in the instruction, I have a problem.
For example :
program main
my_instr(print *,"opt 1", print * ,"opt 2")
end program main
does not compile (gftran -cpp) : Too much args. I am ok.
Thus, to escape the comma, parentheses are added : my_instr((print *,"opt 1"), (print * ,"opt 2"))
But it does not compile any more because of parentheses.
How can I solve that ?
Using the answer (https://stackoverflow.com/a/46311121/7462275), I found a " solution ".
#define option 2
#define unparen(...) __VA_ARGS__
#if option == 1
#define my_instr(instr1, instr2) unparen instr1
#elif option == 2
#define my_instr(instr1, instr2) unparen instr2
#endif
program main
my_instr((print *,"opt 1"), (print * ,"opt 2"))
end program main
But,
gfortran -cpp does not compile (problem with __VA_ARGS__). So, cpp -P is used before gfortran
__VA_ARGS__ : It is not standard to use __VA_ARGS__ without something before (cf comments of :Matthew D. Scholefield in the answer used :
Just something to note, this still doesn't conform to the C standard because of the use of a variadic macro.
and KamilCuk in this question)
Even instructions without comma need to be enclosed between parentheses
This will select the correct string.
#ifdef my_instr
#undef my_instr
#endif
#define my_instr(x) print *, x
#if option == 1
#define str "Opt 1"
#else if option == 2
#define str "Opt 2"
#endif
program foo
my_instr(str)
end program foo
% gfortran -E -Doption=2 a.F90 | cat -s
# 1 "a.F90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "a.F90"
program foo
print *, "Opt 2"
end program foo

C macro that concats two templated types [duplicate]

I faced a problem - I need to use a macro value both as string and as integer.
#define RECORDS_PER_PAGE 10
/*... */
#define REQUEST_RECORDS \
"SELECT Fields FROM Table WHERE Conditions" \
" OFFSET %d * " #RECORDS_PER_PAGE \
" LIMIT " #RECORDS_PER_PAGE ";"
char result_buffer[RECORDS_PER_PAGE][MAX_RECORD_LEN];
/* ...and some more uses of RECORDS_PER_PAGE, elsewhere... */
This fails with a message about "stray #", and even if it worked, I guess I'd get the macro names stringified, not the values. Of course I can feed the values to the final method ( "LIMIT %d ", page*RECORDS_PER_PAGE ) but it's neither pretty nor efficient.
It's times like this when I wish the preprocessor didn't treat strings in a special way and would process their content just like normal code.
For now, I cludged it with #define RECORDS_PER_PAGE_TXT "10" but understandably, I'm not happy about it.
How to get it right?
The xstr macro defined below will stringify after doing macro-expansion.
#define xstr(a) str(a)
#define str(a) #a
#define RECORDS_PER_PAGE 10
#define REQUEST_RECORDS \
"SELECT Fields FROM Table WHERE Conditions" \
" OFFSET %d * " xstr(RECORDS_PER_PAGE) \
" LIMIT " xstr(RECORDS_PER_PAGE) ";"
#include <stdio.h>
#define RECORDS_PER_PAGE 10
#define TEXTIFY(A) #A
#define _REQUEST_RECORDS(OFFSET, LIMIT) \
"SELECT Fields FROM Table WHERE Conditions" \
" OFFSET %d * " TEXTIFY(OFFSET) \
" LIMIT " TEXTIFY(LIMIT) ";"
#define REQUEST_RECORDS _REQUEST_RECORDS(RECORDS_PER_PAGE, RECORDS_PER_PAGE)
int main() {
printf("%s\n", REQUEST_RECORDS);
return 0;
}
Outputs:
SELECT Fields FROM Table WHERE Conditions OFFSET %d * 10 LIMIT 10;
Note the indirection to _REQUEST_RECORDS to evaluate the arguments before stringifying them.
Try double escaping your quotes
#define RECORDS_PER_PAGE 10
#define MAX_RECORD_LEN 10
/*... */
#define DOUBLEESCAPE(a) #a
#define ESCAPEQUOTE(a) DOUBLEESCAPE(a)
#define REQUEST_RECORDS \
"SELECT Fields FROM Table WHERE Conditions" \
" OFFSET %d * " ESCAPEQUOTE(RECORDS_PER_PAGE) \
" LIMIT " ESCAPEQUOTE(RECORDS_PER_PAGE) ";"
char result_buffer[RECORDS_PER_PAGE][MAX_RECORD_LEN];
int main(){
char * a = REQUEST_RECORDS;
}
compiles for me. The token RECORDS_PER_PAGE will be expanded by the ESCAPEQUOTE macro call, which is then sent into DOUBLEESCAPE to be quoted.

Difference in preprocessor output

I was trying to check some single line macros which have 2 pre-processor directives.
#define REPLACE { \
#if EXT == 42 \
#warning "Got 42" \
#endif \
}
int main(void){
REPLACE;
return 0;
}
The pre-processor parses this fine yielding:
$g++ -E includetest.cpp
# 1 "includetest.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "includetest.cpp"
int main(void){
{ #if EXT == 42 #warning "Got 42" #endif };
return 0;
}
which of course is illegal code, since just macro substitution occurs and the ifdef lookalike macro isn't processed again even though it looks like one.
Now if I slightly alter the macro to look something like
#define REPLACE(a) { a + 2 ; \
#if EXT == 42 \
#warning "Got 42" \
#endif \
}
int main(void){
REPLACE(0);
return 0;
}
Which yields this pre-processor error:
$g++ -E includetest.cpp
# 1 "includetest.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "includetest.cpp"
includetest.cpp:1:18: error: '#' is not followed by a macro parameter
#define REPLACE(a) { a + 2 ; \
^
int main(void){
REPLACE(0);
return 0;
}
Why does this error come up ? Of course, this won't compile but I want to know why addition of a parameter ensues in a parsing error from pre-processor ?
People would say " You can't nest another directive in another", but in the first case too they are nested, why doesn't pre-processor error out then ? Or is that responsibility delegated to the compiler ?
EDIT: I am not trying to achieve any functionality per se, this is just an exercise (in futility?) to understand the pre-processor.
Only inside a function macro does a # have special meaning ([cpp.stringize]p1). The standard says that a # needs to be followed by a function argument, which is not happening in your second case (if, warning and endif are not parameters).
Your first case is valid (you can actually have directives inside the replacement list of object macros) exactly because there # doesn't have any special meaning.

std::to_string is not declared compile error in aix

I am compiling my code in AIX env.. it givs me error "std::to_string" is not declared
successfully compiled same code in Windows.
define LOG_MSG(message) CLogManager::LogMessage(CLogManager::CurrentDateTime() + " - " + std::string(__FILE__) + "[" + std::to_string(static_cast<_ULonglong>(__LINE__)) + "] : " + std::string(message) + "\n")
This is the macro and i am using this as
LOG_MSG(" ** BEGIN StorePasswordFromFile()");
This macro is for logging purpose
I'm not sure how much support the latest xlC 14.1 (or whatever version you're using) has for std::to_string().
If the support is incomplete, C has a hideous double macro method (a) of turning __LINE__ into a C-string so that you can just use std::string, the same as you have for the __FILE__ and message items, and it appears the C++ pre-processor has stayed faithful to its hideous roots :-)
The code:
#include <stdio.h>
#define STR1(x) # x
#define STR2(x) STR1(x)
int main(void) {
char x[] = __FILE__;
char y[] = STR2(__LINE__);
printf("file = %s, line = %s\n",x,y);
return 0;
}
outputs:
file = qq.c, line = 6
showing that the __LINE__ has been successfully morphed into a C-string value.
You should be able to use a similar method in your macro:
#define STR1(x) # x
#define STR2(x) STR1(x)
#define LOG_MSG(message) CLogManager::LogMessage( \
CLogManager::CurrentDateTime() + " - " + \
std::string(__FILE__) + "[" + \
std::string(STR2(__LINE__)) + "] : " + \
std::string(message) + "\n")
int main() {
LOG_MSG ("My hovercraft is full of eels");
return 0;
}
Pre-processing that with g++ -E qq.cpp gives you:
CLogManager::LogMessage( CLogManager::CurrentDateTime() + " - " + std::string("qq.cpp") + "[" + std::string("10") + "] : " + std::string("My hovercraft is full of eels") + "\n");
(showing relevant line only) which seems to match what you want.
As a side note however, since you seem to be okay adding C-strings like "[" without needing to construct strings explicitly, I'm not sure that you need the std::string() calls at all for those. You still need the C macro hack to turn the integer into a C-string but, once that's been done, you should just be able to use that as-is.
Changing the final macro to:
#define LOG_MSG(message) CLogManager::LogMessage( \
CLogManager::CurrentDateTime() + " - " + \
__FILE__ + "[" + \
STR2(__LINE__) + "] : " + \
message + "\n")
will give you:
CLogManager::LogMessage( CLogManager::CurrentDateTime() + " - " + "qq.cpp" + "[" + "10" + "] : " + "My hovercraft is full of eels" + "\n");
Whether that's a good idea, I'll leave to the wider community but it at least gets around your immediate problem. I'd probably place the whole lot inside an #if/#else/#endif so that C++11 compilers that know about std::to_string() can use the more accepted approach.
(a) If you're interested in why this works, I'll explain below.
The # and ## macro operators actually take precedence over the recursive nature of macro replacement, as per C11 6.10.3.4 /1:
After all parameters in the replacement list have been substituted and # and ##
processing has taken place, all placemarker preprocessing tokens are removed. The
resulting preprocessing token sequence is then rescanned, along with all subsequent
preprocessing tokens of the source file, for more macro names to replace.
That means that the code:
#define STR(x) # x
STR(__LINE__)
will actually result in "__LINE__" because the # happens first and, once that's happened, the __LINE__ within the string literal is not subject to further replacement. By doing the two-step process:
#define STR1(x) # x
#define STR2(x) STR1(x)
STR2(__LINE__)
the first level of replacement turns STR2(__LINE__) into STR1(3) because __LINE__ on its own is subject to expansion.
Then the second level turns STR1(3), via # 3, into "3".
Perhaps the following may help:
#define STR1(x) # x
#define STR2a(x) STRn(x)
#define STR2b(x) STR1(x)
STR1(__LINE__)
STR2a(__LINE__)
STR2b(__LINE__)
The output of that, annotated, is:
"__LINE__" - stringise, no further processing of __LINE__ inside literal.
STRn(6) - shows first stage of replacement, line number replacement.
"7" - shows full process.

Remove comments from C/C++ code

Is there an easy way to remove comments from a C/C++ source file without doing any preprocessing. (ie, I think you can use gcc -E but this will expand macros.) I just want the source code with comments stripped, nothing else should be changed.
EDIT:
Preference towards an existing tool. I don't want to have to write this myself with regexes, I foresee too many surprises in the code.
Run the following command on your source file:
gcc -fpreprocessed -dD -E test.c
Thanks to KennyTM for finding the right flags. Here’s the result for completeness:
test.c:
#define foo bar
foo foo foo
#ifdef foo
#undef foo
#define foo baz
#endif
foo foo
/* comments? comments. */
// c++ style comments
gcc -fpreprocessed -dD -E test.c:
#define foo bar
foo foo foo
#ifdef foo
#undef foo
#define foo baz
#endif
foo foo
It depends on how perverse your comments are. I have a program scc to strip C and C++ comments. I also have a test file for it, and I tried GCC (4.2.1 on MacOS X) with the options in the currently selected answer - and GCC doesn't seem to do a perfect job on some of the horribly butchered comments in the test case.
NB: This isn't a real-life problem - people don't write such ghastly code.
Consider the (subset - 36 of 135 lines total) of the test case:
/\
*\
Regular
comment
*\
/
The regular C comment number 1 has finished.
/\
\/ This is not a C++/C99 comment!
This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.
/\
\* This is not a C or C++ comment!
This is followed by regular C comment number 2.
/\
*/ This is a regular C comment *\
but this is just a routine continuation *\
and that was not the end either - but this is *\
\
/
The regular C comment number 2 has finished.
This is followed by regular C comment number 3.
/\
\
\
\
* C comment */
On my Mac, the output from GCC (gcc -fpreprocessed -dD -E subset.c) is:
/\
*\
Regular
comment
*\
/
The regular C comment number 1 has finished.
/\
\/ This is not a C++/C99 comment!
This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.
/\
\* This is not a C or C++ comment!
This is followed by regular C comment number 2.
/\
*/ This is a regular C comment *\
but this is just a routine continuation *\
and that was not the end either - but this is *\
\
/
The regular C comment number 2 has finished.
This is followed by regular C comment number 3.
/\
\
\
\
* C comment */
The output from 'scc' is:
The regular C comment number 1 has finished.
/\
\/ This is not a C++/C99 comment!
This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.
/\
\* This is not a C or C++ comment!
This is followed by regular C comment number 2.
The regular C comment number 2 has finished.
This is followed by regular C comment number 3.
The output from 'scc -C' (which recognizes double-slash comments) is:
The regular C comment number 1 has finished.
/\
\/ This is not a C++/C99 comment!
This is followed by C++/C99 comment number 3.
The C++/C99 comment number 3 has finished.
/\
\* This is not a C or C++ comment!
This is followed by regular C comment number 2.
The regular C comment number 2 has finished.
This is followed by regular C comment number 3.
Source for SCC now available on GitHub
The current version of SCC is 6.60 (dated 2016-06-12), though the Git versions were created on 2017-01-18 (in the US/Pacific time zone). The code is available from GitHub at https://github.com/jleffler/scc-snapshots. You can also find snapshots of the previous releases (4.03, 4.04, 5.05) and two pre-releases (6.16, 6.50) — these are all tagged release/x.yz.
The code is still primarily developed under RCS. I'm still working out how I want to use sub-modules or a similar mechanism to handle common library files like stderr.c and stderr.h (which can also be found in https://github.com/jleffler/soq).
SCC version 6.60 attempts to understand C++11, C++14 and C++17 constructs such as binary constants, numeric punctuation, raw strings, and hexadecimal floats. It defaults to C11 mode operation. (Note that the meaning of the -C flag — mentioned above — flipped between version 4.0x described in the main body of the answer and version 6.60 which is currently the latest release.)
gcc -fpreprocessed -dD -E did not work for me but this program does it:
#include <stdio.h>
static void process(FILE *f)
{
int c;
while ( (c=getc(f)) != EOF )
{
if (c=='\'' || c=='"') /* literal */
{
int q=c;
do
{
putchar(c);
if (c=='\\') putchar(getc(f));
c=getc(f);
} while (c!=q);
putchar(c);
}
else if (c=='/') /* opening comment ? */
{
c=getc(f);
if (c!='*') /* no, recover */
{
putchar('/');
ungetc(c,f);
}
else
{
int p;
putchar(' '); /* replace comment with space */
do
{
p=c;
c=getc(f);
} while (c!='/' || p!='*');
}
}
else
{
putchar(c);
}
}
}
int main(int argc, char *argv[])
{
process(stdin);
return 0;
}
There is a stripcmt program than can do this:
StripCmt is a simple utility written in C to remove comments from C, C++, and Java source files. In the grand tradition of Unix text processing programs, it can function either as a FIFO (First In - First Out) filter or accept arguments on the command line.
(per hlovdal's answer to: question about Python code for this)
This is a perl script to remove //one-line and /* multi-line */ comments
#!/usr/bin/perl
undef $/;
$text = <>;
$text =~ s/\/\/[^\n\r]*(\n\r)?//g;
$text =~ s/\/\*+([^*]|\*(?!\/))*\*+\///g;
print $text;
It requires your source file as a command line argument.
Save the script to a file, let say remove_comments.pl
and call it using the following command: perl -w remove_comments.pl [your source file]
Hope it will be helpful
I had this problem as well. I found this tool (Cpp-Decomment) , which worked for me. However it ignores if the comment line extends to next line. Eg:
// this is my comment \
comment continues ...
In this case, I couldn't find a way in the program so just searched for ignored lines and fixed in manually. I believe there would be an option for that or maybe you could change the program's source file to do so.
Because you use C, you might want to use something that's "natural" to C. You can use the C preprocessor to just remove comments. The examples given below work with the C preprocessor from GCC. They should work the same or in similar ways with other C perprocessors as well.
For C, use
cpp -dD -fpreprocessed -o output.c input.c
It also works for removing comments from JSON, for example like this:
cpp -P -o - - <input.json >output.json
In case your C preprocessor is not accessible directly, you can try to replace cpp with cc -E, which calls the C compiler telling it to stop after the preprocessor stage.
In case your C compiler binary is not cc you can replace cc with the name of your C compiler binary, for example clang. Note that not all preprocessors support -fpreprocessed.
I write a C program using standard C library, around 200 lines, which removes comments of C source code file.
qeatzy/removeccomments
behavior
C style comment that span multi-line or occupy entire line gets zeroed out.
C style comment in the middle of a line remain unchanged. eg, void init(/* do initialization */) {...}
C++ style comment that occupy entire line gets zeroed out.
C string literal being respected, via checking " and \".
handles line-continuation. If previous line ending with \, current line is part of previous line.
line number remain the same. Zeroed out lines or part of line become empty.
testing & profiling
I tested with largest cpython source code that contains many comments.
In this case it do the job correctly and fast, 2-5 faster than gcc
time gcc -fpreprocessed -dD -E Modules/unicodeobject.c > res.c 2>/dev/null
time ./removeccomments < Modules/unicodeobject.c > result.c
usage
/path/to/removeccomments < input_file > output_file
I Believe If you use one statement you can easily remove Comments from C
perl -i -pe ‘s/\\\*(.*)/g’ file.c This command Use for removing * C style comments
perl -i -pe 's/\\\\(.*)/g' file.cpp This command Use for removing \ C++ Style Comments
Only Problem with this command it cant remove comments that contains more than one line.but by using this regEx you can easily implement logic for Multiline Removing comments
Recently I wrote some Ruby code to solve this problem. I have considered following exceptions:
comment in strings
multiple line comment on one line, fix greedy match.
multiple lines on multiple lines
Here is the code:
It uses following code to preprocess each line in case those comments appear in strings. If it appears in your code, uh, bad luck. You can replace it with a more complex strings.
MUL_REPLACE_LEFT = "MUL_REPLACE_LEFT"
MUL_REPLACE_RIGHT = "MUL_REPLACE_RIGHT"
SIG_REPLACE = "SIG_REPLACE"
USAGE: ruby -w inputfile outputfile
I know it's late, but I thought I'd share my code and my first attempt at writing a compiler.
Note: this does not account for "\*/" inside a multiline comment e.g /\*...."*/"...\*. Then again, gcc 4.8.1 doesn't either.
void function_removeComments(char *pchar_sourceFile, long long_sourceFileSize)
{
long long_sourceFileIndex = 0;
long long_logIndex = 0;
int int_EOF = 0;
for (long_sourceFileIndex=0; long_sourceFileIndex < long_sourceFileSize;long_sourceFileIndex++)
{
if (pchar_sourceFile[long_sourceFileIndex] == '/' && int_EOF == 0)
{
long_logIndex = long_sourceFileIndex; // log "possible" start of comment
if (long_sourceFileIndex+1 < long_sourceFileSize) // array bounds check given we want to peek at the next character
{
if (pchar_sourceFile[long_sourceFileIndex+1] == '*') // multiline comment
{
for (long_sourceFileIndex+=2;long_sourceFileIndex < long_sourceFileSize; long_sourceFileIndex++)
{
if (pchar_sourceFile[long_sourceFileIndex] == '*' && pchar_sourceFile[long_sourceFileIndex+1] == '/')
{
// since we've found the end of multiline comment
// we want to increment the pointer position two characters
// accounting for "*" and "/"
long_sourceFileIndex+=2;
break; // terminating sequence found
}
}
// didn't find terminating sequence so it must be eof.
// set file pointer position to initial comment start position
// so we can display file contents.
if (long_sourceFileIndex >= long_sourceFileSize)
{
long_sourceFileIndex = long_logIndex;
int_EOF = 1;
}
}
else if (pchar_sourceFile[long_sourceFileIndex+1] == '/') // single line comment
{
// since we know its a single line comment, increment file pointer
// until we encounter a new line or its the eof
for (long_sourceFileIndex++; pchar_sourceFile[long_sourceFileIndex] != '\n' && pchar_sourceFile[long_sourceFileIndex] != '\0'; long_sourceFileIndex++);
}
}
}
printf("%c",pchar_sourceFile[long_sourceFileIndex]);
}
}
#include<stdio.h>
{
char c;
char tmp = '\0';
int inside_comment = 0; // A flag to check whether we are inside comment
while((c = getchar()) != EOF) {
if(tmp) {
if(c == '/') {
while((c = getchar()) !='\n');
tmp = '\0';
putchar('\n');
continue;
}else if(c == '*') {
inside_comment = 1;
while(inside_comment) {
while((c = getchar()) != '*');
c = getchar();
if(c == '/'){
tmp = '\0';
inside_comment = 0;
}
}
continue;
}else {
putchar(c);
tmp = '\0';
continue;
}
}
if(c == '/') {
tmp = c;
} else {
putchar(c);
}
}
return 0;
}
This program runs for both the conditions i.e // and /...../