【问题标题】:Syntactic sugar in C/C++C/C++ 中的语法糖
【发布时间】:2011-08-02 05:03:18
【问题描述】:

我一直在研究 Ruby,发现它的关键字“直到”和“除非”非常有趣。所以我想什么是在 C/C++ 中添加类似关键字的好方法。这是我想出的:

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

我正在寻找一些关于此的建议。谁能推荐一个更好的选择?

这是我编写的一个程序示例,用于说明我打算做什么:

#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;
}

如果您能指出一些可用于 C 或 C++ 的类似技巧的参考资料,那就太好了。

【问题讨论】:

  • 看来你已经做到了。
  • 虽然这些一开始看起来很花哨,但即使你自己编程也更适合使用标准 C++。在团队合作中,这是不行的。您的代码看起来不熟悉。我记得某个 sugar.h 包含类似 Pascal 的定义...
  • 我认为我不喜欢带有else 子句的unless(但是我喜欢当/除非在lisp 中)。 until 循环并没有给我留下深刻的印象,但可能是因为我以前从未使用过它。
  • 您可能对这个项目感兴趣:lolcode.com 它还添加了一些语法糖... :D
  • 如果你必须这样做,我唯一的建议是正确拼写until

标签: c++ c syntax metaprogramming syntactic-sugar


【解决方案1】:

你可以这样做,但要确保它不在源文件中。我建议在没有优化生成的情况下采用 CoffeeScript 方法来处理 JavaScript。

一般来说,您应该编写自己的语言,但导出、提供和拥有已转译的代码,就好像您用 C 编写的那样具有极高的兼容性。

尝试查看awk 并使其在保存或类似内容时转译所有以.cugar 结尾的文件。 :)

祝你好运。

【讨论】:

    【解决方案2】:

    看看 boost foreach 是怎么做的。

    标头定义了 BOOST_FOREACH(丑陋的前缀宏)。 你可以

    #define foreach BOOST_FOREACH
    

    在您的 .cpp 文件中,以便获得更清晰的代码。 但是,您不应该在 .h 文件中执行此操作,而是使用丑陋的 BOOST_FOREACH。

    现在,这里有一组“函数式编程”宏,用于“方便”的 IF THEN ELSE 表达式(因为 ?: 很难看):

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

    现在

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

    脱糖成:

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

    【讨论】:

      【解决方案3】:

      正如人们所说,添加这些词并不能真正提供有用的语法糖,因为读取一段时间 ( 或 if (!) 的成本很小,所有 C 开发人员都习惯了,使用这样的宏你会害怕大多数 C 开发人员。此外,让一种语言看起来像另一种语言并不是一个好主意。

      但是,语法糖很重要。如前所述,在 C++ 中,boost 通过模板添加大量语法糖,而 stl 还提供了 Somme 糖(例如,std::make_pair(a, b)std::pair&lt;decltype(a), decltype(b)&gt;(a, b) 的语法糖。

      随着语言的改进,功能和语法糖都被添加以提高开发人员的可读性、可写性和效率。例如,在 C++11 规范中,添加了“for(数据结构中的元素)”(见下文),以及允许一周推断类型的“auto”关键字(我说弱,因为您需要键入很多地方的很多类型实际上是“明显的”和冗余的)。

      另外,在 haskell 中,使用不带 do 表示法(语法糖)的 monad 会很痛苦,而且没有人会使用它们1


      一个没有语法糖的例子:

      //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;
      }
      

      还有语法糖:

      //C++ >= 11
      std::vector<int> v {3, 7, 9, 12};
      
      for (auto elm : v)
      {
          std::cout << elm << std::endl;
      }
      

      更具可读性,不是吗?


      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
      

      这里是 ideone 的链接:http://ideone.com/v9BqiZ


      1:实际上,该语言比 C++ 更灵活,并且允许创建运算符(例如 &^、+.、:+:、...),所以我们可以想象有人会很快再次引入语法糖:)。

      【讨论】:

        【解决方案4】:

        良好的语法糖示例(恕我直言):

        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
        

        【讨论】:

          【解决方案5】:

          在我看来,你的做法是正确的,如果你打算这样做的话。因为宏的扩展是所以类似于你所期望的[1],我认为使宏看起来像语法 () 是有效的,而不是通常推荐的 SCARY_UPPERCASE_MACROS()用于表明这段代码不遵循通常的语法,你应该小心使用它。

          [1] 唯一的缺陷是无法声明变量,这无论如何都不太可能,并且在使用不当时可能会在正确的位置产生错误,而不是做一些奇怪的事情。

          此外,即使是可读性的小幅提高也很重要,因此能够使用 until ( 而不是 while (! 确实可以更轻松地阅读许多循环。如果结束条件更容易被认为是一个异常条件(不管它是否是),那么以这种方式编写循环会更容易阅读。所以即使它只是语法糖,我认为有理由考虑它。

          然而我认为这不值得。好处是很小的,因为大多数程序员习惯于阅读if (! 并且成本是真实的:任何阅读代码的人都必须检查这是宏还是自定义编译器,以及它是否符合他们的想法。它可能会误导你认为你可以做i=5 unless xxxx; 之类的事情。如此小的改进,如果广泛传播,就会使语言变得支离破碎,所以通常最好以标准方式做事,然后慢慢采用改进。

          但是,它可以做得很好:整个 boost 和 tr1,尤其是用模板完成的东西,看起来像是库的扩展,涉及以各种方式扩展 C++,其中许多没有被采用,因为它们没有被采用。似乎不值得,但其中许多的使用量很小或非常广泛,因为它们做出了真正的改进。

          【讨论】:

          • 这个答案的第一段是有问题的,至少可以这么说!宏没有命名空间;它们完全是全球性的。因此,给它们起一个可能在其他地方使用的短名称是可怕的。如果您定义一个具有短名称(例如 until)的宏,然后包含声明成员函数 until 的第 3 方标头,则该标头将被破坏。它会尝试声明void until(int a);,但会说void while(!(int a)); 祝您好运破译该错误消息!因此(不出所料)Boost 在其所有宏技巧中都使用带有前缀的可怕大写名称。
          【解决方案6】:

          谁能推荐一个更好的选择?

          是的。根本不要这样做。直接使用whileif 语句即可。

          当您使用 C 或 C++ 编程时,请使用 C 或 C++ 编程。虽然 untilunless 在某些语言中可能会经常使用且惯用语,但它们不是 C 或 C++。

          【讨论】:

          • 对此+1。在 Ruby 中执行此操作可能是惯用的,但您不是在使用 Ruby 编程吗?不要试图强迫一种语言表现得像另一种语言,那样你只会写出糟糕的代码。
          • 我知道在这样的大环境中编码只会招来麻烦。但我的问题是这样做的最佳方法是什么?而不是关于是否这样做。
          • @BiGYaN:最好的办法是完全不这样做。
          • 这太有用了。
          【解决方案7】:

          我不认为你的宏不好,特别是如果它们只用于 你自己的代码库。 This article 对你来说可能很有趣。 话虽如此,当我们在 C++ 中使用宏时,我发现它们存在一些缺点。
          例如,我们不能写成:

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

          另一方面,我们可以写成:

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

          至于unless,如果我们定义为:

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

          那么我们可以写unless (T* p = f(x)) ...。但是,在这种情况下,我们不能 在其后添加else 子句。

          【讨论】:

          • 我只是一名 C 编码员,这引起了我的注意。您不能在 C 中执行此操作(问题标记为 C 和 C++)。我简直不敢相信你可以用 C++ 做到这一点……你为什么还要这样做?
          • @JamesMorris:是的,只有当我们在 C++ 中使用宏时,缺点才会出现。谢谢你指出。我编辑了答案。
          【解决方案8】:

          这让我想起了我在某人的代码中看到的一些东西:

          #define R return;
          

          此外,使代码难以理解,会增加维护成本。

          【讨论】:

            【解决方案9】:

            如果您要定义宏,最好让它们看起来非常难看。特别是,它们应该是全大写的,并且有某种前缀。这是因为没有命名空间,也没有与 C++ 的类型系统或重载决议协调。

            因此,如果您的宏被称为 BIGYAN_UNNECESSARY_MACRO_UNTIL,那么它就不会完全“超出常规”。

            如果您想使用新的循环结构扩展 C++,请考虑研究 C++0x 中的 lambda,您可以在其中允许:

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

            它并不完美,但比宏好。

            【讨论】:

            • 你提议使用 lambdas 会让我今晚做噩梦
            • 我很矛盾:lambda 的使用既可怕又令人敬畏。不过我喜欢。
            • 对于像重新实现 while(!b) 这样简单的事情,这显然是矫枉过正。
            • 这确实是可怕和可怕的。然而,这是 FP 语言中非常正常的代码(而且非常干净)。定义自己的控制结构的能力非常有用。
            【解决方案10】:

            我建议最好不要使用它们。

            你不能在 Ruby 风格中使用它们

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

            是非法的。

            C 程序员更难理解代码。同时,额外的宏可能是个问题。

            【讨论】:

            • a > 0 && printf("Hello, world!") ; ?
            猜你喜欢
            • 2017-12-21
            • 2011-09-01
            • 1970-01-01
            • 2010-10-19
            • 1970-01-01
            • 2017-07-29
            • 2010-10-31
            • 1970-01-01
            • 2023-03-15
            相关资源
            最近更新 更多