Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C Macro - Dynamic #include

I'm trying to figure out how to build a variable string for the #include statement using GCC.

The idea is that for each source module I have written, I want to include as a header, a dynamically generated C source, that was created earlier in the build process.

Generating this file is not an issue. Including it, unfortunately, is.

What I have so far is (identities.h):

// identities.h

# define PASTER2(str)  #str
# define PASTER(str)   PASTER2(str ## .iden)
# define EVALUATOR(x)  PASTER(x)

# define IDENTITIES_FILE EVALUATOR(__FILE__)
# include IDENTITIES_FILE

Ideally, this would be used like so (main.c):

//main.c

# include "identities.h"

int main() {return 0;}

Which would be expanded in a single pass by the preprocessor before compilation to yield:

//main.c (preprocessed)

# include "main.c.iden"

int main() {return 0;}

The two levels of indirection I'm using (PASTER and EVALUATOR) are a result of this post.

Unfortunately, this is not working and I am left with the error:

obj/win32/dbg/main.o
In file included from main.c:1:0:
identities.h:42:1: error: #include expects "FILENAME" or <FILENAME>

I think the problem is that the include statement is missing quotes.. Any ideas?

like image 646
J T Avatar asked May 03 '11 18:05

J T


1 Answers

This is actually done in the Linux source tree; See line 100 of compiler-gcc.h.

#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(__GNUC__)

I'm trying to figure out how to build a variable string for the #include statement using GCC.

This token pastes the value of __GNUC__ to a string; "linux/compiler-gcc" __GNUC__ ".h" and then stringifies the result. This maybe a gcc pre-processor extension.

Here is an example,

t1.h

#define FOO 10

t2.h

#define FOO 20

a.c

#ifndef VERSION
#define VERSION 1
#endif
#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(t##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(VERSION)
#include <stdio.h>

int main(void)
{
        printf("FOO is %d\n", FOO);
        return 0;
}

Here are two compiles,

g++ -o a a.cc
g++ -DVERSION=2 -o a a.cc

The output of either compile gives expected result.

As with the Linux source, you can key off of gcc pre-defined values. echo | g++ -dM -E - will give a list.

For your case, you can use the makefile to pass a define to the compile to allow dynamic inclusion of the generated header without altering the source. But then a simple alternative is just to run sed, etc on a template source file and replace it with the known include name.

Either technique is good for generating test fixtures, etc. However, for compiler feature discovery, this is a better method. For programmers who use IDEs, this might be their only choice.

like image 141
artless noise Avatar answered Sep 23 '22 01:09

artless noise