简体   繁体   English

C / C ++中的句法糖

[英]Syntactic sugar in C/C++

I have been looking into Ruby and find its keywords "until" and "unless" very interesting. 我一直在研究Ruby并发现它的关键字“直到”和“除非”非常有趣。 So I thought what was a good way to add similar keywords into C/C++. 所以我认为在C / C ++中添加类似关键字的好方法是什么。 This is what I came up with: 这就是我想出的:

#define until(x)    while(!(x))
#define unless(x)   if(!(x))

I am looking for some suggestions on this. 我正在寻找一些建议。 Can anyone suggest a better alternative? 谁能提出更好的选择?

Here is an example of a program that I wrote to illustrate what I intended to do: 这是我编写的一个程序示例,用于说明我打算做什么:

#include <stdio.h>
#include <stdlib.h>

#define until(x)    while(!(x))
#define unless(x)   if(!(x))

unsigned int factorial(unsigned int n) {
    unsigned int fact=1, i;
    until ( n==0 )
        fact *= n--;
    return fact;    
}

int main(int argc, char*argv[]) {
    unless (argc==2)
        puts("Usage: fact <num>");
    else {
        int n = atoi(argv[1]);
        if (n<0)
            puts("please give +ve number");
        else
            printf("factorial(%u) = %u\n",n,factorial(n));
    }
    return 0;
}

It would be great if you could point me to some references for similar tricks that can be employed in C or C++. 如果你能指出一些可以在C或C ++中使用的类似技巧的参考文献,那将是很棒的。

Can anyone suggest a better alternative? 谁能提出更好的选择?

Yes. 是。 Don't do this at all. 根本不要这样做。 Just use the while and if statements directly. 只需直接使用whileif语句即可。

When you're programming in C or C++, program in C or C++. 当您使用C或C ++编程时,请使用C或C ++编程。 While until and unless might be used frequently and idiomatic in some languages, they are not in C or C++. 虽然until并且unless可能会经常和习惯在一些语言中使用的,他们不是在C或C ++。

The way you did it seems to me the correct way to do it, if you're going to do it at all. 你做的方式在我看来是正确的方法,如果你要去做的话。 Because the expansion of the macro is so similar to what you'd expect[1], I think it's valid to make the macro look like syntax (), rather than the usually recommended SCARY_UPPERCASE_MACROS() which are used to show that this code doesn't follow usual syntax and you should only use it carefully. 因为宏的扩展与你期望的那样相似[1],我认为使宏看起来像syntax()是有效的,而不是通常推荐的SCARY_UPPERCASE_MACROS()用来表明这段代码没有不遵循通常的语法,你应该只仔细使用它。

[1] The only flaw being the inability to declare variables, which is unlikely anyway, and likely to produce an error in the right place when used incorrectly, rather than doing something weird. [1]唯一的缺陷是无法声明变量,无论如何都不太可能,并且如果使用不当可能会在正确的位置产生错误,而不是做一些奇怪的事情。

Furthermore, even small increases in readability are important, so being able to say until ( instead of while (! really does make it easier to read many loops. If the ending condition is more easily thought of as an exceptional condition (regardless of whether it is or not) writing the loop that way round makes it easier to read. So even though it is only syntactic sugar, I think there's reason to consider it. 此外,即使是可读性的小幅增加也很重要,因此可以说until (而不是while (!确实让它更容易阅读许多循环。如果结束条件更容易被认为是一种例外情况(无论是否是或不)以循环方式编写循环使其更容易阅读。所以即使它只是语法糖,我认为有理由考虑它。

However I don't think it's worth it. 但是我不认为这是值得的。 The benefit is small, since most programmers are used to reading if (! and the cost is real: Anyone reading the code will have to check whether this a macro, or a custom compiler, and whether or no it does what they think. And it may misleadingly make you think you can do things like i=5 unless xxxx; . Such little improvements, if widespread, would fragment the language, so often it's best to do things the standard way, and adopt improvements slowly. 好处很小,因为大多数程序员习惯于阅读if (!并且成本是真实的:阅读代码的任何人都必须检查这是一个宏,还是自定义编译器,以及它是否符合他们的想法。它可能误导你会认为你可以做像i=5 unless xxxx;这样的事情, i=5 unless xxxx;这些小改进,如果普遍存在,会破坏语言,所以通常最好以标准方式做事,并慢慢采取改进措施。

However, it can be done well: the entirety of boost and tr1, especially the stuff done with templates to look like extensions to the library, involves extending C++ in various ways, many of which aren't adopted as they didn't seem worth it, but many of which have small or very widespread take-up because they made real improvements. 但是,它可以做得很好:整个boost和tr1,特别是使用模板看起来像库的扩展的东西,涉及以各种方式扩展C ++,其中许多不被采用,因为它们似乎不值得它,但其中很多都有小的或非常广泛的吸收,因为他们做了真正的改进。

This reminded me of something I have seen in someone's code: 这让我想起了我在某人的代码中看到过的东西:

#define R return;

Besides, making the code hard to comprehend, you increase maintenance costs. 此外,使代码难以理解,会增加维护成本。

I suggest it would be better not use them. 我建议最好不要使用它们。

You cannot use them in Ruby style as 你不能在Ruby风格中使用它们

`printf("hello,world") unless(a>0);`

is illegal. 是非法的。

And it would be more difficult for C programmers to understand the code. 而且C程序员更难理解代码。 Meanwhile the extra macro could be a problem. 同时额外的宏可能是一个问题。

If you're going to define macros, it's good practise to make them look really ugly. 如果您要定义宏,那么让它们看起来非常丑陋是一种很好的做法。 In particular, they should be all-capitals, and have some kind of prefix. 特别是,它们应该是全部大写,并且具有某种前缀。 This is because there is no namespacing and no coordination with the type system or overload resolution of C++. 这是因为没有命名空间,也没有与类型系统或C ++重载解析的协调。

So if your macro was called BIGYAN_UNNECESSARY_MACRO_UNTIL then it would be not quite "beyond the pale". 因此,如果您的宏被称为BIGYAN_UNNECESSARY_MACRO_UNTIL那么它将不会“超越苍白”。

If you want to extend C++ with new looping constructs, consider investigating lambdas in C++0x, where you could allow: 如果你想用新的循环结构扩展C ++,可以考虑在C ++ 0x中调查lambdas,你可以允许:

until([&] { return finished; }, [&] 
{
    // do stuff
});

It's not perfect, but it's better than macros. 它并不完美,但它比宏更好。

I don't think your macros are bad in particular if they are used only in your own code base. 如果它们仅在您自己的代码库中使用,我认为您的宏不是特别糟糕。 This article might be interesting for you. 这篇文章可能对你有意思。 That being said, I see some downsides in your macros when we use them in C++. 话虽这么说,当我们在C ++中使用它们时,我会看到你的宏的一些缺点。
For example, we cannot write as: 例如,我们不能写为:

until (T* p = f(x)) ...
unless (T* p = f(x)) ...

on the other hand, we can write as: 另一方面,我们可以写为:

while (T* p = f(x)) ...
if (T* p = f(x)) ...

As for unless , if we define it as: 至于unless ,如果我们将其定义为:

#define unless(x) if (x) {} else

then we can write unless (T* p = f(x)) ... . 然后我们可以写unless (T* p = f(x)) ... However, in this case we cannot add else clause after it. 但是,在这种情况下,我们不能在它之后添加else子句。

Good syntax sugar example (IMHO): 好的语法糖例子(恕我直言):

struct Foo {
    void bar() {}
};
typedef std::vector<Foo*> FooVector;
typedef boost::ptr_vector<Foo> FooPtrVector;

FooVector v1;
for (FooVector::iterator it = v1.begin(); it != v1.end(); ++it)
    (*it)->bar(); // ugly

FooPtrVector v2;
for (FooPtrVector::iterator it = v2.begin(); it != v2.end(); ++it)
    it->bar(); // nice

Look at how boost foreach is done. 看看如何提升foreach。

The header defines BOOST_FOREACH (the ugly, prefixed macro). 标头定义了BOOST_FOREACH(丑陋的前缀宏)。 You can 您可以

#define foreach BOOST_FOREACH

in you .cpp files in order to have cleaner code. 在.cpp文件中,以便有更清晰的代码。 You should not do it in your .h files however and use the ugly BOOST_FOREACH instead. 你不应该在你的.h文件中这样做,而是使用丑陋的BOOST_FOREACH。

Now, here is a set of “functional-programming-ish” macros for “convenient” IF THEN ELSE expressions (because ?: is ugly): 现在,这里有一组用于“方便”IF THEN ELSE表达式的“functional-programming-ish”宏(因为?:很难看):

#define IF(x) (x) ?
#define ELSE :

now 现在

int x = IF(y==0) 1
        ELSE IF(y<0) 2*y
        ELSE 3*y;

desugarises into: desugarises into:

int x = (y==0) ? 1 : (y<0) ? 2*y : 3*y;

As peoples said, adding those word do not really offer a useful syntactic sugar, because the cost to read a while ( or a if (! is small, all C developers are used to, and using such macro you'll scary most of the C developers. Also, making a language look like an other isn't a good idea. 正如人们所说的那样,添加这些词实际上并没有提供有用的语法糖,因为读取一段时间的成本(或者if(!很小,所有C开发人员都习惯了,并且使用这样的宏,你会吓到大部分的C开发人员。另外,使语言看起来像另一个并不是一个好主意。

BUT, syntactic sugar matters. 但是,语法糖很重要。 As already stated, in C++, boost add lot's of syntactic sugar through templates, and the stl also provide Somme sugar (for example, std::make_pair(a, b) is a syntactic sugar for std::pair<decltype(a), decltype(b)>(a, b) . 如前所述,在C ++中,boost通过模板添加很多语法糖,而stl也提供Somme糖(例如, std::make_pair(a, b)std::pair<decltype(a), decltype(b)>(a, b)的语法糖std::pair<decltype(a), decltype(b)>(a, b)

As a language improve, both functionalities and syntactic sugar are added to improve readability, writability, and efficiency of developers. 随着语言的改进,添加了功能和语法糖,以提高开发人员的可读性,可写性和效率。 For example, with the C++11 spec, the "for (elements in datastructure)" was added (see below), and also the "auto" keyword which allow a week inference of types (I say weak because you need to type a lot's of types at a lots of places where the type is actually 'obvious' and redundant). 例如,使用C ++ 11规范,添加了“for(数据结构中的元素)”(见下文),以及允许对类型进行一周推断的“auto”关键字(我说弱,因为你需要输入许多类型的类型实际上是“明显的”和冗余的很多类型。

Also, in haskell, using monads without the do notation (syntactic sugar) would be a real pain, and no-one would be using them 1 . 另外,在haskell中,使用不带符号的单子(语法糖)将是一个真正的痛苦,没有人会使用它们1


An example without syntactic sugar: 没有句法糖的例子:

//C++ < 11
std::vector<int> v;
v.push_back(3);
v.push_back(7);
v.push_back(9);
v.push_back(12);
for (std::vector<int>::iterator it = v.begin();
     it != v.end();
     it++)
{
    std::cout << *it << std::endl;
}

And with syntactic sugar: 用语法糖:

//C++ >= 11
std::vector<int> v {3, 7, 9, 12};

for (auto elm : v)
{
    std::cout << elm << std::endl;
}

A bit more readable, no? 有点可读,不是吗?


An haskell example for the IO monad (from HaskellWiki ) : IO monad的一个haskell示例(来自HaskellWiki ):

f :: IO String
f =
  ask "What's your name ?" >>= \name ->
  putStrLn "Write something." >>= \_ ->
  getLine >>= \string ->
  putStrLn ("Hello " ++ name ++ " you wrote " ++ string) >>= \_ ->
  return name

g :: IO String    
g = do
  name <- ask "What's your name ?"
  putStrLn "Write something."
  string <- getLine
  putStrLn ("Hello " ++ name ++ " you wrote " ++ string)
  return name

Here is a link to ideone : http://ideone.com/v9BqiZ 以下是ideone的链接: http ://ideone.com/v9BqiZ


1 : Actually, the language is more flexible than C++ and allow creating operators (for example &^, +., :+:, ...), so we could imagine that someone would quickly introduce syntactic sugar again :). 1 :实际上,语言比C ++更灵活,允许创建运算符(例如&^,+。,:+:,...),所以我们可以想象有人会很快再次引入语法糖:)。

Well you could do it, but make sure that it's not in the source file. 那么你可以做到,但要确保它不在源文件中。 I reccomend taking the CoffeeScript approach to JavaScript without the optimization generation. 我建议在没有优化生成的情况下将CoffeeScript方法应用于JavaScript。

Just in general you should write your language but export, give, and have the transpiled code as if you would have written it in C with extreme compatability. 一般来说,你应该编写你的语言,但是导出,给出和编译转换后的代码就好像你用C语言编写它具有极高的兼容性。

Try looking into awk and make it transpile all files with ending .cugar on save or something similar. 尝试查看awk并使其转换所有文件,结尾.cugar保存或类似的东西。 :) :)

Good luck. 祝好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM