用宏构造#include指令的路径

时间:2015-08-18 07:30:27

标签: c++ macros include boost-preprocessor

我希望为我的程序中依赖于目标配置的部分提供宏动态创建的包含文件路径。

例如,我想构造一个可以像这样调用的宏:

#include TARGET_PATH_OF(header.h)

将扩展为类似的内容:

#include "corefoundation/header.h"

为OSX配置源(在本例中)

到目前为止,所有尝试都失败了。我希望以前有人这样做过吗?

不起作用的例子:

#include <iostream>
#include <boost/preprocessor.hpp>

#define Dir directory/
#define File filename.h

#define MakePath(f) BOOST_PP_STRINGIZE(BOOST_PP_CAT(Dir,f))
#define MyPath MakePath(File)

using namespace std;

int main() {
    // this is a test - yes I know I could just concatenate strings here
    // but that is not the case for #include
    cout << MyPath << endl;
}

错误:

./enableif.cpp:31:13: error: pasting formed '/filename', an invalid preprocessing token
    cout << MyPath << endl;
            ^
./enableif.cpp:26:16: note: expanded from macro 'MyPath'
#define MyPath MakePath(File)
               ^
./enableif.cpp:25:40: note: expanded from macro 'MakePath'
#define MakePath(f) BOOST_PP_STRINGIZE(BOOST_PP_CAT(Dir,f))
                                       ^
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
#    define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
                               ^
/usr/local/include/boost/preprocessor/cat.hpp:29:36: note: expanded from macro 'BOOST_PP_CAT_I'
#    define BOOST_PP_CAT_I(a, b) a ## b
                                   ^
1 error generated.

4 个答案:

答案 0 :(得分:11)

我倾向于同意utnapistim's answer中的评论,即使可以,也不应该这样做。但实际上,您可以使用符合标准的C编译器。 [注1]

有两个问题需要克服。第一个是你不能使用##运算符来创建一个不是有效的预处理器标记的东西,并且路径名不符合有效的预处理器标记,因为它们包括 / 字符。 (如果令牌以数字开头,就可以了,但 / 永远不会有效。)

您实际上不需要连接标记以便使用#运算符对它们进行字符串化,因为该运算符将对整个宏参数进行字符串化,并且参数可能包含多个标记。但是,stringify尊重空格[注2],因此STRINGIFY(Dir File)不起作用;它将导致"directory/ filename.h",文件名中的无关空间将导致#include失败。因此,您需要在没有任何空格的情况下联接DirFile

以下通过使用类似函数的宏来解决第二个问题,该宏只返回其参数:

#define IDENT(x) x
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define PATH(x,y) STR(IDENT(x)IDENT(y))

#define Dir sys/
#define File socket.h

#include PATH(Dir,File)

请注意,将保留对PATH的调用中的空格字符。所以Path(Dir, File)会失败。

当然,如果你可以编写没有空格的连接,你就不需要IDENT宏的复杂性。例如:

#define XSTR(x) #x
#define STR(x) XSTR(x)

#define Dir sys
#define File socket.h

#include STR(Dir/File)

注释

  1. 我用godbolt上的clang,gcc和icc尝试过它。我不知道它是否适用于Visual Studio。

  2. 更准确地说,它半空白:空格被转换为单个空格字符。

答案 1 :(得分:1)

  

我希望为我的程序中依赖于目标配置的部分提供宏动态创建的包含文件路径。

您应该无法(如果您能够这样做,您可能不应该这样做。)

您正在尝试在源文件中执行编译器的工作,这没有多大意义。如果要根据编译的机器更改包含路径,这是一个已解决的问题(但未在头文件中解决)。

规范解决方案:

在Makefile或CMakeLists.txt中使用IF,根据Visual Studio中的构建配置使用自定义属性页(或者只是为用户在OS环境中设置构建的特定设置)。

然后,将include指令写为:

from django.db.models import Func, F

# It's better to build some library of used database functions first:
class Radians(Func):
    function = 'RADIANS'

class Sin(Func):
    function = 'SIN'

class Cos(Func):
    function = 'COS'

class Acos(Func):
    function = 'ACOS'

def posDifference(lat1, lon1, lat2, lon2):

    EARTH_RADIUS_IN_MILES = 3958.761

    lat1 = F(lat1)
    lon1 = F(lon1)
    lat2 = float(lat2)
    lon2 = float(lon2)

    lat1 = Radians(lat1)
    lat2 = radians(lat2)

    delta_lon = Radians(lon1) - radians(lon2)
    cos_x = (Sin(lat1) * sin(lat2) + Cos(lat1) * cos(lat2) * Cos(delta_lon)) # Distance formula
    distance = Acos(cos_x) * EARTH_RADIUS_IN_MILES # Conversion to miles

    return distance

并依赖环境/构建系统在调用编译器时使路径可用。

答案 2 :(得分:0)

根据您的描述,听起来您发现不是每个""都是字符串。特别是,#include "corefoundation/header.h"看起来像一个普通的字符串,但事实并非如此。在语法上,引用文本外部预处理程序指令适用于编译器,并编译为空终止字符串文字。预处理器指令中的带引号的文本由预处理器以实现定义的方式解释。

也就是说,示例中的错误是因为Boost粘贴了第二个和第三个标记:/filename。第一个,第四个和第五个标记(directory.h)保持不变。显然,这不是你想要的。

依赖自动字符串连接要容易得多。 "directory/" "filename""directory/filename"的字符串文字相同。请注意,两个片段之间没有+。

答案 3 :(得分:0)

适用于VS2013。 (当然,这可以更容易。)

#define myIDENT(x) x
#define myXSTR(x) #x
#define mySTR(x) myXSTR(x)
#define myPATH(x,y) mySTR(myIDENT(x)myIDENT(y))

#define myLIBAEdir D:\\Georgy\\myprojects\\LibraryAE\\build\\native\\include\\ //here whitespace!
#define myFile libae.h

#include myPATH(myLIBAEdir,myFile)