23

I'm trying to generate include file name in macro. This is supposed to be legal in C++:

#define INCLUDE_FILE "module_impl_win.hpp"
#include INCLUDE_FILE

this works fine, but as soon as I try to generated file name it failes to compile

#define INCLUDE_FILE(M) M##"_impl_win.hpp"
#include INCLUDE_FILE("module")

Actually it gives me warning on MSVC2010

warning C4067: unexpected tokens following preprocessor directive - expected a newlin

but it doesn't include the file.

What is the problem? How can I get rid of it?

unwind
  • 391,730
  • 64
  • 469
  • 606
ledokol
  • 461
  • 1
  • 4
  • 13

3 Answers3

21

I'd do this using a helper quoting macro. Something like this will give you what you want:

#define QUOTEME(M)       #M
#define INCLUDE_FILE(M)  QUOTEME(M##_impl_win.hpp)

#include INCLUDE_FILE(module)
Andrew
  • 226
  • 1
  • 3
  • 2
    You have to be careful with this approach. `_impl_win` is an identifier token and since it begins with an underscore (and is in the macro namespace), it is reserved to the implementation. You should break off the leading `_` and concatenate it separately, though off the top of my head I'm not sure how to do that here since `_` itself is a reserved identifier. – James McNellis Jan 20 '11 at 06:37
  • @James McNellis: Standard reserves only names with an underscore followed by uppercase character (or names containing double underscore). I don't know why Sutter recommends to avoid using underscore everywhere.. – ledokol Jan 20 '11 at 06:38
  • @ledokol: _Any_ name beginning with an underscore is reserved _in the global namespace_ (and the macro namespace, which is a "global" namespace of sorts). As for portability, in general this approach should be portable, though there are some peculiarities in how the expansion of macros in include directives is specified. – James McNellis Jan 20 '11 at 06:40
  • @James McNellis: Just checked the standard. Yes, sorry. you were right (Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace). Maybe then it's better to include "module_" and concatenate it with impl_win.hpp? – ledokol Jan 20 '11 at 06:45
  • I'm wary of what I just said. If those names can be used by the implementation during preprocessing, they are in a sense reserved everywhere, which isn't intended. I think this usage is safe, so long as the first letter after the underscore is not a capital letter. That said, moving the underscore to the end of "module" as you show would without a doubt be quite safe. – James McNellis Jan 20 '11 at 06:48
  • 2
    For one thing, things like `QUOTEME(M##.hpp)` or `QUOTEME(M##/impl.hpp)` do not work. – musiphil Sep 13 '13 at 18:51
4

You can also use Boost Preprocessor, especially the BOOST_PP_STRINGIZE/BOOST_PP_CAT macros:

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>

#define INCLUDE_FILE(M) BOOST_PP_STRINGIZE(BOOST_PP_CAT(M, _impl_win.hpp))

// expands to: #include "module_impl_win.hpp"
#include INCLUDE_FILE(module)

(cf. C Macro - Dynamic #include)

Note that this suffers from the same problem as @Andrew's answer (leading underscore = reserved identifier).

Community
  • 1
  • 1
BenC
  • 8,729
  • 3
  • 49
  • 68
3

The problem is that eventually your code would look like this:

#include "module""_impl_win.hpp"

This generates the same warning and error you are seeing but in a slightly more obvious way.

While the compiler will accept that syntax, the preprocessor will not.

I don't have a suggestion to make what you are trying to do work. Personally wouldn't want to use that type of macro since it makes it more difficult to navigate the code visually and it would likely inhibit many editors from quickly navigating the code.

Robert Horvick
  • 3,966
  • 21
  • 18
  • Thanks, didn't know that it concatenates strings like that. Imagine that there are hundred of files that include platform specific headers with #ifdef ... #endif. If tomorrow I'll add support for one more platform then I would need to edit all of this files instead of just editing one macro and adding platform specific implementation files. That's sad. – ledokol Jan 20 '11 at 06:29
  • @ledokol: Why can the platform-specific functionality not be split out into separate headers and source files that are not included elsewhere? It should be easy to consolidate platform-specific and platform-dependent functionality into a relatively small set of files. – James McNellis Jan 20 '11 at 06:43
  • Do you mean why PIMPL is not used to separate platform specific code? Well, it can't be used everywhere. There are lot of template classes which require platform specific implementation class declaration. So I have to split it to header and implementation files. – ledokol Jan 20 '11 at 06:49
  • Just as an example. We have a thread template class, which uses thread_impl implementation class. thread_impl is implemented both for windows and unix, so there is thread_impl_win and thread_impl_nix header files. thread header file must include one of those to be able to use thread_impl class. – ledokol Jan 20 '11 at 06:54