在他的FAQ中,Bjarne Stroustrup说:
构建[Cfront,第一个C ++ 编译],我先用C写一个 “C with Classes”-to-C预处理器。 “C 与课程“是一种C方言 成为C ++的直接祖先...... 然后我写了第一个版本 “C with Classes”中的Cfront。
当我读到这篇文章时,它引起了我对 C 预处理器的兴趣。我已经看到它的宏功能适合于简化常用表达式,但没有想到它能够在我想要将类带到C的级别上显着添加语法和语义。
所以现在我有些疑问:
这种方法的其他例子是从 C 引导语言吗?
Stroustrup原创作品的来源是否可在任何地方使用?
我在哪里可以了解有关利用此技术的具体细节?
该方法的长度/限制是多少?比方说,可以创建一组预处理器宏,让某些人写出类似于Lisp / Scheme的东西吗?
答案 0 :(得分:12)
请注意,Stroustrup 不说他使用C预处理器(cpp)来创建C With Classes - 他没有。他使用C编写了自己的预处理器.Cfront是一个真正的编译器而不是预处理器。事实上,C预处理器非常不适合语言开发,因为它没有任何解析能力。
答案 1 :(得分:5)
有关您可以使用C预处理器创建的“语言”怪异性的示例,请查看此头文件:
http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h
它来自Steve Bourne编写的原始Unix shell的源代码,它旨在将C转换为类似Algol的语言。下面是一段代码在使用时的样子:
http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/args.c
这看起来很奇怪,但它仍然是C.它可能看起来像一种不同的语言,但因为它是在预处理器中实现的,所以没有语法支持,例如。
WHILE foo
DO
SWITCH
....
ENDSW
OD
非常精细且编译得很好,但
也是如此WHILE foo
DO
SWITCH
....
OD
ENDSW
答案 2 :(得分:4)
C预处理器不是您想要的。它不能像Cfront那样添加语法和语义。
Cfront是一个实际的编译器,它将带有Classes(后来是C ++)的C转换为C.它只是一个预处理器,因为它在C编译器之前运行。我使用了一个名为f2c的程序将FORTRAN 77代码转换为C代码。它的工作原理相同。
像Common Lisp这样的语言有足够的宏功能来添加新的语法和语义,还有像Forth这样的语言,系统足够灵活,可以适应变化,但这对大多数语言都不适用。
答案 3 :(得分:3)
正如其他人所说,C ++不是使用C预处理器(CPP)创建的。
那就是说,你可以用CPP和递归做疯狂的事情;我很漂亮 确定它的图灵完成。我即将链接的图书馆使用很多 获得有趣行为的丑陋技巧。虽然你可以建立一种 在最优雅,许多人可能会认为它是Turing Tarpit。
有关此内容的 gentler 简介,请尝试Cloak。
要深入了解,请看
E.g。使用Order或Chaos,您可以在纯CPP中编写递归的fibonacci序列生成器。
答案 4 :(得分:2)
我认为Objective-C以同样的方式开始。它是一个预处理器,它构建了一些C代码,然后传递给C编译器。但它不是#define FOO
意义上的C预处理器,它在标准C预处理器之前或之后作为额外步骤运行。然后可以将任意数量的预处理器步骤的结果发送给C编译器。
答案 5 :(得分:1)
听起来他的“C with Classes”-to-C预处理器与标准C预处理器不同,因为他特意说自己编写这个预处理器。
C预处理器非常有限。它可以用来为常用表达式制作缩写,但就是这样。当您尝试使用它来定义新的语言结构时,它会变得更加麻烦和脆弱。
答案 6 :(得分:0)
我建议你从GCC Macros documentation开始,它提供了很多关于C预处理器的GCC实现的有趣信息。
Clay Bridges在他的回答中提供了几个使用C预处理器的例子。关于Order语言的一个很有趣,有很多例子。 Order的作者确实提出了他/她遇到的一个问题,C预处理器实现可能无法完全实现更新的标准。
一般情况下,使用C预处理器来开发某种混淆语言,例如Steve Bourne在编写Bourne Shell for Unix时所做的一项活动,我认为这种活动适合引渡,然后是多次水上寄宿会议。
要记住关于C预处理器的主要事情是它正在操纵文本标记。因此C预处理器将允许对语法进行相当多的修改。例如,以下宏可以无错误地编译Visual Studio 2005,可以显示不直观的文本操作。
#define TESTOP(a,x,y,op,z) a (x) op (y); z
void f(void)
{
int i = 0, j = 5;
TESTOP( ,i,j,+=, );
TESTOP( ,i,(j + 2),+=, );
TESTOP({,i,(j + 2),+=,});
}
但是,在推动边界时,您确实需要了解并解决C预处理器的一些限制。有关要考虑的一些问题,请参阅GCC topic Macro Pitfalls。
您可以使用C预处理器作为一般宏和文本预处理器,它针对C编译器以外的某些工具。例如,较旧的imake utility for build automation使用C预处理器来提供广泛的宏设施。
我已经看到,最有效使用的C预处理器是简化复杂的代码和声明。
我见过的一个案例是使用C预处理器提供状态机语言,该语言用于创建描述状态机的数据结构和数据。然后将得到的数据结构用作状态机函数的参数。这允许多个不同的状态机过程以C预处理器定义的语言编写,状态机处理由单个函数完成。
Microsoft在其Microsoft基础类(MFC)中使用C预处理器隐藏了MFC的一些消息传递细节。一旦你习惯它,下面的东西相当容易阅读。由于Visual Studio IDE具有使用宏生成和修改代码的工具,因此对程序员来说非常简单。
BEGIN_MESSAGE_MAP(CFrameworkWndDoc, CWindowDocument)
//{{AFX_MSG_MAP(CFrameworkWndDoc)
ON_WM_CHAR()
ON_WM_TIMER()
ON_MESSAGE(WU_EVS_DFLT_LOAD, OnDefaultWinLoad)
ON_MESSAGE(WU_EVS_POPUP_WINDOW, OnPopupWindowByName)
ON_MESSAGE(WU_EVS_POPDOWN_WINDOW, OnPopdownWindowByName)
ON_MESSAGE(WM_APP_CONNENGINE_MSG_RCVD, OnConnEngineMsgRcvd)
ON_MESSAGE(WM_APP_XMLMSG_MSG_RCVD, OnXmlMsgRcvd)
ON_MESSAGE(WM_APP_BIOMETRIC_MSG_RCVD, OnBiometricMsgRcvd)
ON_MESSAGE(WM_APP_SHUTDOWN_MSG, OnShutdownMsgRcvd)
ON_MESSAGE(WM_POWERBROADCAST, OnPowerMsgRcvd)
ON_MESSAGE(WM_APP_SHOW_HIDE_GROUP, OnShowHideGroupMsgRcvd)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
特别是当您看到使用的宏时:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
static const AFX_MSGMAP messageMap = \
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return &messageMap; \
} \
PTM_WARNING_RESTORE
// for Windows messages
#define ON_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(memberFxn)) },
#define ON_WM_TIMER() \
{ WM_TIMER, 0, 0, 0, AfxSig_vw, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(UINT_PTR) > ( &ThisClass :: OnTimer)) },